From c53c4af8bd32a302a889862326d93b16ee977837 Mon Sep 17 00:00:00 2001 From: neversettle <41864816+neversettle17-101@users.noreply.github.com> Date: Sat, 20 Jun 2026 12:49:59 +0530 Subject: [PATCH] test(storage): guard against duplicate goose migration version prefixes (#336) * test(storage): guard against duplicate goose migration version prefixes Statically scans embedded migration filenames for repeated numeric version prefixes and fails with a clear message, catching the class of bug from #333 (two PRs adding 0014_*.sql independently) before goose.Up() would panic at runtime. Co-Authored-By: Claude Sonnet 4.6 * fix(storage): dedupe migration versions by goose's parsed int64, not raw string versionPrefix compared raw filename prefixes, so 014_x.sql and 0014_y.sql were treated as distinct even though goose.NumericComponent parses both to version 14 and panics on the collision. Use goose.NumericComponent directly so the test enforces goose's actual collision rule. Co-Authored-By: Claude Sonnet 4.6 * test(storage): trim PR-specific framing from migration version test comment Co-Authored-By: Claude Sonnet 4.6 --------- Co-authored-by: Claude Sonnet 4.6 --- .../sqlite/migrate_unique_version_test.go | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 backend/internal/storage/sqlite/migrate_unique_version_test.go diff --git a/backend/internal/storage/sqlite/migrate_unique_version_test.go b/backend/internal/storage/sqlite/migrate_unique_version_test.go new file mode 100644 index 000000000..a46fa8192 --- /dev/null +++ b/backend/internal/storage/sqlite/migrate_unique_version_test.go @@ -0,0 +1,39 @@ +package sqlite + +import ( + "testing" + + "github.com/pressly/goose/v3" +) + +// TestMigrationVersionsAreUnique scans the embedded migration filenames and +// parses each version with goose.NumericComponent — the same function goose +// itself uses — so prefixes that parse to the same int64 (e.g. "014" vs +// "0014") are caught as a collision, not just identical strings. Catches the +// conflict with a clear message instead of a goose panic at runtime. +func TestMigrationVersionsAreUnique(t *testing.T) { + entries, err := migrationsFS.ReadDir("migrations") + if err != nil { + t.Fatalf("read migrations dir: %v", err) + } + + seen := map[int64]string{} // parsed version -> filename + for _, e := range entries { + name := e.Name() + if e.IsDir() { + continue + } + + version, err := goose.NumericComponent(name) + if err != nil { + t.Errorf("migration %q has no version goose can parse: %v", name, err) + continue + } + + if other, dup := seen[version]; dup { + t.Errorf("duplicate migration version %d: %s vs %s", version, other, name) + continue + } + seen[version] = name + } +}