From bd7e950c87d681cd72565e44a395834f5fbc99df Mon Sep 17 00:00:00 2001 From: ziggie Date: Thu, 9 Apr 2026 12:58:22 +0200 Subject: [PATCH] sqldb: harden migration config consistency tests Strengthen migration consistency coverage by checking the reverse mapping from embedded SQL files to migrationConfig entries, deriving previous schema state from slice order instead of Version, rejecting schema version regressions, and asserting migration names match the embedded SQL file stems. Also fix the graph v2 migration config name to match the embedded migration filename. --- sqldb/migrations.go | 2 +- sqldb/migrations_dev_test.go | 52 ++++++++++++++++++++++++++++++++++++ sqldb/migrations_test.go | 18 ++++++++++--- 3 files changed, 67 insertions(+), 5 deletions(-) create mode 100644 sqldb/migrations_dev_test.go diff --git a/sqldb/migrations.go b/sqldb/migrations.go index 9f9256da6..395bd669c 100644 --- a/sqldb/migrations.go +++ b/sqldb/migrations.go @@ -93,7 +93,7 @@ var ( // user if necessary. }, { - Name: "000009_graph_v2_columns", + Name: "000009_graph_v2", Version: 11, SchemaVersion: 9, }, diff --git a/sqldb/migrations_dev_test.go b/sqldb/migrations_dev_test.go new file mode 100644 index 000000000..43d1b5dcd --- /dev/null +++ b/sqldb/migrations_dev_test.go @@ -0,0 +1,52 @@ +//go:build test_db_postgres || test_db_sqlite || test_native_sql + +package sqldb + +import ( + "fmt" + "testing" + + "github.com/stretchr/testify/require" +) + +// TestMigrationFilesAllRegistered verifies that every .up.sql file in the +// embedded migrations filesystem has a corresponding entry in migrationConfig. +// This test requires dev build tags so that any future dev-only migrations +// added to migrationAdditions are visible — without them, such entries would +// be absent and their SQL files would trigger false failures. +func TestMigrationFilesAllRegistered(t *testing.T) { + t.Parallel() + + migrations := GetMigrations() + require.NotEmpty(t, migrations) + + // Collect all schema versions referenced by any entry in migrationConfig + // (including migrationAdditions, which is only populated under dev build + // tags). + registeredSchemaVersions := make(map[int]string) + for _, m := range migrations { + registeredSchemaVersions[m.SchemaVersion] = m.Name + } + + // Read all .up.sql files from the embedded filesystem. + embeddedFiles, err := sqlSchemas.ReadDir("sqlc/migrations") + require.NoError(t, err) + + for _, f := range embeddedFiles { + if f.IsDir() { + continue + } + + var schemaVersion int + _, err := fmt.Sscanf(f.Name(), "%06d_", &schemaVersion) + require.NoError(t, err, "migration file %q has no valid "+ + "numeric prefix", f.Name()) + + _, referenced := registeredSchemaVersions[schemaVersion] + require.True(t, referenced, + "SQL migration file %q (schema version %d) has no "+ + "corresponding entry in migrationConfig — add "+ + "an entry with SchemaVersion=%d", + f.Name(), schemaVersion, schemaVersion) + } +} diff --git a/sqldb/migrations_test.go b/sqldb/migrations_test.go index 399afb1ce..4e2b68e0f 100644 --- a/sqldb/migrations_test.go +++ b/sqldb/migrations_test.go @@ -4,6 +4,7 @@ import ( "database/sql" "fmt" "path/filepath" + "strings" "testing" "github.com/golang-migrate/migrate/v4" @@ -667,7 +668,7 @@ func TestMigrationConfigConsistency(t *testing.T) { seenVersions := make(map[int]string) seenSchemaVersions := make(map[int]string) - for _, m := range migrations { + for i, m := range migrations { // 1. Verify no duplicate global versions. if existing, ok := seenVersions[m.Version]; ok { t.Fatalf("duplicate global version %d: %q and %q", @@ -680,20 +681,29 @@ func TestMigrationConfigConsistency(t *testing.T) { // and no two config entries claim the same schema version // with different file prefixes. prevSchema := 0 - if m.Version > 1 { - prevSchema = migrations[m.Version-2].SchemaVersion + if i > 0 { + prevSchema = migrations[i-1].SchemaVersion } + require.GreaterOrEqual(t, m.SchemaVersion, prevSchema, + "migration %q regresses schema version from %d to %d", + m.Name, prevSchema, m.SchemaVersion) + // A migration advances the schema if its SchemaVersion is // higher than the previous migration's SchemaVersion. if m.SchemaVersion > prevSchema { - _, hasFile := fileSchemaVersions[m.SchemaVersion] + fileName, hasFile := fileSchemaVersions[m.SchemaVersion] require.True(t, hasFile, "migration %q (version %d) declares "+ "SchemaVersion=%d but no %06d_*.up.sql"+ " file exists in the embedded FS", m.Name, m.Version, m.SchemaVersion, m.SchemaVersion) + require.Equal(t, strings.TrimSuffix(fileName, ".up.sql"), + m.Name, "migration %q (version %d) has "+ + "SchemaVersion=%d but its name does not "+ + "match embedded file %q", + m.Name, m.Version, m.SchemaVersion, fileName) if existing, ok := seenSchemaVersions[m.SchemaVersion]; ok { t.Fatalf("duplicate schema version %d: "+