mydb/adapter/mysql/mysql_test.go

380 lines
8.3 KiB
Go
Raw Normal View History

2023-09-18 15:15:42 +08:00
package mysql
import (
"database/sql"
"database/sql/driver"
"fmt"
"math/rand"
"strconv"
"testing"
"time"
"git.hexq.cn/tiglog/mydb"
"git.hexq.cn/tiglog/mydb/internal/testsuite"
"github.com/stretchr/testify/suite"
)
type int64Compat int64
type uintCompat uint
type stringCompat string
type uintCompatArray []uintCompat
func (u *int64Compat) Scan(src interface{}) error {
if src != nil {
switch v := src.(type) {
case int64:
*u = int64Compat((src).(int64))
case []byte:
i, err := strconv.ParseInt(string(v), 10, 64)
if err != nil {
return err
}
*u = int64Compat(i)
default:
panic(fmt.Sprintf("expected type %T", src))
}
}
return nil
}
type customJSON struct {
N string `json:"name"`
V float64 `json:"value"`
}
func (c customJSON) Value() (driver.Value, error) {
return JSONValue(c)
}
func (c *customJSON) Scan(src interface{}) error {
return ScanJSON(c, src)
}
type autoCustomJSON struct {
N string `json:"name"`
V float64 `json:"value"`
*JSONConverter
}
var (
_ = driver.Valuer(&customJSON{})
_ = sql.Scanner(&customJSON{})
)
type AdapterTests struct {
testsuite.Suite
}
func (s *AdapterTests) SetupSuite() {
s.Helper = &Helper{}
}
func (s *AdapterTests) TestInsertReturningCompositeKey_Issue383() {
sess := s.Session()
type Admin struct {
ID int `db:"ID,omitempty"`
Accounts string `db:"Accounts"`
LoginPassWord string `db:"LoginPassWord"`
Date time.Time `db:"Date"`
}
dateNow := time.Now()
a := Admin{
Accounts: "admin",
LoginPassWord: "E10ADC3949BA59ABBE56E057F20F883E",
Date: dateNow,
}
adminCollection := sess.Collection("admin")
err := adminCollection.InsertReturning(&a)
s.NoError(err)
s.NotZero(a.ID)
s.NotZero(a.Date)
s.Equal("admin", a.Accounts)
s.Equal("E10ADC3949BA59ABBE56E057F20F883E", a.LoginPassWord)
b := Admin{
Accounts: "admin2",
LoginPassWord: "E10ADC3949BA59ABBE56E057F20F883E",
Date: dateNow,
}
err = adminCollection.InsertReturning(&b)
s.NoError(err)
s.NotZero(b.ID)
s.NotZero(b.Date)
s.Equal("admin2", b.Accounts)
s.Equal("E10ADC3949BA59ABBE56E057F20F883E", a.LoginPassWord)
}
func (s *AdapterTests) TestIssue469_BadConnection() {
var err error
sess := s.Session()
// Ask the MySQL server to disconnect sessions that remain inactive for more
// than 1 second.
_, err = sess.SQL().Exec(`SET SESSION wait_timeout=1`)
s.NoError(err)
// Remain inactive for 2 seconds.
time.Sleep(time.Second * 2)
// A query should start a new connection, even if the server disconnected us.
_, err = sess.Collection("artist").Find().Count()
s.NoError(err)
// This is a new session, ask the MySQL server to disconnect sessions that
// remain inactive for more than 1 second.
_, err = sess.SQL().Exec(`SET SESSION wait_timeout=1`)
s.NoError(err)
// Remain inactive for 2 seconds.
time.Sleep(time.Second * 2)
// At this point the server should have disconnected us. Let's try to create
// a transaction anyway.
err = sess.Tx(func(sess mydb.Session) error {
var err error
_, err = sess.Collection("artist").Find().Count()
if err != nil {
return err
}
return nil
})
s.NoError(err)
// This is a new session, ask the MySQL server to disconnect sessions that
// remain inactive for more than 1 second.
_, err = sess.SQL().Exec(`SET SESSION wait_timeout=1`)
s.NoError(err)
err = sess.Tx(func(sess mydb.Session) error {
var err error
// This query should succeed.
_, err = sess.Collection("artist").Find().Count()
if err != nil {
panic(err.Error())
}
// Remain inactive for 2 seconds.
time.Sleep(time.Second * 2)
// This query should fail because the server disconnected us in the middle
// of a transaction.
_, err = sess.Collection("artist").Find().Count()
if err != nil {
return err
}
return nil
})
s.Error(err, "Expecting an error (can't recover from this)")
}
func (s *AdapterTests) TestMySQLTypes() {
sess := s.Session()
type MyType struct {
ID int64 `db:"id,omitempty"`
JSONMap JSONMap `db:"json_map"`
JSONObject JSONMap `db:"json_object"`
JSONArray JSONArray `db:"json_array"`
CustomJSONObject customJSON `db:"custom_json_object"`
AutoCustomJSONObject autoCustomJSON `db:"auto_custom_json_object"`
CustomJSONObjectPtr *customJSON `db:"custom_json_object_ptr,omitempty"`
AutoCustomJSONObjectPtr *autoCustomJSON `db:"auto_custom_json_object_ptr,omitempty"`
AutoCustomJSONObjectArray []autoCustomJSON `db:"auto_custom_json_object_array"`
AutoCustomJSONObjectMap map[string]autoCustomJSON `db:"auto_custom_json_object_map"`
Int64CompatValueJSONArray []int64Compat `db:"integer_compat_value_json_array"`
UIntCompatValueJSONArray uintCompatArray `db:"uinteger_compat_value_json_array"`
StringCompatValueJSONArray []stringCompat `db:"string_compat_value_json_array"`
}
origMyTypeTests := []MyType{
MyType{
Int64CompatValueJSONArray: []int64Compat{1, -2, 3, -4},
UIntCompatValueJSONArray: []uintCompat{1, 2, 3, 4},
StringCompatValueJSONArray: []stringCompat{"a", "b", "", "c"},
},
MyType{
Int64CompatValueJSONArray: []int64Compat(nil),
UIntCompatValueJSONArray: []uintCompat(nil),
StringCompatValueJSONArray: []stringCompat(nil),
},
MyType{
AutoCustomJSONObjectArray: []autoCustomJSON{
autoCustomJSON{
N: "Hello",
},
autoCustomJSON{
N: "World",
},
},
AutoCustomJSONObjectMap: map[string]autoCustomJSON{
"a": autoCustomJSON{
N: "Hello",
},
"b": autoCustomJSON{
N: "World",
},
},
JSONArray: JSONArray{float64(1), float64(2), float64(3), float64(4)},
},
MyType{
JSONArray: JSONArray{},
},
MyType{
JSONArray: JSONArray(nil),
},
MyType{},
MyType{
CustomJSONObject: customJSON{
N: "Hello",
},
AutoCustomJSONObject: autoCustomJSON{
N: "World",
},
},
MyType{
CustomJSONObject: customJSON{},
AutoCustomJSONObject: autoCustomJSON{},
},
MyType{
CustomJSONObject: customJSON{
N: "Hello 1",
},
AutoCustomJSONObject: autoCustomJSON{
N: "World 2",
},
},
MyType{
CustomJSONObjectPtr: nil,
AutoCustomJSONObjectPtr: nil,
},
MyType{
CustomJSONObjectPtr: &customJSON{},
AutoCustomJSONObjectPtr: &autoCustomJSON{},
},
MyType{
CustomJSONObjectPtr: &customJSON{
N: "Hello 3",
},
AutoCustomJSONObjectPtr: &autoCustomJSON{
N: "World 4",
},
},
MyType{},
MyType{
CustomJSONObject: customJSON{
V: 4.4,
},
},
MyType{
CustomJSONObject: customJSON{},
},
MyType{
CustomJSONObject: customJSON{
N: "Peter",
V: 5.56,
},
},
}
for i := 0; i < 100; i++ {
myTypeTests := make([]MyType, len(origMyTypeTests))
perm := rand.Perm(len(origMyTypeTests))
for i, v := range perm {
myTypeTests[v] = origMyTypeTests[i]
}
for i := range myTypeTests {
result, err := sess.Collection("my_types").Insert(myTypeTests[i])
s.NoError(err)
var actual MyType
err = sess.Collection("my_types").Find(result).One(&actual)
s.NoError(err)
expected := myTypeTests[i]
expected.ID = result.ID().(int64)
s.Equal(expected, actual)
}
for i := range myTypeTests {
res, err := sess.SQL().InsertInto("my_types").Values(myTypeTests[i]).Exec()
s.NoError(err)
id, err := res.LastInsertId()
s.NoError(err)
s.NotEqual(0, id)
var actual MyType
err = sess.Collection("my_types").Find(id).One(&actual)
s.NoError(err)
expected := myTypeTests[i]
expected.ID = id
s.Equal(expected, actual)
var actual2 MyType
err = sess.SQL().SelectFrom("my_types").Where("id = ?", id).One(&actual2)
s.NoError(err)
s.Equal(expected, actual2)
}
inserter := sess.SQL().InsertInto("my_types")
for i := range myTypeTests {
inserter = inserter.Values(myTypeTests[i])
}
_, err := inserter.Exec()
s.NoError(err)
err = sess.Collection("my_types").Truncate()
s.NoError(err)
batch := sess.SQL().InsertInto("my_types").Batch(50)
go func() {
defer batch.Done()
for i := range myTypeTests {
batch.Values(myTypeTests[i])
}
}()
err = batch.Wait()
s.NoError(err)
var values []MyType
err = sess.SQL().SelectFrom("my_types").All(&values)
s.NoError(err)
for i := range values {
expected := myTypeTests[i]
expected.ID = values[i].ID
s.Equal(expected, values[i])
}
}
}
func TestAdapter(t *testing.T) {
suite.Run(t, &AdapterTests{})
}