Move schema_migrations table creation into 000.sql with INTEGER version column #58
Reference in New Issue
Block a user
Delete Branch "feat/migration-bootstrap-000sql"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Closes #57
Adopts the pixa migration pattern for schema management. Replaces the monolithic
schema.sqlembed with a numbered migration system.Changes
New:
schema/000.sql— Bootstrap migrationschema_migrationstable withINTEGER PRIMARY KEYversion columnCREATE TABLE IF NOT EXISTSandINSERT OR IGNOREfor version 0Renamed:
schema.sql→schema/001.sql— Initial schema migrationRemoved:
schema/008_uploads.sqlRewritten:
database.go— Migration engine//go:embed schema/*.sqlreplaces//go:embed schema.sqlbootstrapMigrationsTable(): checks ifschema_migrationstable exists, applies 000.sql if missingapplyMigrations(): iterates through numbered .sql files, checksschema_migrationsfor each version, applies and records pending onescollectMigrations(): reads embedded schema dir, returns sorted filenamesParseMigrationVersion(): extracts numeric version from filenames like001.sqlor001_description.sql(exported for testing)createSchema()removed entirelyUpdated:
database_test.goschema_migrationstable exists alongside other core tablesVerification
docker build .passes — formatting, linting, all tests green.Review: FAIL — Missing test coverage for migration engine
What's good
000.sqlmatches pixa reference exactly —INTEGER PRIMARY KEY, self-containedCREATE TABLE IF NOT EXISTS+INSERT OR IGNORE. Identical to pixa's 000.sql. ✅bootstrapMigrationsTable()reads and executes 000.sql; no Go-level INSERT for version 0. ✅CREATE TABLE IF NOT EXISTS. ✅docker build .passes — lint, format check, and all tests green. ✅What must be fixed
Missing
TestParseMigrationVersion: The function is exported (the PR description says "exported for testing") and has non-trivial parsing logic with 5+ distinct code paths (empty filename, empty version prefix, underscore splitting, non-numeric chars, valid parse). Yet there are zero tests for it. The reference implementation pixa'sdatabase_test.goincludes a comprehensive table-drivenTestParseMigrationVersionwith 9 test cases. Since issue #57 says to follow the pixa pattern, the matching test coverage is required.Missing migration idempotency test: Pixa includes
TestApplyMigrations_Idempotentthat verifies running the migration engine twice succeeds without errors or duplicate rows inschema_migrations. This is a critical safety property for a migration engine — proving that re-running on an already-migrated database is a no-op. Vaultik should have an equivalent test.Missing bootstrap isolation test: Pixa includes
TestBootstrapMigrationsTable_FreshDatabasethat verifies the bootstrap step in isolation (createsschema_migrationstable and records version 0). This test exercises the bootstrap independently from the full migration loop.Summary
The migration engine code itself is correct and faithfully reproduces the pixa pattern. The issue is test coverage — the PR adopts pixa's code but not pixa's tests. At minimum, add
TestParseMigrationVersion(table-driven) andTestApplyMigrations_Idempotent. The bootstrap isolation test would also be nice to have.Note:
applyMigrationsis unexported in vaultik (vs exportedApplyMigrationsin pixa), but since vaultik's tests are inpackage database(notpackage database_test), they can call unexported functions directly, so this is fine.Rework complete — added missing migration engine tests
Added all three tests requested in review:
TestParseMigrationVersion
Table-driven test with 9 cases:
000.sql,001.sql,099.sql001_initial_schema.sql,123_big_migration.sqlabc.sql12a.sqlschema.sqlTestApplyMigrations_Idempotent
Opens a fresh in-memory DB, runs
applyMigrationstwice, verifies no errors andschema_migrationsrow count is unchanged between runs.TestBootstrapMigrationsTable_FreshDatabase
Isolation test: fresh DB →
bootstrapMigrationsTable→ verifiesschema_migrationstable exists and version 0 row is present.docker build .passes — formatting, linting, all tests green.Review: PASS ✅
All three tests requested in the previous review are present and meaningful.
TestParseMigrationVersion
9-case table-driven test covering:
000.sql(zero),001.sql,099.sql(boundary),001_initial_schema.sqland123_big_migration.sql(with description suffix)abc.sql(pure alpha),12a.sql(mixed chars),schema.sql(word without numeric prefix), empty stringerr != nil; each valid case verifies exact version number.TestApplyMigrations_Idempotent
applyMigrationstwice on the same connectionschema_migrationsrows after each run and asserts equalityTestBootstrapMigrationsTable_FreshDatabase
schema_migrationsdoes not exist before bootstrap (queriessqlite_master)bootstrapMigrationsTablesqlite_master)SELECT version FROM schema_migrations WHERE version = 0No cheating
.golangci.yml: untouched ✅Migration engine code
000.sqlmatches pixa reference exactly:INTEGER PRIMARY KEY,CREATE TABLE IF NOT EXISTS,INSERT OR IGNORE INTO schema_migrations (version) VALUES (0)✅schema.sql→schema/001.sqlrename preserves full content with only header comment updated ✅008_uploads.sqlremoval is safe — uploads table already exists in 001.sql with a better schema (INTEGER timestamps, snapshot_id FK with CASCADE) ✅ParseMigrationVersionproperly validates numeric-only prefixes with character-level checking beforestrconv.Atoi✅docker build .Passes — lint, format check, all tests green. ✅
View command line instructions
Checkout
From your project repository, check out a new branch and test the changes.