mirror of
https://github.com/Sagit-chu/flvx.git
synced 2026-10-13 12:46:36 +08:00
fix(backend): harden postgres id default repair and CI coverage
Run Postgres id-sequence repair on every startup migration and add contract coverage plus a GitHub Actions Postgres job to catch schema-drift regressions before release.
This commit is contained in:
@@ -1307,6 +1307,8 @@ func bootstrapSchema(db *store.DB, schemaSQL, seedSQL string) error {
|
||||
|
||||
const currentSchemaVersion = 2
|
||||
|
||||
var ensurePostgresIDDefaultsFn = ensurePostgresIDDefaults
|
||||
|
||||
func getSchemaVersion(db *store.DB) int {
|
||||
_, _ = db.Exec(`CREATE TABLE IF NOT EXISTS schema_version (version INTEGER NOT NULL DEFAULT 0)`)
|
||||
var v int
|
||||
@@ -1327,6 +1329,11 @@ func migrateSchema(db *store.DB) error {
|
||||
}
|
||||
|
||||
ver := getSchemaVersion(db)
|
||||
if db.Dialect() == store.DialectPostgres {
|
||||
if err := ensurePostgresIDDefaultsFn(db); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if ver >= currentSchemaVersion {
|
||||
return nil
|
||||
}
|
||||
@@ -1396,11 +1403,6 @@ func migrateSchema(db *store.DB) error {
|
||||
return err
|
||||
}
|
||||
|
||||
if db.Dialect() == store.DialectPostgres {
|
||||
if err := ensurePostgresIDDefaults(db); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
setSchemaVersion(db, currentSchemaVersion)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
package sqlite
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"errors"
|
||||
"testing"
|
||||
|
||||
"go-backend/internal/store"
|
||||
|
||||
_ "modernc.org/sqlite"
|
||||
)
|
||||
|
||||
func TestMigrateSchemaRunsPostgresIDRepairEvenAtCurrentVersion(t *testing.T) {
|
||||
raw, err := sql.Open("sqlite", ":memory:")
|
||||
if err != nil {
|
||||
t.Fatalf("open sqlite: %v", err)
|
||||
}
|
||||
t.Cleanup(func() {
|
||||
_ = raw.Close()
|
||||
})
|
||||
|
||||
db := store.Wrap(raw, store.DialectPostgres)
|
||||
if _, err := db.Exec(`CREATE TABLE schema_version (version INTEGER NOT NULL DEFAULT 0)`); err != nil {
|
||||
t.Fatalf("create schema_version: %v", err)
|
||||
}
|
||||
if _, err := db.Exec(`INSERT INTO schema_version(version) VALUES(?)`, currentSchemaVersion); err != nil {
|
||||
t.Fatalf("seed schema_version: %v", err)
|
||||
}
|
||||
|
||||
called := 0
|
||||
original := ensurePostgresIDDefaultsFn
|
||||
ensurePostgresIDDefaultsFn = func(db *store.DB) error {
|
||||
called++
|
||||
return nil
|
||||
}
|
||||
t.Cleanup(func() {
|
||||
ensurePostgresIDDefaultsFn = original
|
||||
})
|
||||
|
||||
if err := migrateSchema(db); err != nil {
|
||||
t.Fatalf("migrateSchema: %v", err)
|
||||
}
|
||||
if called != 1 {
|
||||
t.Fatalf("expected postgres id repair to run once, got %d", called)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMigrateSchemaReturnsPostgresIDRepairError(t *testing.T) {
|
||||
raw, err := sql.Open("sqlite", ":memory:")
|
||||
if err != nil {
|
||||
t.Fatalf("open sqlite: %v", err)
|
||||
}
|
||||
t.Cleanup(func() {
|
||||
_ = raw.Close()
|
||||
})
|
||||
|
||||
db := store.Wrap(raw, store.DialectPostgres)
|
||||
if _, err := db.Exec(`CREATE TABLE schema_version (version INTEGER NOT NULL DEFAULT 0)`); err != nil {
|
||||
t.Fatalf("create schema_version: %v", err)
|
||||
}
|
||||
if _, err := db.Exec(`INSERT INTO schema_version(version) VALUES(?)`, currentSchemaVersion); err != nil {
|
||||
t.Fatalf("seed schema_version: %v", err)
|
||||
}
|
||||
|
||||
wantErr := errors.New("repair failed")
|
||||
original := ensurePostgresIDDefaultsFn
|
||||
ensurePostgresIDDefaultsFn = func(db *store.DB) error {
|
||||
return wantErr
|
||||
}
|
||||
t.Cleanup(func() {
|
||||
ensurePostgresIDDefaultsFn = original
|
||||
})
|
||||
|
||||
err = migrateSchema(db)
|
||||
if !errors.Is(err, wantErr) {
|
||||
t.Fatalf("expected error %v, got %v", wantErr, err)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user