Skip to content

Commit 0588009

Browse files
authored
test(contrib/drivers/mariadb): add pagination, error, concurrent, rawtype and sharding tests (gogf#4723)
## Summary - Port 11 pagination tests: Page/Limit/Offset, combined with Where/Order, boundary conditions (page 0, large offset), Count with pagination - Port 8 error-handling tests: invalid table/field names, syntax errors, duplicate key, connection errors, error wrapping and message verification - Port 5 concurrency tests: parallel read/write with goroutines and WaitGroup, concurrent transactions, race condition verification - Port 6 raw-type tests: custom type scanning, time.Time handling, json.RawMessage, sql.NullString/NullInt64, []byte fields - Port 6 sharding/table-name tests: dynamic table name via Sharding callback, table name with prefix, schema.table format All tests are structurally identical to the MySQL driver baseline. SQL syntax is standard and shared. Package and import references are adapted for MariaDB. ref gogf#4689
1 parent 6204c13 commit 0588009

6 files changed

Lines changed: 2761 additions & 3 deletions

contrib/drivers/mariadb/mariadb_unit_init_test.go

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,10 @@ const (
3030
)
3131

3232
var (
33-
db gdb.DB
34-
db2 gdb.DB
35-
ctx = context.TODO()
33+
db gdb.DB
34+
db2 gdb.DB
35+
dbInvalid gdb.DB
36+
ctx = context.TODO()
3637
)
3738

3839
func init() {
@@ -61,6 +62,19 @@ func init() {
6162
}
6263
db = db.Schema(TestSchema1)
6364
db2 = db.Schema(TestSchema2)
65+
66+
// Invalid db (wrong port for testing error handling).
67+
nodeInvalid := gdb.ConfigNode{
68+
Link: fmt.Sprintf("mariadb:root:%s@tcp(127.0.0.1:3317)/?loc=Local&parseTime=true", TestDbPass),
69+
TranTimeout: time.Second * 3,
70+
}
71+
gdb.AddConfigNode("nodeinvalid", nodeInvalid)
72+
if r, err := gdb.NewByGroup("nodeinvalid"); err != nil {
73+
gtest.Error(err)
74+
} else {
75+
dbInvalid = r
76+
}
77+
dbInvalid = dbInvalid.Schema(TestSchema1)
6478
}
6579

6680
func createTable(table ...string) string {
Lines changed: 338 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,338 @@
1+
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
2+
//
3+
// This Source Code Form is subject to the terms of the MIT License.
4+
// If a copy of the MIT was not distributed with this file,
5+
// You can obtain one at https://github.com/gogf/gf.
6+
7+
package mariadb_test
8+
9+
import (
10+
"fmt"
11+
"sync"
12+
"testing"
13+
14+
"github.com/gogf/gf/v2/database/gdb"
15+
"github.com/gogf/gf/v2/frame/g"
16+
"github.com/gogf/gf/v2/test/gtest"
17+
)
18+
19+
// Test_Concurrent_Insert tests concurrent Insert operations
20+
func Test_Concurrent_Insert(t *testing.T) {
21+
table := createTable()
22+
defer dropTable(table)
23+
24+
gtest.C(t, func(t *gtest.T) {
25+
var wg sync.WaitGroup
26+
concurrency := 10
27+
28+
wg.Add(concurrency)
29+
for i := 0; i < concurrency; i++ {
30+
go func(id int) {
31+
defer wg.Done()
32+
_, err := db.Model(table).Insert(g.Map{
33+
"passport": fmt.Sprintf("user_%d", id),
34+
"password": fmt.Sprintf("pass_%d", id),
35+
"nickname": fmt.Sprintf("name_%d", id),
36+
})
37+
t.AssertNil(err)
38+
}(i + 1)
39+
}
40+
wg.Wait()
41+
42+
// Verify all records inserted
43+
count, err := db.Model(table).Count()
44+
t.AssertNil(err)
45+
t.Assert(count, concurrency)
46+
})
47+
}
48+
49+
// Test_Concurrent_Update tests concurrent Update operations
50+
func Test_Concurrent_Update(t *testing.T) {
51+
table := createInitTable()
52+
defer dropTable(table)
53+
54+
gtest.C(t, func(t *gtest.T) {
55+
var wg sync.WaitGroup
56+
concurrency := 5
57+
58+
wg.Add(concurrency)
59+
for i := 0; i < concurrency; i++ {
60+
go func(id int) {
61+
defer wg.Done()
62+
_, err := db.Model(table).Data(g.Map{
63+
"nickname": fmt.Sprintf("updated_%d", id),
64+
}).Where("id", id+1).Update()
65+
t.AssertNil(err)
66+
}(i)
67+
}
68+
wg.Wait()
69+
70+
// Verify updates
71+
for i := 0; i < concurrency; i++ {
72+
one, err := db.Model(table).Where("id", i+1).One()
73+
t.AssertNil(err)
74+
t.Assert(one["nickname"].String(), fmt.Sprintf("updated_%d", i))
75+
}
76+
})
77+
}
78+
79+
// Test_Concurrent_Delete tests concurrent Delete operations
80+
func Test_Concurrent_Delete(t *testing.T) {
81+
table := createInitTable()
82+
defer dropTable(table)
83+
84+
gtest.C(t, func(t *gtest.T) {
85+
var wg sync.WaitGroup
86+
concurrency := 5
87+
88+
wg.Add(concurrency)
89+
for i := 0; i < concurrency; i++ {
90+
go func(id int) {
91+
defer wg.Done()
92+
_, err := db.Model(table).Where("id", id+1).Delete()
93+
t.AssertNil(err)
94+
}(i)
95+
}
96+
wg.Wait()
97+
98+
// Verify deletions
99+
count, err := db.Model(table).Count()
100+
t.AssertNil(err)
101+
t.Assert(count, TableSize-concurrency)
102+
})
103+
}
104+
105+
// Test_Concurrent_Query tests concurrent Query operations
106+
func Test_Concurrent_Query(t *testing.T) {
107+
table := createInitTable()
108+
defer dropTable(table)
109+
110+
gtest.C(t, func(t *gtest.T) {
111+
var wg sync.WaitGroup
112+
concurrency := 20
113+
114+
wg.Add(concurrency)
115+
for i := 0; i < concurrency; i++ {
116+
go func(id int) {
117+
defer wg.Done()
118+
result, err := db.Model(table).Where("id", (id%TableSize)+1).One()
119+
t.AssertNil(err)
120+
t.AssertNE(result, nil)
121+
}(i)
122+
}
123+
wg.Wait()
124+
})
125+
}
126+
127+
// Test_Concurrent_Transaction tests concurrent transaction operations
128+
func Test_Concurrent_Transaction(t *testing.T) {
129+
table := createTable()
130+
defer dropTable(table)
131+
132+
gtest.C(t, func(t *gtest.T) {
133+
var wg sync.WaitGroup
134+
concurrency := 10
135+
136+
wg.Add(concurrency)
137+
for i := 0; i < concurrency; i++ {
138+
go func(id int) {
139+
defer wg.Done()
140+
err := db.Transaction(ctx, func(ctx g.Ctx, tx gdb.TX) error {
141+
_, err := tx.Model(table).Insert(g.Map{
142+
"passport": fmt.Sprintf("user_%d", id),
143+
"password": fmt.Sprintf("pass_%d", id),
144+
"nickname": fmt.Sprintf("name_%d", id),
145+
})
146+
return err
147+
})
148+
t.AssertNil(err)
149+
}(i + 1)
150+
}
151+
wg.Wait()
152+
153+
// Verify all transactions committed
154+
count, err := db.Model(table).Count()
155+
t.AssertNil(err)
156+
t.Assert(count, concurrency)
157+
})
158+
}
159+
160+
// Test_Concurrent_Mixed_Operations tests mixed concurrent operations
161+
func Test_Concurrent_Mixed_Operations(t *testing.T) {
162+
table := createInitTable()
163+
defer dropTable(table)
164+
165+
gtest.C(t, func(t *gtest.T) {
166+
var wg sync.WaitGroup
167+
operations := 30
168+
169+
wg.Add(operations)
170+
for i := 0; i < operations; i++ {
171+
op := i % 3
172+
switch op {
173+
case 0: // Insert
174+
go func(id int) {
175+
defer wg.Done()
176+
_, _ = db.Model(table).Insert(g.Map{
177+
"passport": fmt.Sprintf("new_user_%d", id),
178+
"password": fmt.Sprintf("new_pass_%d", id),
179+
"nickname": fmt.Sprintf("new_name_%d", id),
180+
})
181+
}(i)
182+
case 1: // Update
183+
go func(id int) {
184+
defer wg.Done()
185+
targetId := (id % TableSize) + 1
186+
_, _ = db.Model(table).Data(g.Map{
187+
"nickname": fmt.Sprintf("concurrent_%d", id),
188+
}).Where("id", targetId).Update()
189+
}(i)
190+
case 2: // Query
191+
go func(id int) {
192+
defer wg.Done()
193+
targetId := (id % TableSize) + 1
194+
_, _ = db.Model(table).Where("id", targetId).One()
195+
}(i)
196+
}
197+
}
198+
wg.Wait()
199+
200+
// Verify database is still consistent
201+
count, err := db.Model(table).Count()
202+
t.AssertNil(err)
203+
t.AssertGT(count, TableSize)
204+
})
205+
}
206+
207+
// Test_Concurrent_Connection_Pool tests connection pool under load
208+
func Test_Concurrent_Connection_Pool(t *testing.T) {
209+
table := createInitTable()
210+
defer dropTable(table)
211+
212+
gtest.C(t, func(t *gtest.T) {
213+
var wg sync.WaitGroup
214+
concurrency := 50
215+
216+
wg.Add(concurrency)
217+
for i := 0; i < concurrency; i++ {
218+
go func(id int) {
219+
defer wg.Done()
220+
// Each goroutine performs multiple operations
221+
for j := 0; j < 5; j++ {
222+
_, err := db.Model(table).Where("id", (id%TableSize)+1).One()
223+
t.AssertNil(err)
224+
}
225+
}(i)
226+
}
227+
wg.Wait()
228+
})
229+
}
230+
231+
// Test_Concurrent_Schema_Switch tests concurrent schema switching
232+
func Test_Concurrent_Schema_Switch(t *testing.T) {
233+
table1 := createTableWithDb(db, "test_schema_1")
234+
table2 := createTableWithDb(db2, "test_schema_2")
235+
defer dropTableWithDb(db, table1)
236+
defer dropTableWithDb(db2, table2)
237+
238+
gtest.C(t, func(t *gtest.T) {
239+
var wg sync.WaitGroup
240+
concurrency := 10
241+
242+
wg.Add(concurrency * 2)
243+
for i := 0; i < concurrency; i++ {
244+
// Insert to schema1
245+
go func(id int) {
246+
defer wg.Done()
247+
_, err := db.Model(table1).Insert(g.Map{
248+
"passport": fmt.Sprintf("user_s1_%d", id),
249+
"password": fmt.Sprintf("pass_%d", id),
250+
"nickname": fmt.Sprintf("name_%d", id),
251+
})
252+
t.AssertNil(err)
253+
}(i)
254+
255+
// Insert to schema2
256+
go func(id int) {
257+
defer wg.Done()
258+
_, err := db2.Model(table2).Insert(g.Map{
259+
"passport": fmt.Sprintf("user_s2_%d", id),
260+
"password": fmt.Sprintf("pass_%d", id),
261+
"nickname": fmt.Sprintf("name_%d", id),
262+
})
263+
t.AssertNil(err)
264+
}(i)
265+
}
266+
wg.Wait()
267+
268+
// Verify both schemas
269+
count1, err := db.Model(table1).Count()
270+
t.AssertNil(err)
271+
t.Assert(count1, concurrency)
272+
273+
count2, err := db2.Model(table2).Count()
274+
t.AssertNil(err)
275+
t.Assert(count2, concurrency)
276+
})
277+
}
278+
279+
// Test_Concurrent_Model_Clone tests concurrent model cloning
280+
func Test_Concurrent_Model_Clone(t *testing.T) {
281+
table := createInitTable()
282+
defer dropTable(table)
283+
284+
gtest.C(t, func(t *gtest.T) {
285+
baseModel := db.Model(table).Where("id>", 0)
286+
var wg sync.WaitGroup
287+
concurrency := 20
288+
289+
wg.Add(concurrency)
290+
for i := 0; i < concurrency; i++ {
291+
go func(id int) {
292+
defer wg.Done()
293+
// Clone model for each goroutine
294+
m := baseModel.Clone()
295+
result, err := m.Where("id<=", TableSize/2).All()
296+
t.AssertNil(err)
297+
t.AssertGT(len(result), 0)
298+
}(i)
299+
}
300+
wg.Wait()
301+
})
302+
}
303+
304+
// Test_Concurrent_Batch_Insert tests concurrent batch insert operations
305+
func Test_Concurrent_Batch_Insert(t *testing.T) {
306+
table := createTable()
307+
defer dropTable(table)
308+
309+
gtest.C(t, func(t *gtest.T) {
310+
var wg sync.WaitGroup
311+
concurrency := 5
312+
batchSize := 10
313+
314+
wg.Add(concurrency)
315+
for i := 0; i < concurrency; i++ {
316+
go func(batchId int) {
317+
defer wg.Done()
318+
batch := make([]g.Map, 0, batchSize)
319+
for j := 0; j < batchSize; j++ {
320+
id := batchId*batchSize + j
321+
batch = append(batch, g.Map{
322+
"passport": fmt.Sprintf("batch_user_%d", id),
323+
"password": fmt.Sprintf("pass_%d", id),
324+
"nickname": fmt.Sprintf("name_%d", id),
325+
})
326+
}
327+
_, err := db.Model(table).Data(batch).Insert()
328+
t.AssertNil(err)
329+
}(i)
330+
}
331+
wg.Wait()
332+
333+
// Verify all batch inserts
334+
count, err := db.Model(table).Count()
335+
t.AssertNil(err)
336+
t.Assert(count, concurrency*batchSize)
337+
})
338+
}

0 commit comments

Comments
 (0)