@@ -40,7 +40,10 @@ package tests
4040
4141import (
4242 "context"
43+ "fmt"
44+ "sync"
4345 "testing"
46+ "time"
4447
4548 . "github.com/oracle-samples/gorm-oracle/tests/utils"
4649
@@ -103,11 +106,11 @@ func TestScopes(t *testing.T) {
103106 t .Errorf ("Should found two users's name in 1, 2, but got %v" , result .RowsAffected )
104107 }
105108
106- var maxId int64
109+ var maxID int64
107110 userTable := func (db * gorm.DB ) * gorm.DB {
108111 return db .WithContext (context .Background ()).Table ("users" )
109112 }
110- if err := DB .Scopes (userTable ).Select ("max(\" id\" )" ).Scan (& maxId ).Error ; err != nil {
113+ if err := DB .Scopes (userTable ).Select ("max(\" id\" )" ).Scan (& maxID ).Error ; err != nil {
111114 t .Errorf ("select max(id)" )
112115 }
113116}
@@ -166,3 +169,374 @@ func TestComplexScopes(t *testing.T) {
166169 })
167170 }
168171}
172+
173+ func TestEmptyAndNilScopes (t * testing.T ) {
174+ setupScopeTestData (t )
175+
176+ // Test with no scopes
177+ var users []User
178+ err := DB .Scopes ().Find (& users ).Error
179+ if err != nil {
180+ t .Errorf ("Empty scopes should work, got error: %v" , err )
181+ }
182+
183+ // Test with empty slice of scopes
184+ emptyScopes := []func (* gorm.DB ) * gorm.DB {}
185+ err = DB .Scopes (emptyScopes ... ).Find (& users ).Error
186+ if err != nil {
187+ t .Errorf ("Empty scope slice should work, got error: %v" , err )
188+ }
189+
190+ // Test behavior when we have mixed nil and valid scopes
191+ defer func () {
192+ if r := recover (); r != nil {
193+ t .Logf ("Mixed nil scopes caused panic (expected): %v" , r )
194+ }
195+ }()
196+ }
197+
198+ func TestScopesWithDatabaseErrors (t * testing.T ) {
199+ setupScopeTestData (t )
200+
201+ // Scope that generates invalid SQL
202+ invalidSQLScope := func (db * gorm.DB ) * gorm.DB {
203+ return db .Where ("invalid_column_name_12345 = ?" , "test" )
204+ }
205+
206+ var users []User
207+ err := DB .Scopes (invalidSQLScope ).Find (& users ).Error
208+ if err == nil {
209+ t .Error ("Expected error for invalid SQL in scope, got nil" )
210+ }
211+
212+ // Verify database still works after scope error
213+ err = DB .Find (& users ).Error
214+ if err != nil {
215+ t .Errorf ("Database should still work after scope error, got: %v" , err )
216+ }
217+ }
218+
219+ func TestConflictingScopes (t * testing.T ) {
220+ setupScopeTestData (t )
221+
222+ // Scopes with contradictory conditions
223+ alwaysTrue := func (db * gorm.DB ) * gorm.DB {
224+ return db .Where ("1 = 1" )
225+ }
226+ alwaysFalse := func (db * gorm.DB ) * gorm.DB {
227+ return db .Where ("1 = 0" )
228+ }
229+
230+ var users []User
231+ err := DB .Scopes (alwaysTrue , alwaysFalse ).Find (& users ).Error
232+ if err != nil {
233+ t .Errorf ("Conflicting scopes should not cause error, got: %v" , err )
234+ }
235+ if len (users ) != 0 {
236+ t .Errorf ("Conflicting scopes should return no results, got %d users" , len (users ))
237+ }
238+
239+ // Test conflicting WHERE conditions on same column
240+ nameScope1 := func (db * gorm.DB ) * gorm.DB {
241+ return db .Where ("\" name\" = ?" , "ScopeUser1" )
242+ }
243+ nameScope2 := func (db * gorm.DB ) * gorm.DB {
244+ return db .Where ("\" name\" = ?" , "ScopeUser2" )
245+ }
246+
247+ err = DB .Scopes (nameScope1 , nameScope2 ).Find (& users ).Error
248+ if err != nil {
249+ t .Errorf ("Conflicting name scopes should not cause error, got: %v" , err )
250+ }
251+ if len (users ) != 0 {
252+ t .Errorf ("Conflicting name scopes should return no results, got %d users" , len (users ))
253+ }
254+ }
255+
256+ func TestContextCancellationInScopes (t * testing.T ) {
257+ setupScopeTestData (t )
258+
259+ // Create a context that gets cancelled immediately
260+ ctx , cancel := context .WithTimeout (context .Background (), 1 * time .Millisecond )
261+ defer cancel ()
262+
263+ contextScope := func (db * gorm.DB ) * gorm.DB {
264+ return db .WithContext (ctx )
265+ }
266+
267+ var users []User
268+ err := DB .Scopes (contextScope ).Find (& users ).Error
269+ // Error is expected due to context cancellation
270+
271+ // Verify database still works after context cancellation
272+ err = DB .Find (& users ).Error
273+ if err != nil {
274+ t .Errorf ("Database should work after context cancellation in scope, got: %v" , err )
275+ }
276+ }
277+
278+ func TestConcurrentScopeUsage (t * testing.T ) {
279+ setupScopeTestData (t )
280+
281+ const numGoroutines = 10
282+ const operationsPerGoroutine = 5
283+
284+ var wg sync.WaitGroup
285+ errors := make (chan error , numGoroutines * operationsPerGoroutine )
286+
287+ for i := 0 ; i < numGoroutines ; i ++ {
288+ wg .Add (1 )
289+ go func (goroutineID int ) {
290+ defer wg .Done ()
291+
292+ for j := 0 ; j < operationsPerGoroutine ; j ++ {
293+ userScope := func (db * gorm.DB ) * gorm.DB {
294+ return db .Where ("\" name\" LIKE ?" , fmt .Sprintf ("ScopeUser%d" , (goroutineID % 3 )+ 1 ))
295+ }
296+
297+ var users []User
298+ err := DB .Scopes (userScope ).Find (& users ).Error
299+ if err != nil {
300+ errors <- fmt .Errorf ("goroutine %d operation %d failed: %v" , goroutineID , j , err )
301+ }
302+ }
303+ }(i )
304+ }
305+
306+ wg .Wait ()
307+ close (errors )
308+
309+ errorCount := 0
310+ for err := range errors {
311+ t .Errorf ("Concurrent scope error: %v" , err )
312+ errorCount ++
313+ }
314+
315+ if errorCount > 0 {
316+ t .Errorf ("Got %d errors in concurrent scope usage" , errorCount )
317+ }
318+ }
319+
320+ func TestScopesThatModifyUnexpectedQuery (t * testing.T ) {
321+ setupScopeTestData (t )
322+
323+ // Scope that changes the table
324+ tableChangingScope := func (db * gorm.DB ) * gorm.DB {
325+ return db .Table ("companies" )
326+ }
327+
328+ // Scope that changes the model
329+ modelChangingScope := func (db * gorm.DB ) * gorm.DB {
330+ return db .Model (& Company {})
331+ }
332+
333+ // Test table changing scope
334+ var users []User
335+ err := DB .Model (& User {}).Scopes (tableChangingScope ).Find (& users ).Error
336+
337+ // Test model changing scope
338+ err = DB .Scopes (modelChangingScope ).Find (& users ).Error
339+
340+ // Scope that adds unexpected clauses
341+ limitScope := func (db * gorm.DB ) * gorm.DB {
342+ return db .Limit (1 ).Offset (1 ).Order ("\" id\" DESC" )
343+ }
344+
345+ err = DB .Scopes (limitScope ).Find (& users ).Error
346+ if err != nil {
347+ t .Errorf ("Scope with limit/offset/order should work, got: %v" , err )
348+ }
349+ }
350+
351+ func TestLargeNumberOfScopes (t * testing.T ) {
352+ setupScopeTestData (t )
353+
354+ // Create a large number of scopes
355+ const numScopes = 100
356+ scopes := make ([]func (* gorm.DB ) * gorm.DB , numScopes )
357+
358+ for i := 0 ; i < numScopes ; i ++ {
359+ val := i
360+ scopes [i ] = func (db * gorm.DB ) * gorm.DB {
361+ return db .Where ("\" id\" > ?" , val * - 1 ) // Always true conditions
362+ }
363+ }
364+
365+ var users []User
366+ start := time .Now ()
367+ err := DB .Scopes (scopes ... ).Find (& users ).Error
368+ duration := time .Since (start )
369+
370+ if err != nil {
371+ t .Errorf ("Large number of scopes failed: %v" , err )
372+ }
373+
374+ t .Logf ("Processing %d scopes took %v" , numScopes , duration )
375+
376+ // Verify we still get results
377+ if len (users ) == 0 {
378+ t .Error ("Large number of scopes should still return results" )
379+ }
380+ }
381+
382+ func TestScopesWithTransactions (t * testing.T ) {
383+ setupScopeTestData (t )
384+
385+ // Test scopes within a transaction
386+ err := DB .Transaction (func (tx * gorm.DB ) error {
387+ transactionScope := func (db * gorm.DB ) * gorm.DB {
388+ return db .Where ("\" name\" = ?" , "ScopeUser1" )
389+ }
390+
391+ var users []User
392+ return tx .Scopes (transactionScope ).Find (& users ).Error
393+ })
394+
395+ if err != nil {
396+ t .Errorf ("Scopes within transaction should work, got: %v" , err )
397+ }
398+
399+ // Test scope that tries to start its own transaction (nested transaction scenario)
400+ nestedTxScope := func (db * gorm.DB ) * gorm.DB {
401+ return db .Begin ()
402+ }
403+
404+ err = DB .Transaction (func (tx * gorm.DB ) error {
405+ var users []User
406+ return tx .Scopes (nestedTxScope ).Find (& users ).Error
407+ })
408+ }
409+
410+ func TestScopesWithRawSQL (t * testing.T ) {
411+ setupScopeTestData (t )
412+
413+ // Scope that adds raw SQL conditions
414+ rawSQLScope := func (db * gorm.DB ) * gorm.DB {
415+ return db .Where ("LENGTH(\" name\" ) > ?" , 5 )
416+ }
417+
418+ var users []User
419+ err := DB .Scopes (rawSQLScope ).Find (& users ).Error
420+ if err != nil {
421+ t .Errorf ("Raw SQL scope should work, got: %v" , err )
422+ }
423+
424+ // Test proper parameterized queries (safe)
425+ safeParameterizedScope := func (db * gorm.DB ) * gorm.DB {
426+ return db .Where ("\" name\" = ? OR \" name\" LIKE ?" , "test" , "ScopeUser%" )
427+ }
428+
429+ err = DB .Scopes (safeParameterizedScope ).Find (& users ).Error
430+ if err != nil {
431+ t .Errorf ("Parameterized SQL in scope should be safe, got: %v" , err )
432+ }
433+
434+ // Test complex safe expressions
435+ complexSafeScope := func (db * gorm.DB ) * gorm.DB {
436+ return db .Where ("(\" name\" = ? OR \" age\" > ?) AND \" deleted_at\" IS NULL" , "ScopeUser1" , 10 )
437+ }
438+
439+ err = DB .Scopes (complexSafeScope ).Find (& users ).Error
440+ if err != nil {
441+ t .Errorf ("Complex parameterized SQL should work, got: %v" , err )
442+ }
443+
444+ // Test that we get expected results
445+ if len (users ) == 0 {
446+ t .Error ("Should have found some users with complex scope" )
447+ }
448+ }
449+
450+ func TestScopeErrorRecovery (t * testing.T ) {
451+ setupScopeTestData (t )
452+
453+ // First, cause an error with a bad scope
454+ badScope := func (db * gorm.DB ) * gorm.DB {
455+ return db .Where ("non_existent_column = ?" , "value" )
456+ }
457+
458+ var users []User
459+ err := DB .Scopes (badScope ).Find (& users ).Error
460+ if err == nil {
461+ t .Error ("Expected error from bad scope" )
462+ }
463+
464+ // Then verify normal operations still work
465+ goodScope := func (db * gorm.DB ) * gorm.DB {
466+ return db .Where ("\" name\" = ?" , "ScopeUser1" )
467+ }
468+
469+ err = DB .Scopes (goodScope ).Find (& users ).Error
470+ if err != nil {
471+ t .Errorf ("Good scope should work after bad scope error: %v" , err )
472+ }
473+
474+ if len (users ) != 1 {
475+ t .Errorf ("Expected 1 user, got %d" , len (users ))
476+ }
477+ }
478+
479+ func TestScopeChainModification (t * testing.T ) {
480+ // Test that scopes don't interfere with each other's chain modifications
481+ setupScopeTestData (t )
482+
483+ scope1Called := false
484+ scope2Called := false
485+
486+ scope1 := func (db * gorm.DB ) * gorm.DB {
487+ scope1Called = true
488+ return db .Where ("\" id\" > ?" , 0 )
489+ }
490+
491+ scope2 := func (db * gorm.DB ) * gorm.DB {
492+ scope2Called = true
493+ return db .Where ("\" name\" IS NOT NULL" )
494+ }
495+
496+ var users []User
497+ err := DB .Scopes (scope1 , scope2 ).Find (& users ).Error
498+ if err != nil {
499+ t .Errorf ("Scope chain should work, got: %v" , err )
500+ }
501+
502+ if ! scope1Called {
503+ t .Error ("Scope1 should have been called" )
504+ }
505+ if ! scope2Called {
506+ t .Error ("Scope2 should have been called" )
507+ }
508+ }
509+
510+ func TestScopesWithSubqueries (t * testing.T ) {
511+ setupScopeTestData (t )
512+
513+ // Scope that uses a subquery
514+ subqueryScope := func (db * gorm.DB ) * gorm.DB {
515+ subQuery := DB .Model (& User {}).Select ("\" name\" " ).Where ("\" id\" = 1" )
516+ return db .Where ("\" name\" IN (?)" , subQuery )
517+ }
518+
519+ var users []User
520+ err := DB .Scopes (subqueryScope ).Find (& users ).Error
521+ if err != nil {
522+ t .Errorf ("Subquery scope should work, got: %v" , err )
523+ }
524+ }
525+
526+ // Helper function to set up test data for scope tests
527+ func setupScopeTestData (t * testing.T ) {
528+ // Clean up any existing data
529+ DB .Exec ("DELETE FROM users WHERE \" name\" LIKE 'ScopeUser%'" )
530+
531+ // Create test users
532+ users := []* User {
533+ GetUser ("ScopeUser1" , Config {}),
534+ GetUser ("ScopeUser2" , Config {}),
535+ GetUser ("ScopeUser3" , Config {}),
536+ }
537+
538+ err := DB .Create (& users ).Error
539+ if err != nil {
540+ t .Fatalf ("Failed to create test data: %v" , err )
541+ }
542+ }
0 commit comments