feat(testdb): fast test database creation for Postgres and Spanner#502
Conversation
Postgres: NewPostgresDatabaseFromTemplate creates isolated test databases by cloning a content-addressed template database that has already been migrated. The template is created once per unique migration set (SHA256 of migration filenames and contents) and reused across all tests in the process. Uses pg_advisory_lock for cross-process safety and marks the template with IS_TEMPLATE=true, ALLOW_CONNECTIONS=false. Spanner: NewSpannerDatabaseFromMigrations and CreateSpannerDatabaseFromMigrations create a Spanner test database with all migrations applied in a single batched DDL update via UpdateDatabaseDdl, instead of running each migration file individually through golang-migrate. The spanner_schema_migrations table is pre-populated with the latest version so subsequent RunMigrations calls are no-ops (ErrNoChange). Falls back to normal migration if DDL parsing fails. Shared helpers: migrationFiles, migrationHash, and migrationVersion utilities for walking embedded migration FS, computing content-addressed hashes, and extracting version numbers from filenames.
There was a problem hiding this comment.
Code Review
This pull request introduces utilities for accelerating test database setup in PostgreSQL and Google Spanner by utilizing template databases and batched DDL migrations. The feedback focuses on critical correctness and robustness issues: first, the PostgreSQL template creation uses session-level advisory locks on a connection pool, which can cause leaked locks or deadlocks and must be refactored to use a dedicated connection; second, several error paths in the Spanner setup fail to clean up the created database, leading to resource leaks; and finally, defensive nil checks should be added to prevent potential panics when the migrations filesystem is not provided.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
Pre-existing govulncheck failure in admin.TestAdmin__AddHandler via idna.ToASCII. Not related to the testdb changes but blocking CI.
Covers migrationFiles, migrationHash, and migrationVersion with embedded test fixture migrations. Brings testdb package coverage above 0% to satisfy CI coverage threshold.
Windows CI reads embedded files with CRLF line endings, causing exact string equality assertions to fail.
Some tests (e.g. TestEnvironment in card-gateway) call service.NewEnvironment directly without going through CreateTestDatabase. The old CreateTestDatabase created the service database as a side effect via openOrCreateDatabase. NewPostgresDatabaseFromTemplate now preserves that behavior by creating the service database if it doesn't exist. Also fix nil pointer panic by guarding testEnv.Shutdown in card-gateway.
Use a dedicated Postgres connection for advisory lock acquisition and release so the session-level lock is not leaked across pooled connections. Clean up Spanner databases on post-create failures and guard nil migration filesystems before reading migrations.
Adds unit coverage for service database creation side effects, content-addressed lock keys, and nil Spanner migration files so short CI coverage stays above threshold.
Use the advisory lock only for the template lifecycle: check whether the content-addressed template exists, create and migrate it if missing, then mark it as a template before unlocking. The per-test clone happens after unlocking so clone creation is not unnecessarily serialized.
Summary
Add reusable test database helpers in
database/testdbthat eliminate per-test migration replay.Postgres:
NewPostgresDatabaseFromTemplatepg_advisory_lockfor cross-process safetyIS_TEMPLATE=true,ALLOW_CONNECTIONS=falseCREATE DATABASE test... TEMPLATE tmpl...— full isolation, no migration replaySpanner:
NewSpannerDatabaseFromMigrations/CreateSpannerDatabaseFromMigrationsUpdateDatabaseDdlspanner_schema_migrationstable with latest version so subsequentRunMigrationscalls are no-ops (ErrNoChange)CreateSpannerDatabaseFromMigrationsis the error-returning variant for use outsidetesting.TBcontexts (e.g.sync.Once.Do)Shared helpers (
migrations.go)migrationFiles: walks embeddedfs.FS, filters by suffix, sorts by filenamemigrationHash: SHA256 of filenames + contents (content-addressed)migrationVersion: extracts version number from last migration filenameBenchmark results
card-gateway (Postgres template DB, 37 packages):
moov-money (Spanner fast DDL, 9 packages):
Test plan
go build ./...passesgo vet ./database/testdb/...passes