fix(#48): give SYBSession an explicit singular table name

Every other GoAuto model declares TableName(); SYBSession did not, so
gorm silently created syb_sessions while the rest of the schema is
singular. Migration succeeds either way, which is what makes it easy to
miss — it only surfaces when someone queries the conventional name.

Caught by running a real import, before the MySQL migration had been run
anywhere.

Migrate now takes its model list from MigratedModels(), so the new
convention test asserts over exactly the set that Migrate creates instead
of a hand-copied list that would drift. Mutation-checked: dropping the
TableName method again makes the test fail.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
QiuSW
2026-08-20 09:00:58 +08:00
co-authored by Claude Opus 5
parent 7edb4e7b9f
commit 4f7f3a612a
3 changed files with 29 additions and 3 deletions
+10 -3
View File
@@ -8,8 +8,11 @@ import (
// Migrate creates the complete GoAuto MVP schema. AutoMigrate is intentionally
// kept behind this package so the CLI migration and tests use the same model set.
func Migrate(db *gorm.DB) error {
return db.AutoMigrate(
// MigratedModels is the single source of truth for which models make up the
// schema, so tests can assert repo-wide conventions over exactly the set that
// Migrate creates rather than a hand-maintained copy that drifts.
func MigratedModels() []any {
return []any{
&models.AgentDevice{},
&models.PDDProduct{},
&models.ShopeeProduct{},
@@ -22,5 +25,9 @@ func Migrate(db *gorm.DB) error {
&models.CollectionColorPrice{},
&models.CollectionSKU{},
&models.CollectionSKUValue{},
)
}
}
func Migrate(db *gorm.DB) error {
return db.AutoMigrate(MigratedModels()...)
}
@@ -359,3 +359,20 @@ func TestMySQLTextJSONBlobColumnsHaveNoLiteralDefault(t *testing.T) {
}
}
}
// 仓库约定:所有 GoAuto 模型显式声明单数表名。漏写 TableName() 时 gorm 会
// 悄悄用复数(syb_session -> syb_sessions),迁移照样成功,直到有人按约定的
// 名字写查询才会发现。这里对迁移清单里的每个模型逐一核对。
func TestEveryModelDeclaresSingularTableName(t *testing.T) {
type tableNamer interface{ TableName() string }
for _, model := range migrations.MigratedModels() {
namer, ok := model.(tableNamer)
if !ok {
t.Fatalf("%T 没有声明 TableName(),gorm 会用复数表名", model)
}
name := namer.TableName()
if strings.HasSuffix(name, "s") && !strings.HasSuffix(name, "ss") {
t.Fatalf("%T 的表名 %q 像复数,仓库约定用单数", model, name)
}
}
}
+2
View File
@@ -380,6 +380,8 @@ type SYBProduct struct {
func (SYBProduct) TableName() string { return "syb_product" }
func (SYBSession) TableName() string { return "syb_session" }
func (product *SYBProduct) BeforeCreate(_ *gorm.DB) error {
if product.RawJSON == "" {
product.RawJSON = "{}"