mirror of
https://github.com/go-admin-team/go-admin.git
synced 2026-09-21 02:04:09 +00:00
sort is gorm:"size:4", which MySQL builds as a tinyint holding -128..127. The demo menu seeded Sort: 900, so on MySQL the run stopped at 1786700001000 with Error 1264, and every migration after it - including the soft-delete conversion - never ran. deleted_at therefore stayed NULL while the code queries deleted_at = 0, and the login returned 'incorrect Username or Password' on a database whose password hash was correct all along. sqlite ignores the declared width, so a fresh install there passed and the fault only appeared on MySQL.
83 lines
2.0 KiB
Go
83 lines
2.0 KiB
Go
package version
|
|
|
|
import (
|
|
"go/ast"
|
|
"go/parser"
|
|
"go/token"
|
|
"math"
|
|
"os"
|
|
"path/filepath"
|
|
"strconv"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
// Fields tagged gorm:"size:4" become a tinyint on MySQL, which holds -128..127.
|
|
// sqlite ignores the width, so a value that overflows passes every local test
|
|
// and fails on a real install - and because the migration is not transactional,
|
|
// it fails partway, leaving later migrations unapplied.
|
|
//
|
|
// That is what happened: a seeded menu with Sort: 900 stopped the run at
|
|
// 1786700001000, so the soft-delete conversion never ran, deleted_at stayed
|
|
// NULL, and nobody could log in.
|
|
var narrowColumns = map[string]struct{ min, max int64 }{
|
|
"Sort": {math.MinInt8, math.MaxInt8},
|
|
"Status": {math.MinInt8, math.MaxInt8},
|
|
}
|
|
|
|
func TestSeededValuesFitTheirColumns(t *testing.T) {
|
|
dir, err := os.Getwd()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
files, err := filepath.Glob(filepath.Join(dir, "*.go"))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
checked := 0
|
|
for _, path := range files {
|
|
if strings.HasSuffix(path, "_test.go") {
|
|
continue
|
|
}
|
|
fset := token.NewFileSet()
|
|
f, err := parser.ParseFile(fset, path, nil, 0)
|
|
if err != nil {
|
|
t.Fatalf("parse %s: %v", path, err)
|
|
}
|
|
ast.Inspect(f, func(n ast.Node) bool {
|
|
kv, ok := n.(*ast.KeyValueExpr)
|
|
if !ok {
|
|
return true
|
|
}
|
|
key, ok := kv.Key.(*ast.Ident)
|
|
if !ok {
|
|
return true
|
|
}
|
|
limits, watched := narrowColumns[key.Name]
|
|
if !watched {
|
|
return true
|
|
}
|
|
lit, ok := kv.Value.(*ast.BasicLit)
|
|
if !ok || lit.Kind != token.INT {
|
|
return true
|
|
}
|
|
v, err := strconv.ParseInt(lit.Value, 10, 64)
|
|
if err != nil {
|
|
return true
|
|
}
|
|
checked++
|
|
if v < limits.min || v > limits.max {
|
|
t.Errorf("%s:%d: %s: %d does not fit a tinyint (%d..%d);\n"+
|
|
" MySQL rejects it with Error 1264 and the migration stops there",
|
|
filepath.Base(path), fset.Position(lit.Pos()).Line, key.Name, v, limits.min, limits.max)
|
|
}
|
|
return true
|
|
})
|
|
}
|
|
|
|
if checked == 0 {
|
|
t.Fatal("no seeded values were examined; the scan is broken, not the code")
|
|
}
|
|
}
|