fix: harden module lifecycle and debug log setup
Export CurrentDialect(), return setup errors when debug logger is not registered, and guard DestName logging when the module is uninitialized. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -20,14 +20,14 @@ func DestName(destTypeStruct any, path ...string) string {
|
|||||||
|
|
||||||
for i, p := range path {
|
for i, p := range path {
|
||||||
if v.Kind() != reflect.Struct {
|
if v.Kind() != reflect.Struct {
|
||||||
dbxshared.DBModule.Logger().Error("DestName: path parent is not a struct", "path", destIdent+"."+strings.Join(path[:i+1], "."))
|
destNameLogError("DestName: path parent is not a struct", destIdent+"."+strings.Join(path[:i+1], "."))
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
v = v.FieldByName(p)
|
v = v.FieldByName(p)
|
||||||
|
|
||||||
if !v.IsValid() {
|
if !v.IsValid() {
|
||||||
dbxshared.DBModule.Logger().Error("DestName: field does not exist", "path", destIdent+"."+strings.Join(path[:i+1], "."))
|
destNameLogError("DestName: field does not exist", destIdent+"."+strings.Join(path[:i+1], "."))
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -36,3 +36,10 @@ func DestName(destTypeStruct any, path ...string) string {
|
|||||||
|
|
||||||
return destIdent
|
return destIdent
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func destNameLogError(msg, path string) {
|
||||||
|
if dbxshared.DBModule == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
dbxshared.DBModule.Logger().Error(msg, "path", path)
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
package dbxshared
|
package dbxshared
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
type Logger interface {
|
type Logger interface {
|
||||||
InitLogger()
|
InitLogger()
|
||||||
}
|
}
|
||||||
@@ -20,11 +22,17 @@ func RegisterLogger(dialect dialectString, logger Logger) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// InitLogger initializes the logger for a specific dialect.
|
// InitLogger initializes the logger for a specific dialect.
|
||||||
func InitLogger(dialect dialectString) {
|
func InitLogger(dialect dialectString) error {
|
||||||
dialectStr := dialect.String()
|
dialectStr := dialect.String()
|
||||||
logger, exists := loggerRegistry[dialectStr]
|
logger, exists := loggerRegistry[dialectStr]
|
||||||
if !exists {
|
if !exists {
|
||||||
panic("No logger registered for dialect: " + dialectStr)
|
return fmt.Errorf("no logger registered for dialect %q: blank-import dbxm (MySQL) or dbxp (Postgres)", dialectStr)
|
||||||
}
|
}
|
||||||
logger.InitLogger()
|
logger.InitLogger()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ResetLoggerRegistry clears registered loggers. It is intended for tests only.
|
||||||
|
func ResetLoggerRegistry() {
|
||||||
|
loggerRegistry = make(map[string]Logger)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,32 @@
|
|||||||
|
package dbxshared
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
type testDialect string
|
||||||
|
|
||||||
|
func (d testDialect) String() string { return string(d) }
|
||||||
|
|
||||||
|
type stubLogger struct{}
|
||||||
|
|
||||||
|
func (stubLogger) InitLogger() {}
|
||||||
|
|
||||||
|
func TestInitLogger_UnregisteredDialect(t *testing.T) {
|
||||||
|
ResetLoggerRegistry()
|
||||||
|
t.Cleanup(ResetLoggerRegistry)
|
||||||
|
|
||||||
|
err := InitLogger(testDialect("mysql"))
|
||||||
|
assert.ErrorContains(t, err, "blank-import dbxm")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestInitLogger_RegisteredDialect(t *testing.T) {
|
||||||
|
ResetLoggerRegistry()
|
||||||
|
t.Cleanup(ResetLoggerRegistry)
|
||||||
|
|
||||||
|
RegisterLogger(testDialect("mysql"), stubLogger{})
|
||||||
|
err := InitLogger(testDialect("mysql"))
|
||||||
|
assert.NoError(t, err)
|
||||||
|
}
|
||||||
@@ -45,6 +45,11 @@ var (
|
|||||||
state = dbState{}
|
state = dbState{}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// CurrentDialect returns the SQL dialect configured by [ModuleDB].
|
||||||
|
func CurrentDialect() Dialect {
|
||||||
|
return state.dialect
|
||||||
|
}
|
||||||
|
|
||||||
// SQLO returns the current SQL database handle.
|
// SQLO returns the current SQL database handle.
|
||||||
func SQLO() *sql.DB {
|
func SQLO() *sql.DB {
|
||||||
dbxshared.DBModule.RequireLoaded("dbx.SQLO requires database module")
|
dbxshared.DBModule.RequireLoaded("dbx.SQLO requires database module")
|
||||||
@@ -118,7 +123,10 @@ func setupDB(m *app.Module) error {
|
|||||||
)
|
)
|
||||||
|
|
||||||
if state.debugLog {
|
if state.debugLog {
|
||||||
dbxshared.InitLogger(state.dialect)
|
if err := dbxshared.InitLogger(state.dialect); err != nil {
|
||||||
|
m.Logger().Error("Couldn't initialize Jet query debug logger", "err", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
@@ -0,0 +1,45 @@
|
|||||||
|
package dbx
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"gitea.auvem.com/go-toolkit/dbx/internal/dbxshared"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestDestName_MissingFieldWithoutModule(t *testing.T) {
|
||||||
|
type Foo struct {
|
||||||
|
Bar int
|
||||||
|
}
|
||||||
|
|
||||||
|
name := DestName(Foo{}, "Missing")
|
||||||
|
assert.Empty(t, name)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDestName_ValidPath(t *testing.T) {
|
||||||
|
type Inner struct {
|
||||||
|
Value string
|
||||||
|
}
|
||||||
|
type Foo struct {
|
||||||
|
Inner Inner
|
||||||
|
}
|
||||||
|
|
||||||
|
name := DestName(Foo{}, "Inner", "Value")
|
||||||
|
assert.Equal(t, "Foo.Inner.Value", name)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDialect_AfterModuleDB(t *testing.T) {
|
||||||
|
if dbxshared.DBModule != nil {
|
||||||
|
t.Skip("database module already initialized")
|
||||||
|
}
|
||||||
|
|
||||||
|
_ = ModuleDB(DialectPostgres, &DBConfig{
|
||||||
|
User: "user",
|
||||||
|
Password: "pass",
|
||||||
|
URI: "localhost:5432",
|
||||||
|
Name: "test",
|
||||||
|
MaxConn: 1,
|
||||||
|
}, false)
|
||||||
|
|
||||||
|
assert.Equal(t, DialectPostgres, CurrentDialect())
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user