feat: goner/xorm unit test

This commit is contained in:
dapeng 2024-11-20 20:55:58 +08:00
parent 72b1cd18bd
commit fec6a3787b
5 changed files with 187 additions and 48 deletions

View File

@ -90,11 +90,11 @@ func ToError(input any) Error {
case Error:
return input.(Error)
case error:
return NewInnerError(input.(error).Error(), http.StatusInternalServerError)
return NewInnerErrorSkip(input.(error).Error(), http.StatusInternalServerError, 2)
case string:
return NewInnerError(input.(string), http.StatusInternalServerError)
return NewInnerErrorSkip(input.(string), http.StatusInternalServerError, 2)
default:
return NewInnerError(fmt.Sprintf("%v", input), http.StatusInternalServerError)
return NewInnerErrorSkip(fmt.Sprintf("%v", input), http.StatusInternalServerError, 2)
}
}

View File

@ -27,32 +27,36 @@ func newSession(eng xorm.EngineInterface) XInterface {
}
type ClusterNodeConf struct {
DriverName string `properties:"driver-name" mapstructure:"driver-name"`
DSN string `properties:"dsn" mapstructure:"dsn"`
DriverName string `properties:"driver-name,default=driver" mapstructure:"driver-name"`
DSN string `properties:"dsn,default=dsn" mapstructure:"dsn"`
}
type Conf struct {
DriverName string `properties:"driver-name" mapstructure:"driver-name"`
Dsn string `properties:"dsn" mapstructure:"dsn"`
MaxIdleCount int `properties:"max-idle-count" mapstructure:"max-idle-count"`
MaxOpen int `properties:"max-open" mapstructure:"max-open"`
MaxLifetime time.Duration `properties:"max-lifetime" mapstructure:"max-lifetime"`
ShowSql bool `properties:"show-sql" mapstructure:"show-sql"`
EnableCluster bool `properties:"cluster.enable" mapstructure:"cluster.enable"`
MasterConf *ClusterNodeConf `properties:"cluster.master" mapstructure:"cluster.master"`
SlavesConf []*ClusterNodeConf `properties:"cluster.slaves" mapstructure:"cluster.slaves"`
DriverName string `properties:"driver-name,default=driver" mapstructure:"driver-name"`
Dsn string `properties:"dsn,default=dsn" mapstructure:"dsn"`
MaxIdleCount int `properties:"max-idle-count,default=5" mapstructure:"max-idle-count"`
MaxOpen int `properties:"max-open,default=20" mapstructure:"max-open"`
MaxLifetime time.Duration `properties:"max-lifetime,default=10m" mapstructure:"max-lifetime"`
ShowSql bool `properties:"show-sql,default=true" mapstructure:"show-sql"`
EnableCluster bool `properties:"cluster.enable,default=false" mapstructure:"cluster.enable"`
}
//go:generate mockgen -package xorm -destination=./engine_mock_test.go xorm.io/xorm EngineInterface
type wrappedEngine struct {
gone.Flag
xorm.EngineInterface
group *xorm.EngineGroup
newFunc func(driverName string, dataSourceName string) (xorm.EngineInterface, error)
newSession func(xorm.EngineInterface) XInterface
log gone.Logger `gone:"gone-logger"`
conf *Conf `gone:"config,database"`
masterConf *ClusterNodeConf `gone:"config,database.cluster.master"`
slavesConf []*ClusterNodeConf `gone:"config,database.cluster.slaves"`
unitTest bool
}
func (e *wrappedEngine) GetOriginEngine() xorm.EngineInterface {
@ -64,6 +68,9 @@ func (e *wrappedEngine) Start(gone.Cemetery) error {
if err != nil {
return err
}
if e.unitTest {
return nil
}
e.config()
return e.Ping()
}
@ -73,20 +80,20 @@ func (e *wrappedEngine) create() error {
}
if e.conf.EnableCluster {
if e.conf.MasterConf == nil {
if e.masterConf == nil {
return gone.NewInnerError("master config(database.cluster.master) is nil", gone.StartError)
}
if len(e.conf.SlavesConf) == 0 {
if len(e.slavesConf) == 0 {
return gone.NewInnerError("slaves config(database.cluster.slaves) is nil", gone.StartError)
}
master, err := e.newFunc(e.conf.MasterConf.DriverName, e.conf.MasterConf.DSN)
master, err := e.newFunc(e.masterConf.DriverName, e.masterConf.DSN)
if err != nil {
return gone.NewInnerError(err.Error(), gone.StartError)
}
slaves := make([]*xorm.Engine, 0, len(e.conf.SlavesConf))
for _, slave := range e.conf.SlavesConf {
slaves := make([]*xorm.Engine, 0, len(e.slavesConf))
for _, slave := range e.slavesConf {
slaveEngine, err := e.newFunc(slave.DriverName, slave.DSN)
if err != nil {
return gone.NewInnerError(err.Error(), gone.StartError)
@ -94,10 +101,11 @@ func (e *wrappedEngine) create() error {
slaves = append(slaves, slaveEngine.(*xorm.Engine))
}
e.EngineInterface, err = xorm.NewEngineGroup(master, slaves)
e.group, err = xorm.NewEngineGroup(master, slaves)
if err != nil {
return gone.NewInnerError(err.Error(), gone.StartError)
}
e.EngineInterface = e.group
} else {
var err error
e.EngineInterface, err = e.newFunc(e.conf.DriverName, e.conf.Dsn)
@ -116,6 +124,9 @@ func (e *wrappedEngine) config() {
}
func (e *wrappedEngine) Stop(gone.Cemetery) error {
if e.unitTest {
return nil
}
return e.EngineInterface.(io.Closer).Close()
}

View File

@ -19,6 +19,8 @@ func NewProvider(engine *wrappedEngine) (gone.Vampire, gone.GonerOption) {
return &provider{
engineMap: engineMap,
newFunc: engine.newFunc,
unitTest: engine.unitTest,
}, gone.GonerId("xorm")
}
@ -30,6 +32,9 @@ type provider struct {
cemetery gone.Cemetery `gone:"*"`
configure gone.Configure `gone:"*"`
log gone.Logger `gone:"*"`
newFunc func(driverName string, dataSourceName string) (xorm.EngineInterface, error)
unitTest bool
}
var xormInterface = gone.GetInterfaceType(new(gone.XormEngine))
@ -66,8 +71,27 @@ func (p *provider) Suck(conf string, v reflect.Value) gone.SuckError {
return gone.NewInnerError("failed to get config for cluster: "+clusterName, gone.InjectError)
}
var masterConf ClusterNodeConf
err = p.configure.Get(clusterName+".cluster.master", &masterConf, "")
if err != nil {
return gone.NewInnerError("failed to get master config for cluster: "+clusterName, gone.InjectError)
}
var slavesConf []*ClusterNodeConf
err = p.configure.Get(clusterName+".cluster.slaves", &slavesConf, "")
if err != nil {
return gone.NewInnerError("failed to get slaves config for cluster: "+clusterName, gone.InjectError)
}
db = newWrappedEngine()
db.conf = &config
db.masterConf = &masterConf
db.slavesConf = slavesConf
//for test
db.newFunc = p.newFunc
db.unitTest = p.unitTest
err = db.Start(p.cemetery)
if err != nil {
return gone.NewInnerError("failed to start xorm engine for cluster: "+clusterName, gone.InjectError)
@ -78,6 +102,7 @@ func (p *provider) Suck(conf string, v reflect.Value) gone.SuckError {
return engine.Stop(cemetery)
}
}(db))
p.engineMap[clusterName] = db
}
@ -86,7 +111,7 @@ func (p *provider) Suck(conf string, v reflect.Value) gone.SuckError {
return gone.NewInnerError(fmt.Sprintf("database(name=%s) is not enable cluster, cannot inject []gone.XormEngine", clusterName), gone.InjectError)
}
engines := db.EngineInterface.(*xorm.EngineGroup).Slaves()
engines := db.group.Slaves()
xormEngines := make([]gone.XormEngine, 0, len(engines))
for _, eng := range engines {
xormEngines = append(xormEngines, &wrappedEngine{
@ -104,7 +129,7 @@ func (p *provider) Suck(conf string, v reflect.Value) gone.SuckError {
}
v.Set(reflect.ValueOf(&wrappedEngine{
EngineInterface: db.EngineInterface.(*xorm.EngineGroup).Master(),
EngineInterface: db.group.Master(),
}))
return nil
}
@ -114,10 +139,10 @@ func (p *provider) Suck(conf string, v reflect.Value) gone.SuckError {
return gone.NewInnerError(fmt.Sprintf("database(name=%s) is not enable cluster, cannot inject slave into gone.XormEngine", clusterName), gone.InjectError)
}
slaves := db.EngineInterface.(*xorm.EngineGroup).Slaves()
slaves := db.group.Slaves()
var index int64
var err error
if slaveIndex == "" {
if slaveIndex != "" {
index, err = strconv.ParseInt(slaveIndex, 10, 64)
if err != nil || index < 0 || index >= int64(len(slaves)) {
return gone.NewInnerError(fmt.Sprintf("invalid slave index: %s, must be greater than or equal to 0 and less than %d ", slaveIndex, len(slaves)), gone.InjectError)

View File

@ -1,56 +1,146 @@
package xorm
import (
"fmt"
"github.com/gone-io/gone"
"github.com/gone-io/gone/goner/config"
"github.com/stretchr/testify/assert"
"go.uber.org/mock/gomock"
"reflect"
"testing"
"xorm.io/xorm"
)
func Test_provider_Suck(t *testing.T) {
controller := gomock.NewController(t)
engineInterface := NewMockEngineInterface(controller)
var defaultDb gone.XormEngine
gone.RunTest(func() {
println("ok")
enginesMap := make(map[string]*xorm.Engine)
for i := 0; i < 10; i++ {
enginesMap[fmt.Sprintf("db%d", i)] = &xorm.Engine{}
}
gone.RunTest(func(i struct {
p *provider `gone:"*"`
}) {
t.Run("get default gone.XormEngine", func(t *testing.T) {
var X gone.XormEngine
err := i.p.Suck("", reflect.ValueOf(&X).Elem())
assert.Nil(t, err)
assert.Equal(t, X, defaultDb)
})
t.Run("get default master gone.XormEngine", func(t *testing.T) {
var X gone.XormEngine
err := i.p.Suck("master", reflect.ValueOf(&X).Elem())
assert.Nil(t, err)
assert.Equal(t, X.(*wrappedEngine).EngineInterface, enginesMap["db0"])
})
t.Run("get default slave gone.XormEngine", func(t *testing.T) {
var X gone.XormEngine
err := i.p.Suck("slave", reflect.ValueOf(&X).Elem())
assert.Nil(t, err)
assert.Equal(t, X.(*wrappedEngine).EngineInterface, enginesMap["db1"])
err = i.p.Suck("slave=1", reflect.ValueOf(&X).Elem())
assert.Nil(t, err)
assert.Equal(t, X.(*wrappedEngine).EngineInterface, enginesMap["db2"])
err = i.p.Suck("slave=0", reflect.ValueOf(&X).Elem())
assert.Nil(t, err)
assert.Equal(t, X.(*wrappedEngine).EngineInterface, enginesMap["db1"])
})
t.Run("get default slave gone.XormEngine with error index", func(t *testing.T) {
var X gone.XormEngine
err := i.p.Suck("slave=-1", reflect.ValueOf(&X).Elem())
assert.Error(t, err)
err = i.p.Suck("slave=2", reflect.ValueOf(&X).Elem())
assert.Error(t, err)
})
t.Run("get default slaves with []gone.XormEngine", func(t *testing.T) {
var X []gone.XormEngine
err := i.p.Suck("", reflect.ValueOf(&X).Elem())
assert.Nil(t, err)
assert.Equal(t, len(X), 2)
assert.Equal(t, X[0].(*wrappedEngine).EngineInterface, enginesMap["db1"])
assert.Equal(t, X[1].(*wrappedEngine).EngineInterface, enginesMap["db2"])
})
t.Run("get user.database gone.XormEngine", func(t *testing.T) {
var X gone.XormEngine
err := i.p.Suck("db=user.database", reflect.ValueOf(&X).Elem())
assert.Nil(t, err)
err = i.p.Suck("db=user.database,master", reflect.ValueOf(&X).Elem())
assert.Nil(t, err)
assert.Equal(t, X.(*wrappedEngine).EngineInterface, enginesMap["db3"])
})
t.Run("get user.database slave gone.XormEngine", func(t *testing.T) {
var X gone.XormEngine
err := i.p.Suck("db=user.database,slave=1", reflect.ValueOf(&X).Elem())
assert.Nil(t, err)
assert.Equal(t, X.(*wrappedEngine).EngineInterface, enginesMap["db5"])
err = i.p.Suck("db=user.database,slave=2", reflect.ValueOf(&X).Elem())
assert.Error(t, err)
})
t.Run("get user.database slave with []gone.XormEngine", func(t *testing.T) {
var X []gone.XormEngine
err := i.p.Suck("db=user.database", reflect.ValueOf(&X).Elem())
assert.Nil(t, err)
assert.Equal(t, len(X), 2)
assert.Equal(t, X[0].(*wrappedEngine).EngineInterface, enginesMap["db4"])
assert.Equal(t, X[1].(*wrappedEngine).EngineInterface, enginesMap["db5"])
})
}, func(cemetery gone.Cemetery) error {
e := wrappedEngine{
//Logger: in.logger,
newFunc: func(driverName string, dataSourceName string) (xorm.EngineInterface, error) {
engineInterface := NewMockEngineInterface(controller)
engineInterface.EXPECT().Ping().Return(nil).AnyTimes()
newFunc := func(driverName string, dataSourceName string) (xorm.EngineInterface, error) {
return enginesMap[dataSourceName], nil
}
return engineInterface, nil
},
e := wrappedEngine{
newFunc: newFunc,
newSession: func(engineInterface xorm.EngineInterface) XInterface {
session := NewMockXInterface(controller)
//session.EXPECT().Begin().Return(nil)
//session.EXPECT().Close().Return(nil)
//session.EXPECT().Rollback().Return(errors.New("error"))
return session
},
conf: &Conf{
EnableCluster: true,
MasterConf: &ClusterNodeConf{},
SlavesConf: []*ClusterNodeConf{
{
DriverName: "mysql",
DSN: "root:123456@tcp(127.0.0.1:3306)/test?charset=utf8mb4&parseTime=True&loc=Local",
},
{
DriverName: "mysql",
DSN: "root:123456@tcp(127.0.0.1:3306)/test?charset=utf8mb4&parseTime=True&loc=Local",
},
},
masterConf: &ClusterNodeConf{
DriverName: "mysql",
DSN: "db0",
},
slavesConf: []*ClusterNodeConf{
{
DriverName: "mysql",
DSN: "db1",
},
{
DriverName: "mysql",
DSN: "db2",
},
},
}
err := e.Start(cemetery)
err := e.create()
if err != nil {
return err
}
e.EngineInterface = engineInterface
e.unitTest = true
defaultDb = &e
cemetery.Bury(NewProvider(&e))
_ = config.Priest(cemetery)
return nil
})
}

View File

@ -0,0 +1,13 @@
user.database.cluster.enable=true
user.database.cluster.master.driver-name=mysql
user.database.cluster.master.dsn=db3
user.database.ping-before-start=false
user.database.cluster.slaves[0].driver-name=mysql
user.database.cluster.slaves[0].dsn=db4
user.database.cluster.slaves[1].driver-name=postgresql
user.database.cluster.slaves[1].dsn=db5