39
39
package tests
40
40
41
41
import (
42
+ "context"
42
43
"fmt"
44
+ "sync"
43
45
"testing"
46
+ "time"
44
47
45
48
"gorm.io/gorm"
46
49
)
@@ -65,3 +68,329 @@ func TestWithSingleConnection(t *testing.T) {
65
68
t .Errorf ("WithSingleConnection() method should get correct value, expect: %v, got %v" , expectedString , actualString )
66
69
}
67
70
}
71
+
72
+ func TestTransactionCommitRollback (t * testing.T ) {
73
+ // Create test table
74
+ type TestTxTable struct {
75
+ ID uint `gorm:"primaryKey"`
76
+ Name string `gorm:"size:100;column:name"`
77
+ }
78
+
79
+ err := DB .AutoMigrate (& TestTxTable {})
80
+ if err != nil {
81
+ t .Fatalf ("Failed to migrate test table: %v" , err )
82
+ }
83
+ defer DB .Migrator ().DropTable (& TestTxTable {})
84
+
85
+ // Test commit
86
+ t .Run ("Commit" , func (t * testing.T ) {
87
+ tx := DB .Begin ()
88
+ if tx .Error != nil {
89
+ t .Fatalf ("Failed to begin transaction: %v" , tx .Error )
90
+ }
91
+
92
+ record := TestTxTable {Name : "test_commit" }
93
+ if err := tx .Create (& record ).Error ; err != nil {
94
+ t .Errorf ("Failed to create record in transaction: %v" , err )
95
+ }
96
+
97
+ if err := tx .Commit ().Error ; err != nil {
98
+ t .Errorf ("Failed to commit transaction: %v" , err )
99
+ }
100
+
101
+ // Verify record exists using quoted column name
102
+ var count int64
103
+ if err := DB .Model (& TestTxTable {}).Where ("\" name\" = ?" , "test_commit" ).Count (& count ).Error ; err != nil {
104
+ t .Errorf ("Failed to count records: %v" , err )
105
+ }
106
+ if count != 1 {
107
+ t .Errorf ("Expected 1 record after commit, got %d" , count )
108
+ }
109
+ })
110
+
111
+ // Test rollback
112
+ t .Run ("Rollback" , func (t * testing.T ) {
113
+ tx := DB .Begin ()
114
+ if tx .Error != nil {
115
+ t .Fatalf ("Failed to begin transaction: %v" , tx .Error )
116
+ }
117
+
118
+ record := TestTxTable {Name : "test_rollback" }
119
+ if err := tx .Create (& record ).Error ; err != nil {
120
+ t .Errorf ("Failed to create record in transaction: %v" , err )
121
+ }
122
+
123
+ if err := tx .Rollback ().Error ; err != nil {
124
+ t .Errorf ("Failed to rollback transaction: %v" , err )
125
+ }
126
+
127
+ // Verify record doesn't exist using quoted column name
128
+ var count int64
129
+ if err := DB .Model (& TestTxTable {}).Where ("\" name\" = ?" , "test_rollback" ).Count (& count ).Error ; err != nil {
130
+ t .Errorf ("Failed to count records: %v" , err )
131
+ }
132
+ if count != 0 {
133
+ t .Errorf ("Expected 0 records after rollback, got %d" , count )
134
+ }
135
+ })
136
+ }
137
+
138
+ func TestConnectionAfterError (t * testing.T ) {
139
+ // Execute an invalid query to cause an error
140
+ err := DB .Exec ("SELECT invalid_column FROM dual" ).Error
141
+ if err == nil {
142
+ t .Error ("Expected error for invalid query, but got nil" )
143
+ }
144
+
145
+ // Verify connection still works after error
146
+ var result string
147
+ err = DB .Raw ("SELECT 'connection_works' FROM dual" ).Scan (& result ).Error
148
+ if err != nil {
149
+ t .Errorf ("Connection should work after error, but got: %v" , err )
150
+ }
151
+ if result != "connection_works" {
152
+ t .Errorf ("Expected 'connection_works', got '%s'" , result )
153
+ }
154
+ }
155
+
156
+ func TestConcurrentConnections (t * testing.T ) {
157
+ const numGoroutines = 10
158
+ const operationsPerGoroutine = 5
159
+
160
+ var wg sync.WaitGroup
161
+ errors := make (chan error , numGoroutines * operationsPerGoroutine )
162
+
163
+ for i := 0 ; i < numGoroutines ; i ++ {
164
+ wg .Add (1 )
165
+ go func (goroutineID int ) {
166
+ defer wg .Done ()
167
+ for j := 0 ; j < operationsPerGoroutine ; j ++ {
168
+ var result string
169
+ query := fmt .Sprintf ("SELECT 'goroutine_%d_op_%d' FROM dual" , goroutineID , j )
170
+ if err := DB .Raw (query ).Scan (& result ).Error ; err != nil {
171
+ errors <- fmt .Errorf ("goroutine %d operation %d failed: %v" , goroutineID , j , err )
172
+ return
173
+ }
174
+ expected := fmt .Sprintf ("goroutine_%d_op_%d" , goroutineID , j )
175
+ if result != expected {
176
+ errors <- fmt .Errorf ("goroutine %d operation %d: expected '%s', got '%s'" , goroutineID , j , expected , result )
177
+ return
178
+ }
179
+ }
180
+ }(i )
181
+ }
182
+
183
+ wg .Wait ()
184
+ close (errors )
185
+
186
+ for err := range errors {
187
+ t .Error (err )
188
+ }
189
+ }
190
+
191
+ func TestContextTimeout (t * testing.T ) {
192
+ // Test with very short timeout that should trigger
193
+ ctx , cancel := context .WithTimeout (context .Background (), 1 * time .Millisecond )
194
+ defer cancel ()
195
+
196
+ // This should timeout for most operations
197
+ err := DB .WithContext (ctx ).Raw ("SELECT 1 FROM dual" ).Error
198
+ if err == nil {
199
+ t .Log ("Operation completed before timeout (this is possible on fast systems)" )
200
+ }
201
+
202
+ // Test with reasonable timeout that should succeed
203
+ ctx2 , cancel2 := context .WithTimeout (context .Background (), 5 * time .Second )
204
+ defer cancel2 ()
205
+
206
+ var result int
207
+ err = DB .WithContext (ctx2 ).Raw ("SELECT 42 FROM dual" ).Scan (& result ).Error
208
+ if err != nil {
209
+ t .Errorf ("Operation with reasonable timeout failed: %v" , err )
210
+ }
211
+ if result != 42 {
212
+ t .Errorf ("Expected 42, got %d" , result )
213
+ }
214
+ }
215
+
216
+ func TestLargeResultSet (t * testing.T ) {
217
+ var results []struct {
218
+ RowNum int `gorm:"column:ROW_NUM"`
219
+ Value string `gorm:"column:VALUE"`
220
+ }
221
+
222
+ query := `
223
+ SELECT LEVEL as row_num, 'row_' || LEVEL as value
224
+ FROM dual
225
+ CONNECT BY LEVEL <= 1000
226
+ `
227
+
228
+ err := DB .Raw (query ).Scan (& results ).Error
229
+ if err != nil {
230
+ t .Errorf ("Failed to execute large result set query: %v" , err )
231
+ return
232
+ }
233
+
234
+ if len (results ) != 1000 {
235
+ t .Errorf ("Expected 1000 rows, got %d" , len (results ))
236
+ return
237
+ }
238
+
239
+ // Verify first and last rows
240
+ if results [0 ].RowNum != 1 || results [0 ].Value != "row_1" {
241
+ t .Errorf ("First row incorrect: %+v" , results [0 ])
242
+ }
243
+ if results [999 ].RowNum != 1000 || results [999 ].Value != "row_1000" {
244
+ t .Errorf ("Last row incorrect: %+v" , results [999 ])
245
+ }
246
+ }
247
+
248
+ func TestSessionInfo (t * testing.T ) {
249
+ // Test USER function first (should always work)
250
+ var username string
251
+ err := DB .Raw ("SELECT USER FROM dual" ).Scan (& username ).Error
252
+ if err != nil {
253
+ t .Errorf ("Failed to get username: %v" , err )
254
+ return
255
+ }
256
+
257
+ if username == "" {
258
+ t .Skip ("USER function returned empty - unusual Oracle configuration" )
259
+ }
260
+
261
+ // Test SYS_CONTEXT functions
262
+ var sessionInfo struct {
263
+ InstanceName string `gorm:"column:instance_name"`
264
+ DatabaseName string `gorm:"column:database_name"`
265
+ }
266
+
267
+ query := `
268
+ SELECT
269
+ SYS_CONTEXT('USERENV', 'INSTANCE_NAME') as instance_name,
270
+ SYS_CONTEXT('USERENV', 'DB_NAME') as database_name
271
+ FROM dual
272
+ `
273
+
274
+ err = DB .Raw (query ).Scan (& sessionInfo ).Error
275
+ if err != nil {
276
+ t .Errorf ("Failed to get session context info: %v" , err )
277
+ return
278
+ }
279
+
280
+ // Log what we found
281
+ t .Logf ("Session Info - User: %s" , username )
282
+ if sessionInfo .InstanceName != "" {
283
+ t .Logf ("Instance: %s" , sessionInfo .InstanceName )
284
+ }
285
+ if sessionInfo .DatabaseName != "" {
286
+ t .Logf ("Database: %s" , sessionInfo .DatabaseName )
287
+ }
288
+
289
+ // Only require username - instance/database names might not be available in all environments
290
+ if sessionInfo .InstanceName == "" || sessionInfo .DatabaseName == "" {
291
+ t .Skip ("SYS_CONTEXT functions unavailable - likely permissions or configuration issue" )
292
+ }
293
+ }
294
+
295
+ func TestConnectionPing (t * testing.T ) {
296
+ sqlDB , err := DB .DB ()
297
+ if err != nil {
298
+ t .Fatalf ("Failed to get sql.DB: %v" , err )
299
+ }
300
+
301
+ err = sqlDB .Ping ()
302
+ if err != nil {
303
+ t .Errorf ("Database ping failed: %v" , err )
304
+ }
305
+
306
+ // Test ping with context
307
+ ctx , cancel := context .WithTimeout (context .Background (), 5 * time .Second )
308
+ defer cancel ()
309
+
310
+ err = sqlDB .PingContext (ctx )
311
+ if err != nil {
312
+ t .Errorf ("Database ping with context failed: %v" , err )
313
+ }
314
+ }
315
+
316
+ func TestIntervalDataTypes (t * testing.T ) {
317
+ // Test Year to Month interval
318
+ t .Run ("YearToMonth" , func (t * testing.T ) {
319
+ var result string
320
+ err := DB .Raw ("SELECT INTERVAL '2-3' YEAR TO MONTH FROM dual" ).Scan (& result ).Error
321
+ if err != nil {
322
+ t .Errorf ("Year to Month interval query failed: %v" , err )
323
+ }
324
+ t .Logf ("Year to Month interval result: %s" , result )
325
+ })
326
+
327
+ // Test Day to Second interval
328
+ t .Run ("DayToSecond" , func (t * testing.T ) {
329
+ var result string
330
+ err := DB .Raw ("SELECT INTERVAL '4 5:12:10.222' DAY TO SECOND FROM dual" ).Scan (& result ).Error
331
+ if err != nil {
332
+ t .Errorf ("Day to Second interval query failed: %v" , err )
333
+ }
334
+ t .Logf ("Day to Second interval result: %s" , result )
335
+ })
336
+ }
337
+
338
+ func TestConnectionPoolStats (t * testing.T ) {
339
+ sqlDB , err := DB .DB ()
340
+ if err != nil {
341
+ t .Fatalf ("Failed to get sql.DB: %v" , err )
342
+ }
343
+
344
+ stats := sqlDB .Stats ()
345
+ t .Logf ("Connection Pool Stats:" )
346
+ t .Logf (" Open Connections: %d" , stats .OpenConnections )
347
+ t .Logf (" In Use: %d" , stats .InUse )
348
+ t .Logf (" Idle: %d" , stats .Idle )
349
+ t .Logf (" Wait Count: %d" , stats .WaitCount )
350
+ t .Logf (" Wait Duration: %v" , stats .WaitDuration )
351
+ t .Logf (" Max Idle Closed: %d" , stats .MaxIdleClosed )
352
+ t .Logf (" Max Idle Time Closed: %d" , stats .MaxIdleTimeClosed )
353
+ t .Logf (" Max Lifetime Closed: %d" , stats .MaxLifetimeClosed )
354
+
355
+ // Basic sanity checks
356
+ if stats .OpenConnections < 0 {
357
+ t .Error ("Open connections should not be negative" )
358
+ }
359
+ if stats .InUse < 0 {
360
+ t .Error ("In use connections should not be negative" )
361
+ }
362
+ if stats .Idle < 0 {
363
+ t .Error ("Idle connections should not be negative" )
364
+ }
365
+ }
366
+
367
+ func TestDatabaseVersionInfo (t * testing.T ) {
368
+ var versionInfo struct {
369
+ Version string `gorm:"column:version"`
370
+ Banner string `gorm:"column:banner"`
371
+ }
372
+
373
+ query := `
374
+ SELECT
375
+ (SELECT VERSION FROM V$INSTANCE) as version,
376
+ (SELECT BANNER FROM V$VERSION WHERE ROWNUM = 1) as banner
377
+ FROM dual
378
+ `
379
+
380
+ err := DB .Raw (query ).Scan (& versionInfo ).Error
381
+ if err == nil && versionInfo .Version != "" && versionInfo .Banner != "" {
382
+ t .Logf ("Database Version: %s" , versionInfo .Version )
383
+ t .Logf ("Database Banner: %s" , versionInfo .Banner )
384
+ return
385
+ }
386
+
387
+ // Fallback to PRODUCT_COMPONENT_VERSION
388
+ var simpleVersion string
389
+ err = DB .Raw ("SELECT VERSION FROM PRODUCT_COMPONENT_VERSION WHERE PRODUCT LIKE 'Oracle%' AND ROWNUM = 1" ).Scan (& simpleVersion ).Error
390
+ if err == nil && simpleVersion != "" {
391
+ t .Logf ("Database Version: %s" , simpleVersion )
392
+ return
393
+ }
394
+
395
+ t .Skip ("Could not retrieve database version info - insufficient privileges to access system views" )
396
+ }
0 commit comments