77 "testing"
88
99 "github.com/stretchr/testify/assert"
10- "github.com/stretchr/testify/require"
11- _ "modernc.org/sqlite" // Import pure Go SQLite driver for testing
1210)
1311
1412// DatabaseExecutor matches the user's interface from the problem description
@@ -23,43 +21,69 @@ type DatabaseExecutor interface {
2321// Ensure that *sql.DB implements our interface
2422var _ DatabaseExecutor = (* sql .DB )(nil )
2523
24+ // mockDatabaseExecutor is a mock implementation for testing
25+ type mockDatabaseExecutor struct {}
26+
27+ func (m * mockDatabaseExecutor ) ExecContext (ctx context.Context , query string , args ... interface {}) (sql.Result , error ) {
28+ return & mockResult {}, nil
29+ }
30+
31+ func (m * mockDatabaseExecutor ) QueryContext (ctx context.Context , query string , args ... interface {}) (* sql.Rows , error ) {
32+ return nil , nil
33+ }
34+
35+ func (m * mockDatabaseExecutor ) QueryRowContext (ctx context.Context , query string , args ... interface {}) * sql.Row {
36+ return & sql.Row {}
37+ }
38+
39+ func (m * mockDatabaseExecutor ) PrepareContext (ctx context.Context , query string ) (* sql.Stmt , error ) {
40+ return nil , nil
41+ }
42+
43+ func (m * mockDatabaseExecutor ) BeginTx (ctx context.Context , opts * sql.TxOptions ) (* sql.Tx , error ) {
44+ return nil , nil
45+ }
46+
47+ // mockResult implements sql.Result for testing
48+ type mockResult struct {}
49+
50+ func (m * mockResult ) LastInsertId () (int64 , error ) { return 0 , nil }
51+ func (m * mockResult ) RowsAffected () (int64 , error ) { return 0 , nil }
52+
53+ // Ensure our mock implements the interface
54+ var _ DatabaseExecutor = (* mockDatabaseExecutor )(nil )
55+
2656// MockDatabaseService simulates what the database module provides
2757type MockDatabaseService interface {
28- DB () * sql. DB
58+ DB () DatabaseExecutor
2959 Close () error
3060}
3161
3262// mockDatabaseServiceImpl simulates the lazy wrapper that the database module uses
3363type mockDatabaseServiceImpl struct {
34- db * sql. DB
64+ executor DatabaseExecutor
3565}
3666
37- func (m * mockDatabaseServiceImpl ) DB () * sql. DB {
38- return m .db
67+ func (m * mockDatabaseServiceImpl ) DB () DatabaseExecutor {
68+ return m .executor
3969}
4070
4171func (m * mockDatabaseServiceImpl ) Close () error {
42- if m .db != nil {
43- return m .db .Close ()
44- }
4572 return nil
4673}
4774
4875// TestInterfaceMatchingCore demonstrates the core issue with interface matching
4976func TestInterfaceMatchingCore (t * testing.T ) {
5077 // Create a mock database service (simulating what the database module provides)
51- db , err := sql .Open ("sqlite" , ":memory:" )
52- require .NoError (t , err )
53- defer db .Close ()
54-
55- mockService := & mockDatabaseServiceImpl {db : db }
78+ mockExecutor := & mockDatabaseExecutor {}
79+ mockService := & mockDatabaseServiceImpl {executor : mockExecutor }
5680
57- // Test 1: Check if *sql.DB implements DatabaseExecutor (it should)
81+ // Test 1: Check if mockDatabaseExecutor implements DatabaseExecutor (it should)
5882 expectedType := reflect .TypeOf ((* DatabaseExecutor )(nil )).Elem ()
59- sqlDBType := reflect .TypeOf ((* sql . DB )(nil ))
83+ mockExecutorType := reflect .TypeOf ((* mockDatabaseExecutor )(nil ))
6084
61- assert .True (t , sqlDBType .Implements (expectedType ),
62- "*sql.DB should implement DatabaseExecutor interface" )
85+ assert .True (t , mockExecutorType .Implements (expectedType ),
86+ "mockDatabaseExecutor should implement DatabaseExecutor interface" )
6387
6488 // Test 2: Check if mockDatabaseServiceImpl implements DatabaseExecutor (it should NOT)
6589 mockServiceType := reflect .TypeOf ((* mockDatabaseServiceImpl )(nil ))
@@ -71,16 +95,16 @@ func TestInterfaceMatchingCore(t *testing.T) {
7195 assert .True (t , mockServiceType .Implements (mockDBServiceType ),
7296 "mockDatabaseServiceImpl should implement MockDatabaseService interface" )
7397
74- // Test 4: Demonstrate the workaround - extract the *sql.DB from the service
98+ // Test 4: Demonstrate the workaround - extract the DatabaseExecutor from the service
7599 actualDB := mockService .DB ()
76100 actualDBType := reflect .TypeOf (actualDB )
77101 assert .True (t , actualDBType .Implements (expectedType ),
78- "The *sql.DB extracted from the service should implement DatabaseExecutor" )
102+ "The DatabaseExecutor returned by the service should implement DatabaseExecutor" )
79103
80104 t .Log ("✅ Core issue demonstrated:" )
81- t .Log (" - *sql.DB implements DatabaseExecutor ✓" )
105+ t .Log (" - mockDatabaseExecutor implements DatabaseExecutor ✓" )
82106 t .Log (" - Database service wrapper does NOT implement DatabaseExecutor ✗" )
83- t .Log (" - But wrapper.DB() returns *sql.DB which does implement DatabaseExecutor ✓" )
107+ t .Log (" - But wrapper.DB() returns DatabaseExecutor which does implement DatabaseExecutor ✓" )
84108}
85109
86110// TestServiceDependencyMatching simulates the service dependency resolution
@@ -134,15 +158,15 @@ func (m *MockDatabaseModule) Dependencies() []string {
134158}
135159
136160func (m * MockDatabaseModule ) ProvidesServices () []ServiceProvider {
137- // Create a dummy *sql.DB for testing
138- db , _ := sql . Open ( "sqlite" , ":memory:" )
139- wrapper := & mockDatabaseServiceImpl {db : db }
161+ // Create a dummy mock executor for testing
162+ mockExecutor := & mockDatabaseExecutor {}
163+ wrapper := & mockDatabaseServiceImpl {executor : mockExecutor }
140164
141165 return []ServiceProvider {
142166 {
143167 Name : "database.service" ,
144168 Description : "Database service wrapper" ,
145- Instance : wrapper , // Provide wrapper, not *sql.DB directly
169+ Instance : wrapper , // Provide wrapper, not direct executor
146170 },
147171 }
148172}
@@ -154,7 +178,6 @@ func (m *MockDatabaseModule) RequiresServices() []ServiceDependency {
154178// FailingTestModule tries to use MatchByInterface=true (will fail)
155179type FailingTestModule struct {
156180 name string
157- db DatabaseExecutor
158181}
159182
160183func (m * FailingTestModule ) Name () string {
0 commit comments