@@ -17,30 +17,46 @@ limitations under the License.
1717package sequel
1818
1919import (
20+ "encoding/json"
2021 "testing"
22+ "time"
2123
2224 "github.com/microbus-io/sequel/testdata"
2325 "github.com/microbus-io/testarossa"
2426)
2527
2628func TestDB_AutoCreate (t * testing.T ) {
27- for _ , driver := range []string {"mysql" , "pgx" /* , "mssql" */ } {
28- t .Run (driver , func (t * testing.T ) {
29+ t .Parallel ()
30+ dsns := map [string ]string {
31+ "mysql" : "root:root@tcp(127.0.0.1:3306)/" ,
32+ "pgx" : "postgres://postgres:postgres@127.0.0.1:5432/" ,
33+ // "mssql": "sqlserver://sa:Password123@127.0.0.1:1433",
34+ }
35+ for drv , dsn := range dsns {
36+ t .Run (drv , func (t * testing.T ) {
2937 assert := testarossa .For (t )
3038
31- db , err := OpenTesting (driver , "" , "AutoCreate" )
39+ db , err := OpenTesting (drv , dsn , t . Name () )
3240 assert .NoError (err )
33- assert .NotNil (db )
41+ if ! assert .NotNil (db ) {
42+ return
43+ }
3444 defer db .Close ()
3545
3646 err = db .Migrate ("auto_create" , testdata .FS )
3747 assert .NoError (err )
3848
39- row := db .QueryRow ("SELECT COUNT(id) FROM foo" )
4049 var count int
41- err = row .Scan (& count )
50+ stmt := "SELECT COUNT(id) FROM foo"
51+ err = db .QueryRow (stmt ).Scan (& count )
4252 assert .NoError (err )
4353 assert .Equal (3 , count )
54+
55+ var id int
56+ stmt = db .ConformArgPlaceholders ("SELECT id FROM foo WHERE id=?" )
57+ err = db .QueryRow (stmt , 1 ).Scan (& id )
58+ assert .NoError (err )
59+ assert .Equal (1 , id )
4460 })
4561 }
4662}
@@ -86,4 +102,281 @@ func TestDB_DatabaseNameFromDataSourceName(t *testing.T) {
86102 assert .Expect (name , "my_database" , err , nil )
87103 name , err = databaseNameFromDataSourceName ("mssql" , "sqlserver://user:pw@127.0.0.1:1433" )
88104 assert .Expect (name , "" , err , nil )
105+
106+ // empty dsn
107+ _ , err = databaseNameFromDataSourceName ("mysql" , "" )
108+ assert .Error (err )
109+
110+ // unsupported driver
111+ _ , err = databaseNameFromDataSourceName ("sqlite" , "file.db" )
112+ assert .Error (err )
113+ }
114+
115+ func TestDB_InferDriverName (t * testing.T ) {
116+ t .Parallel ()
117+ assert := testarossa .For (t )
118+
119+ // Postgres prefix
120+ assert .Equal ("pgx" , inferDriverName ("postgres://user:pw@127.0.0.1:5432/mydb" ))
121+
122+ // SQL Server prefix
123+ assert .Equal ("mssql" , inferDriverName ("sqlserver://user:pw@127.0.0.1:1433" ))
124+
125+ // MySQL tcp() style
126+ assert .Equal ("mysql" , inferDriverName ("root:root@tcp(127.0.0.1:3306)/" ))
127+
128+ // Port-based inference
129+ assert .Equal ("mysql" , inferDriverName ("root:root@127.0.0.1:3306/" ))
130+ assert .Equal ("pgx" , inferDriverName ("user:pw@127.0.0.1:5432/" ))
131+ assert .Equal ("mssql" , inferDriverName ("user:pw@127.0.0.1:1433" ))
132+
133+ // Empty string
134+ assert .Equal ("" , inferDriverName ("" ))
135+
136+ // Unrecognizable DSN
137+ assert .Equal ("" , inferDriverName ("some-unknown-dsn" ))
138+ }
139+
140+ func TestDB_SetDatabaseInDataSourceName (t * testing.T ) {
141+ t .Parallel ()
142+ assert := testarossa .For (t )
143+
144+ // mysql - set database
145+ dsn , err := setDatabaseInDataSourceName ("mysql" , "root:root@tcp(127.0.0.1:3306)/" , "mydb" )
146+ assert .NoError (err )
147+ name , _ := databaseNameFromDataSourceName ("mysql" , dsn )
148+ assert .Equal ("mydb" , name )
149+
150+ // mysql - clear database
151+ dsn , err = setDatabaseInDataSourceName ("mysql" , "root:root@tcp(127.0.0.1:3306)/mydb" , "" )
152+ assert .NoError (err )
153+ name , _ = databaseNameFromDataSourceName ("mysql" , dsn )
154+ assert .Equal ("" , name )
155+
156+ // pgx - set database
157+ dsn , err = setDatabaseInDataSourceName ("pgx" , "postgres://user:pw@127.0.0.1:5432/" , "mydb" )
158+ assert .NoError (err )
159+ name , _ = databaseNameFromDataSourceName ("pgx" , dsn )
160+ assert .Equal ("mydb" , name )
161+
162+ // pgx - clear database
163+ dsn , err = setDatabaseInDataSourceName ("pgx" , "postgres://user:pw@127.0.0.1:5432/mydb" , "" )
164+ assert .NoError (err )
165+ name , _ = databaseNameFromDataSourceName ("pgx" , dsn )
166+ assert .Equal ("" , name )
167+
168+ // mssql - set database
169+ dsn , err = setDatabaseInDataSourceName ("mssql" , "sqlserver://user:pw@127.0.0.1:1433" , "mydb" )
170+ assert .NoError (err )
171+ name , _ = databaseNameFromDataSourceName ("mssql" , dsn )
172+ assert .Equal ("mydb" , name )
173+
174+ // mssql - clear database
175+ dsn , err = setDatabaseInDataSourceName ("mssql" , "sqlserver://user:pw@127.0.0.1:1433?database=mydb" , "" )
176+ assert .NoError (err )
177+ name , _ = databaseNameFromDataSourceName ("mssql" , dsn )
178+ assert .Equal ("" , name )
179+
180+ // empty dsn
181+ _ , err = setDatabaseInDataSourceName ("mysql" , "" , "mydb" )
182+ assert .Error (err )
183+
184+ // unsupported driver
185+ _ , err = setDatabaseInDataSourceName ("sqlite" , "file.db" , "mydb" )
186+ assert .Error (err )
187+ }
188+
189+ func TestDB_ConformArgPlaceholders_NoArgs (t * testing.T ) {
190+ t .Parallel ()
191+ assert := testarossa .For (t )
192+
193+ db := & DB {driverName : "pgx" }
194+ stmt := `SELECT * FROM foo WHERE id=1`
195+ assert .Equal (stmt , db .ConformArgPlaceholders (stmt ))
196+ }
197+
198+ func TestDB_ConformArgPlaceholders_NonPgx (t * testing.T ) {
199+ t .Parallel ()
200+ assert := testarossa .For (t )
201+
202+ // MySQL driver should return the statement unchanged
203+ db := & DB {driverName : "mysql" }
204+ stmt := `SELECT * FROM foo WHERE id=? AND name=?`
205+ assert .Equal (stmt , db .ConformArgPlaceholders (stmt ))
206+
207+ // MSSQL driver should also return unchanged
208+ db = & DB {driverName : "mssql" }
209+ assert .Equal (stmt , db .ConformArgPlaceholders (stmt ))
210+ }
211+
212+ func TestDB_NowUTC (t * testing.T ) {
213+ t .Parallel ()
214+ assert := testarossa .For (t )
215+
216+ db := & DB {driverName : "mysql" }
217+ assert .Equal ("UTC_TIMESTAMP(3)" , db .NowUTC ())
218+
219+ db = & DB {driverName : "pgx" }
220+ assert .Equal ("(NOW() AT TIME ZONE 'UTC')" , db .NowUTC ())
221+
222+ db = & DB {driverName : "mssql" }
223+ assert .Equal ("SYSUTCDATETIME()" , db .NowUTC ())
224+
225+ db = & DB {driverName : "unknown" }
226+ assert .Equal ("" , db .NowUTC ())
227+ }
228+
229+ func TestDB_RegexpTextSearch (t * testing.T ) {
230+ t .Parallel ()
231+ assert := testarossa .For (t )
232+
233+ // MySQL
234+ db := & DB {driverName : "mysql" }
235+ assert .Equal ("''" + ` REGEXP ?` , db .RegexpTextSearch ())
236+ assert .Equal ("name REGEXP ?" , db .RegexpTextSearch ("name" ))
237+ assert .Equal ("CONCAT_WS(' ',name,email) REGEXP ?" , db .RegexpTextSearch ("name" , "email" ))
238+
239+ // Postgres
240+ db = & DB {driverName : "pgx" }
241+ assert .Equal ("REGEXP_LIKE('', ?, 'i')" , db .RegexpTextSearch ())
242+ assert .Equal ("REGEXP_LIKE(name, ?, 'i')" , db .RegexpTextSearch ("name" ))
243+ assert .Equal ("REGEXP_LIKE(CONCAT_WS(' ',name,email), ?, 'i')" , db .RegexpTextSearch ("name" , "email" ))
244+
245+ // MSSQL
246+ db = & DB {driverName : "mssql" }
247+ assert .Equal ("REGEXP_LIKE('', ?, 'i')" , db .RegexpTextSearch ())
248+ assert .Equal ("REGEXP_LIKE(name, ?, 'i')" , db .RegexpTextSearch ("name" ))
249+
250+ // Unknown driver
251+ db = & DB {driverName : "unknown" }
252+ assert .Equal ("" , db .RegexpTextSearch ("name" ))
253+ }
254+
255+ func TestDB_Nullify (t * testing.T ) {
256+ t .Parallel ()
257+ assert := testarossa .For (t )
258+
259+ // Zero values should return nil
260+ assert .Nil (Nullify ("" ))
261+ assert .Nil (Nullify (0 ))
262+ assert .Nil (Nullify (false ))
263+ assert .Nil (Nullify (time.Time {}))
264+
265+ // Non-zero values should return the value itself
266+ assert .Equal ("hello" , Nullify ("hello" ))
267+ assert .Equal (42 , Nullify (42 ))
268+ assert .Equal (true , Nullify (true ))
269+ now := time .Now ()
270+ assert .Equal (now , Nullify (now ))
271+ }
272+
273+ func TestDB_Nullable (t * testing.T ) {
274+ t .Parallel ()
275+ assert := testarossa .For (t )
276+
277+ var s string
278+ n := Nullable (& s )
279+
280+ // Simulate scanning a value
281+ n .V = "hello"
282+ n .Valid = true
283+ err := ApplyBindings (n )
284+ assert .NoError (err )
285+ assert .Equal ("hello" , s )
286+
287+ // Simulate scanning a NULL (Valid=false, V is zero)
288+ s = "previous"
289+ n2 := Nullable (& s )
290+ n2 .Valid = false
291+ err = ApplyBindings (n2 )
292+ assert .NoError (err )
293+ assert .Equal ("" , s )
294+ }
295+
296+ func TestDB_Bind (t * testing.T ) {
297+ t .Parallel ()
298+ assert := testarossa .For (t )
299+
300+ var tags []string
301+ b := Bind (func (value string ) error {
302+ return json .Unmarshal ([]byte (value ), & tags )
303+ })
304+
305+ // Simulate scanning a JSON string
306+ b .V = `["a","b","c"]`
307+ b .Valid = true
308+ err := ApplyBindings (b )
309+ assert .NoError (err )
310+ assert .Len (tags , 3 )
311+ assert .Equal ("a" , tags [0 ])
312+ assert .Equal ("b" , tags [1 ])
313+ assert .Equal ("c" , tags [2 ])
314+ }
315+
316+ func TestDB_Bind_Error (t * testing.T ) {
317+ t .Parallel ()
318+ assert := testarossa .For (t )
319+
320+ b := Bind (func (value string ) error {
321+ return json .Unmarshal ([]byte (value ), & []int {})
322+ })
323+
324+ // Simulate scanning invalid JSON
325+ b .V = `not-json`
326+ b .Valid = true
327+ err := ApplyBindings (b )
328+ assert .Error (err )
329+ }
330+
331+ func TestDB_ApplyBindings_NoBindings (t * testing.T ) {
332+ t .Parallel ()
333+ assert := testarossa .For (t )
334+
335+ // ApplyBindings should be safe with non-binder args
336+ var x int
337+ var s string
338+ err := ApplyBindings (& x , & s )
339+ assert .NoError (err )
340+
341+ // Empty args
342+ err = ApplyBindings ()
343+ assert .NoError (err )
344+ }
345+
346+ func TestDB_DriverName (t * testing.T ) {
347+ t .Parallel ()
348+ assert := testarossa .For (t )
349+
350+ db := & DB {driverName : "mysql" }
351+ assert .Equal ("mysql" , db .DriverName ())
352+
353+ db = & DB {driverName : "pgx" }
354+ assert .Equal ("pgx" , db .DriverName ())
355+ }
356+
357+ func TestDB_CloseNil (t * testing.T ) {
358+ t .Parallel ()
359+ assert := testarossa .For (t )
360+
361+ // Close on nil DB should not panic
362+ var db * DB
363+ err := db .Close ()
364+ assert .NoError (err )
365+ }
366+
367+ func TestDB_OpenEmptyDSN (t * testing.T ) {
368+ t .Parallel ()
369+ assert := testarossa .For (t )
370+
371+ _ , err := Open ("mysql" , "" )
372+ assert .Error (err )
373+ }
374+
375+ func TestDB_OpenInferDriverFails (t * testing.T ) {
376+ t .Parallel ()
377+ assert := testarossa .For (t )
378+
379+ // Unrecognizable DSN without explicit driver
380+ _ , err := Open ("" , "some-unknown-connection-string" )
381+ assert .Error (err )
89382}
0 commit comments