Skip to content

Commit

Permalink
feat: goner/xorm unit test
Browse files Browse the repository at this point in the history
  • Loading branch information
dapeng committed Nov 20, 2024
1 parent 72b1cd1 commit fec6a37
Show file tree
Hide file tree
Showing 5 changed files with 187 additions and 48 deletions.
6 changes: 3 additions & 3 deletions error.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
}

Expand Down
45 changes: 28 additions & 17 deletions goner/xorm/implement.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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()
}
Expand All @@ -73,31 +80,32 @@ 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)
}
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)
Expand All @@ -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()
}

Expand Down
33 changes: 29 additions & 4 deletions goner/xorm/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -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")
}

Expand All @@ -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))
Expand Down Expand Up @@ -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)
Expand All @@ -78,6 +102,7 @@ func (p *provider) Suck(conf string, v reflect.Value) gone.SuckError {
return engine.Stop(cemetery)
}
}(db))

p.engineMap[clusterName] = db
}

Expand All @@ -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{
Expand All @@ -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
}
Expand All @@ -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)
Expand Down
138 changes: 114 additions & 24 deletions goner/xorm/provider_test.go
Original file line number Diff line number Diff line change
@@ -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
})
}
13 changes: 13 additions & 0 deletions goner/xorm/testdata/config/default_test.properties
Original file line number Diff line number Diff line change
@@ -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

0 comments on commit fec6a37

Please sign in to comment.