Skip to content

Commit daccf44

Browse files
committed
tests: enhance connection test coverage
1 parent eb86661 commit daccf44

File tree

2 files changed

+339
-0
lines changed

2 files changed

+339
-0
lines changed

tests/connection_test.go

Lines changed: 329 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,11 @@
3939
package tests
4040

4141
import (
42+
"context"
4243
"fmt"
44+
"sync"
4345
"testing"
46+
"time"
4447

4548
"gorm.io/gorm"
4649
)
@@ -65,3 +68,329 @@ func TestWithSingleConnection(t *testing.T) {
6568
t.Errorf("WithSingleConnection() method should get correct value, expect: %v, got %v", expectedString, actualString)
6669
}
6770
}
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+
}

tests/passed-tests.txt

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,16 @@ TestPluginCallbacks
3838
TestCallbacksGet
3939
TestCallbacksRemove
4040
TestWithSingleConnection
41+
TestTransactionCommitRollback
42+
TestConnectionAfterError
43+
TestConcurrentConnections
44+
TestContextTimeout
45+
TestLargeResultSet
46+
TestSessionInfo
47+
TestConnectionPing
48+
TestIntervalDataTypes
49+
TestConnectionPoolStats
50+
TestDatabaseVersionInfo
4151
TestCountWithGroup
4252
TestCount
4353
TestCountOnEmptyTable

0 commit comments

Comments
 (0)