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: "+