sqldb: add migration consistency test
Tests that all migration files follow the defined schema and that there are not duplicates which could cause collision.
This commit is contained in:
1 file changed
+97
@@ -616,3 +616,100 @@ func TestMigrationSucceedsAfterDirtyStateMigrationFailure19RC1(t *testing.T) {
|
|||||||
require.False(t, dirty)
|
require.False(t, dirty)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestMigrationConfigConsistency verifies that the migration configuration in
|
||||||
|
// migrationConfig is consistent with the actual SQL schema files embedded in
|
||||||
|
// the binary. This catches version collisions (e.g. two migrations claiming
|
||||||
|
// the same schema version) and missing schema files.
|
||||||
|
func TestMigrationConfigConsistency(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
migrations := GetMigrations()
|
||||||
|
require.NotEmpty(t, migrations)
|
||||||
|
|
||||||
|
// Build a set of schema versions that have actual .up.sql files in
|
||||||
|
// the embedded filesystem.
|
||||||
|
embeddedFiles, err := sqlSchemas.ReadDir("sqlc/migrations")
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
fileSchemaVersions := make(map[int]string)
|
||||||
|
for _, f := range embeddedFiles {
|
||||||
|
if f.IsDir() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
var version int
|
||||||
|
_, err := fmt.Sscanf(f.Name(), "%06d_", &version)
|
||||||
|
require.NoError(t, err, "schema migration file %q is "+
|
||||||
|
"missing a valid numeric prefix (expected "+
|
||||||
|
"format: 000XXX_name.up.sql)", f.Name())
|
||||||
|
|
||||||
|
// Enforce the 6-digit zero-padded naming convention
|
||||||
|
// for consistent directory listing order.
|
||||||
|
expectedPrefix := fmt.Sprintf("%06d_", version)
|
||||||
|
require.True(t,
|
||||||
|
len(f.Name()) > len(expectedPrefix) &&
|
||||||
|
f.Name()[:len(expectedPrefix)] == expectedPrefix,
|
||||||
|
"schema migration file %q should use 6-digit "+
|
||||||
|
"zero-padded prefix %q", f.Name(),
|
||||||
|
expectedPrefix)
|
||||||
|
|
||||||
|
// Verify no two files share the same numeric prefix.
|
||||||
|
if existing, ok := fileSchemaVersions[version]; ok {
|
||||||
|
t.Fatalf("duplicate schema file version %06d: "+
|
||||||
|
"%q and %q", version, existing, f.Name())
|
||||||
|
}
|
||||||
|
|
||||||
|
fileSchemaVersions[version] = f.Name()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Track seen versions to detect duplicates.
|
||||||
|
seenVersions := make(map[int]string)
|
||||||
|
seenSchemaVersions := make(map[int]string)
|
||||||
|
|
||||||
|
for _, 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",
|
||||||
|
m.Version, existing, m.Name)
|
||||||
|
}
|
||||||
|
seenVersions[m.Version] = m.Name
|
||||||
|
|
||||||
|
// 2. For schema migrations (those that advance the schema
|
||||||
|
// version), verify a corresponding .up.sql file exists
|
||||||
|
// 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
|
||||||
|
}
|
||||||
|
|
||||||
|
// A migration advances the schema if its SchemaVersion is
|
||||||
|
// higher than the previous migration's SchemaVersion.
|
||||||
|
if m.SchemaVersion > prevSchema {
|
||||||
|
_, 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)
|
||||||
|
|
||||||
|
if existing, ok := seenSchemaVersions[m.SchemaVersion]; ok {
|
||||||
|
t.Fatalf("duplicate schema version %d: "+
|
||||||
|
"%q and %q", m.SchemaVersion,
|
||||||
|
existing, m.Name)
|
||||||
|
}
|
||||||
|
seenSchemaVersions[m.SchemaVersion] = m.Name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. Verify versions are sequential starting from 1.
|
||||||
|
for i, m := range migrations {
|
||||||
|
require.Equal(t, i+1, m.Version,
|
||||||
|
"migration %q has version %d but expected %d "+
|
||||||
|
"(migrations must be sequential)",
|
||||||
|
m.Name, m.Version, i+1)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
Reference in new issue
Block a user