Skip to content

Commit 766579d

Browse files
authored
test(contrib/drivers/mariadb): add transaction, where, hook and ctx tests (gogf#4720)
## Summary - Port 28 transaction tests: Begin/Commit/Rollback, nested SavePoint, transaction propagation (Required/Nested/NotSupported), timeout, panic recovery, concurrent transactions - Port 41 where-condition tests: Where/WhereOr/WhereNot/WhereIn/WhereBetween, prefix handling, complex AND/OR combinations, NULL checks, struct/map/slice parameter types - Port 11 hook tests: HookSelect/HookInsert/HookUpdate/HookDelete for both Model and raw SQL paths, hook chaining and context propagation - Port 8 ctx tests: context propagation through Model/TX operations, context-based logging with traceId verification 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 030cd84 commit 766579d

5 files changed

Lines changed: 4751 additions & 2 deletions
Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
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+
"context"
11+
"testing"
12+
"time"
13+
14+
"github.com/gogf/gf/v2/database/gdb"
15+
"github.com/gogf/gf/v2/os/glog"
16+
"github.com/gogf/gf/v2/test/gtest"
17+
)
18+
19+
func Test_Ctx(t *testing.T) {
20+
gtest.C(t, func(t *gtest.T) {
21+
db, err := gdb.Instance()
22+
t.AssertNil(err)
23+
24+
err1 := db.PingMaster()
25+
err2 := db.PingSlave()
26+
t.Assert(err1, nil)
27+
t.Assert(err2, nil)
28+
29+
newDb := db.Ctx(context.Background())
30+
t.AssertNE(newDb, nil)
31+
})
32+
}
33+
34+
func Test_Ctx_Query(t *testing.T) {
35+
db.GetLogger().(*glog.Logger).SetCtxKeys("SpanId", "TraceId")
36+
gtest.C(t, func(t *gtest.T) {
37+
db.SetDebug(true)
38+
defer db.SetDebug(false)
39+
ctx := context.WithValue(context.Background(), "TraceId", "12345678")
40+
ctx = context.WithValue(ctx, "SpanId", "0.1")
41+
db.Query(ctx, "select 1")
42+
})
43+
gtest.C(t, func(t *gtest.T) {
44+
db.SetDebug(true)
45+
defer db.SetDebug(false)
46+
db.Query(ctx, "select 2")
47+
})
48+
}
49+
50+
func Test_Ctx_Model(t *testing.T) {
51+
table := createInitTable()
52+
defer dropTable(table)
53+
db.GetLogger().(*glog.Logger).SetCtxKeys("SpanId", "TraceId")
54+
gtest.C(t, func(t *gtest.T) {
55+
db.SetDebug(true)
56+
defer db.SetDebug(false)
57+
ctx := context.WithValue(context.Background(), "TraceId", "12345678")
58+
ctx = context.WithValue(ctx, "SpanId", "0.1")
59+
db.Model(table).Ctx(ctx).All()
60+
})
61+
gtest.C(t, func(t *gtest.T) {
62+
db.SetDebug(true)
63+
defer db.SetDebug(false)
64+
db.Model(table).All()
65+
})
66+
}
67+
68+
// Test_Ctx_Timeout tests context timeout behavior
69+
func Test_Ctx_Timeout(t *testing.T) {
70+
table := createInitTable()
71+
defer dropTable(table)
72+
73+
gtest.C(t, func(t *gtest.T) {
74+
// Create a context with very short timeout
75+
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Nanosecond)
76+
defer cancel()
77+
78+
// Wait for timeout
79+
time.Sleep(1 * time.Millisecond)
80+
81+
// Query should fail due to context timeout
82+
_, err := db.Model(table).Ctx(ctx).All()
83+
t.AssertNE(err, nil)
84+
})
85+
}
86+
87+
// Test_Ctx_Cancel tests context cancellation
88+
func Test_Ctx_Cancel(t *testing.T) {
89+
table := createInitTable()
90+
defer dropTable(table)
91+
92+
gtest.C(t, func(t *gtest.T) {
93+
ctx, cancel := context.WithCancel(context.Background())
94+
// Cancel immediately
95+
cancel()
96+
97+
// Query should fail due to cancelled context
98+
_, err := db.Model(table).Ctx(ctx).All()
99+
t.AssertNE(err, nil)
100+
})
101+
}
102+
103+
// Test_Ctx_Propagation_Transaction tests context propagation in transaction
104+
func Test_Ctx_Propagation_Transaction(t *testing.T) {
105+
table := createInitTable()
106+
defer dropTable(table)
107+
db.GetLogger().(*glog.Logger).SetCtxKeys("TraceId")
108+
109+
gtest.C(t, func(t *gtest.T) {
110+
db.SetDebug(true)
111+
defer db.SetDebug(false)
112+
113+
ctx := context.WithValue(context.Background(), "TraceId", "tx_trace_123")
114+
err := db.Transaction(ctx, func(ctx context.Context, tx gdb.TX) error {
115+
// Context should propagate to transaction operations
116+
_, err := tx.Model(table).Ctx(ctx).Where("id", 1).One()
117+
return err
118+
})
119+
t.AssertNil(err)
120+
})
121+
}
122+
123+
// Test_Ctx_Multiple_Values tests context with multiple values
124+
func Test_Ctx_Multiple_Values(t *testing.T) {
125+
table := createInitTable()
126+
defer dropTable(table)
127+
db.GetLogger().(*glog.Logger).SetCtxKeys("TraceId", "RequestId", "UserId")
128+
129+
gtest.C(t, func(t *gtest.T) {
130+
db.SetDebug(true)
131+
defer db.SetDebug(false)
132+
133+
ctx := context.WithValue(context.Background(), "TraceId", "trace_001")
134+
ctx = context.WithValue(ctx, "RequestId", "req_002")
135+
ctx = context.WithValue(ctx, "UserId", "user_003")
136+
137+
db.Model(table).Ctx(ctx).Where("id", 1).One()
138+
})
139+
}
140+
141+
// Test_Ctx_Nested_Operations tests context in nested operations
142+
func Test_Ctx_Nested_Operations(t *testing.T) {
143+
table := createInitTable()
144+
defer dropTable(table)
145+
db.GetLogger().(*glog.Logger).SetCtxKeys("TraceId")
146+
147+
gtest.C(t, func(t *gtest.T) {
148+
db.SetDebug(true)
149+
defer db.SetDebug(false)
150+
151+
ctx := context.WithValue(context.Background(), "TraceId", "nested_trace")
152+
153+
// Nested query operations should all have context
154+
result, err := db.Model(table).Ctx(ctx).Where("id>", 0).All()
155+
t.AssertNil(err)
156+
157+
if len(result) > 0 {
158+
// Another query using same context
159+
_, err = db.Model(table).Ctx(ctx).Where("id", result[0]["id"]).One()
160+
t.AssertNil(err)
161+
}
162+
})
163+
}
Lines changed: 229 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,229 @@
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+
"context"
11+
"database/sql"
12+
"fmt"
13+
"testing"
14+
15+
"github.com/gogf/gf/v2/container/gvar"
16+
"github.com/gogf/gf/v2/database/gdb"
17+
"github.com/gogf/gf/v2/frame/g"
18+
"github.com/gogf/gf/v2/test/gtest"
19+
)
20+
21+
func Test_Model_Hook_Select(t *testing.T) {
22+
table := createInitTable()
23+
defer dropTable(table)
24+
25+
gtest.C(t, func(t *gtest.T) {
26+
m := db.Model(table).Hook(gdb.HookHandler{
27+
Select: func(ctx context.Context, in *gdb.HookSelectInput) (result gdb.Result, err error) {
28+
result, err = in.Next(ctx)
29+
if err != nil {
30+
return
31+
}
32+
for i, record := range result {
33+
record["test"] = gvar.New(100 + record["id"].Int())
34+
result[i] = record
35+
}
36+
return
37+
},
38+
})
39+
all, err := m.Where(`id > 6`).OrderAsc(`id`).All()
40+
t.AssertNil(err)
41+
t.Assert(len(all), 4)
42+
t.Assert(all[0]["id"].Int(), 7)
43+
t.Assert(all[0]["test"].Int(), 107)
44+
t.Assert(all[1]["test"].Int(), 108)
45+
t.Assert(all[2]["test"].Int(), 109)
46+
t.Assert(all[3]["test"].Int(), 110)
47+
})
48+
}
49+
50+
func Test_Model_Hook_Insert(t *testing.T) {
51+
table := createTable()
52+
defer dropTable(table)
53+
54+
gtest.C(t, func(t *gtest.T) {
55+
m := db.Model(table).Hook(gdb.HookHandler{
56+
Insert: func(ctx context.Context, in *gdb.HookInsertInput) (result sql.Result, err error) {
57+
for i, item := range in.Data {
58+
item["passport"] = fmt.Sprintf(`test_port_%d`, item["id"])
59+
item["nickname"] = fmt.Sprintf(`test_name_%d`, item["id"])
60+
in.Data[i] = item
61+
}
62+
return in.Next(ctx)
63+
},
64+
})
65+
_, err := m.Insert(g.Map{
66+
"id": 1,
67+
"nickname": "name_1",
68+
})
69+
t.AssertNil(err)
70+
one, err := m.One()
71+
t.AssertNil(err)
72+
t.Assert(one["id"].Int(), 1)
73+
t.Assert(one["passport"], `test_port_1`)
74+
t.Assert(one["nickname"], `test_name_1`)
75+
})
76+
}
77+
78+
func Test_Model_Hook_Update(t *testing.T) {
79+
table := createInitTable()
80+
defer dropTable(table)
81+
82+
gtest.C(t, func(t *gtest.T) {
83+
m := db.Model(table).Hook(gdb.HookHandler{
84+
Update: func(ctx context.Context, in *gdb.HookUpdateInput) (result sql.Result, err error) {
85+
switch value := in.Data.(type) {
86+
case gdb.List:
87+
for i, data := range value {
88+
data["passport"] = `port`
89+
data["nickname"] = `name`
90+
value[i] = data
91+
}
92+
in.Data = value
93+
94+
case gdb.Map:
95+
value["passport"] = `port`
96+
value["nickname"] = `name`
97+
in.Data = value
98+
}
99+
return in.Next(ctx)
100+
},
101+
})
102+
_, err := m.Data(g.Map{
103+
"nickname": "name_1",
104+
}).WherePri(1).Update()
105+
t.AssertNil(err)
106+
107+
one, err := m.One()
108+
t.AssertNil(err)
109+
t.Assert(one["id"].Int(), 1)
110+
t.Assert(one["passport"], `port`)
111+
t.Assert(one["nickname"], `name`)
112+
})
113+
}
114+
115+
func Test_Model_Hook_Delete(t *testing.T) {
116+
table := createInitTable()
117+
defer dropTable(table)
118+
119+
gtest.C(t, func(t *gtest.T) {
120+
m := db.Model(table).Hook(gdb.HookHandler{
121+
Delete: func(ctx context.Context, in *gdb.HookDeleteInput) (result sql.Result, err error) {
122+
return db.Model(table).Data(g.Map{
123+
"nickname": `deleted`,
124+
}).Where(in.Condition).Update()
125+
},
126+
})
127+
_, err := m.Where(1).Delete()
128+
t.AssertNil(err)
129+
130+
all, err := m.All()
131+
t.AssertNil(err)
132+
for _, item := range all {
133+
t.Assert(item["nickname"].String(), `deleted`)
134+
}
135+
})
136+
}
137+
138+
// Test_Model_Hook_Multiple tests multiple hooks execution order
139+
func Test_Model_Hook_Multiple(t *testing.T) {
140+
table := createInitTable()
141+
defer dropTable(table)
142+
143+
gtest.C(t, func(t *gtest.T) {
144+
var execOrder []string
145+
146+
m := db.Model(table).Hook(gdb.HookHandler{
147+
Select: func(ctx context.Context, in *gdb.HookSelectInput) (result gdb.Result, err error) {
148+
execOrder = append(execOrder, "hook1_before")
149+
result, err = in.Next(ctx)
150+
execOrder = append(execOrder, "hook1_after")
151+
return
152+
},
153+
}).Hook(gdb.HookHandler{
154+
Select: func(ctx context.Context, in *gdb.HookSelectInput) (result gdb.Result, err error) {
155+
execOrder = append(execOrder, "hook2_before")
156+
result, err = in.Next(ctx)
157+
execOrder = append(execOrder, "hook2_after")
158+
return
159+
},
160+
})
161+
162+
_, err := m.Where("id", 1).One()
163+
t.AssertNil(err)
164+
165+
// Verify only the last registered hook executes (Hook is override, not chain)
166+
t.Assert(len(execOrder), 2)
167+
t.Assert(execOrder, g.Slice{"hook2_before", "hook2_after"})
168+
})
169+
}
170+
171+
// Test_Model_Hook_Error_Abort tests hook returning error aborts operation
172+
func Test_Model_Hook_Error_Abort(t *testing.T) {
173+
table := createInitTable()
174+
defer dropTable(table)
175+
176+
gtest.C(t, func(t *gtest.T) {
177+
m := db.Model(table).Hook(gdb.HookHandler{
178+
Insert: func(ctx context.Context, in *gdb.HookInsertInput) (result sql.Result, err error) {
179+
// Return error to abort insert
180+
return nil, fmt.Errorf("hook aborted insert")
181+
},
182+
})
183+
184+
_, err := m.Insert(g.Map{
185+
"passport": "test_abort",
186+
"password": "pass",
187+
"nickname": "name",
188+
})
189+
t.AssertNE(err, nil)
190+
t.Assert(err.Error(), "hook aborted insert")
191+
192+
// Verify record was not inserted
193+
count, err := db.Model(table).Where("passport", "test_abort").Count()
194+
t.AssertNil(err)
195+
t.Assert(count, 0)
196+
})
197+
}
198+
199+
// Test_Model_Hook_Modify_Data tests hook modifying data before insert
200+
func Test_Model_Hook_Modify_Data(t *testing.T) {
201+
table := createTable()
202+
defer dropTable(table)
203+
204+
gtest.C(t, func(t *gtest.T) {
205+
m := db.Model(table).Hook(gdb.HookHandler{
206+
Insert: func(ctx context.Context, in *gdb.HookInsertInput) (result sql.Result, err error) {
207+
// Modify all data items
208+
for i := range in.Data {
209+
in.Data[i]["password"] = "encrypted_" + fmt.Sprint(in.Data[i]["password"])
210+
in.Data[i]["nickname"] = "verified_" + fmt.Sprint(in.Data[i]["nickname"])
211+
}
212+
return in.Next(ctx)
213+
},
214+
})
215+
216+
_, err := m.Insert(g.Map{
217+
"passport": "test_user",
218+
"password": "plain123",
219+
"nickname": "john",
220+
})
221+
t.AssertNil(err)
222+
223+
// Verify data was modified by hook
224+
one, err := db.Model(table).Where("passport", "test_user").One()
225+
t.AssertNil(err)
226+
t.Assert(one["password"].String(), "encrypted_plain123")
227+
t.Assert(one["nickname"].String(), "verified_john")
228+
})
229+
}

0 commit comments

Comments
 (0)