Archive milestone artifacts: - milestones/v1.0-ROADMAP.md (full roadmap archive) - milestones/v1.0-REQUIREMENTS.md (26/26 requirements complete) - milestones/v1.0-phases/ (8 phase directories with plans, summaries, verifications) Updated: - PROJECT.md: full evolution review, all consolidation requirements validated - ROADMAP.md: collapsed to milestone summary with archive link - STATE.md: reset for next milestone - MILESTONES.md: created with stats and accomplishments - RETROSPECTIVE.md: created with lessons learned Deleted: - REQUIREMENTS.md (archived, fresh for next milestone) 8 phases, 17 plans, 34 tasks, 84 tests added, 6 days
8.9 KiB
phase, plan, type, wave, depends_on, files_modified, autonomous, requirements, must_haves
| phase | plan | type | wave | depends_on | files_modified | autonomous | requirements | must_haves | |||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 03-test-infrastructure | 01 | execute | 1 |
|
true |
|
|
Purpose: Establish the test foundation that all subsequent test phases (4-5) depend on. Tests need real database instances with identical configuration to production — same PRAGMAs, same migrations, same constraints — so test results are trustworthy.
Output: Modified database.go with shared PRAGMA function + production PRAGMAs applied, and new testhelper.go with NewTestDB(t).
<execution_context> @/home/caleb/.config/opencode/get-shit-done/workflows/execute-plan.md @/home/caleb/.config/opencode/get-shit-done/templates/summary.md </execution_context>
@.planning/PROJECT.md @.planning/ROADMAP.md @.planning/STATE.md@backend/database/database.go
From backend/database/database.go:
// DB wraps the SQLite database connection and queries.
type DB struct {
db *sql.DB
Ctx context.Context
Queries *sqlcgen.Queries
logger *slog.Logger
}
// NewDB opens the database and applies schema migrations.
func NewDB(logger *slog.Logger) (*DB, error)
// BeginTx starts a new database transaction.
func (d *DB) BeginTx() (*sql.Tx, error)
// ExecContext executes a query without returning any rows.
func (d *DB) ExecContext(query string, args ...any) (sql.Result, error)
// QueryContext executes a query that returns rows.
func (d *DB) QueryContext(query string, args ...any) (*sql.Rows, error)
From backend/database/database.go (internal):
//go:embed sql/schemas/*.sql
var schemas embed.FS
func runMigrations(ctx context.Context, db *sql.DB, logger *slog.Logger) error
func isDuplicateColumnErr(err error) bool
-
Create an unexported
applyPRAGMAs(ctx context.Context, db *sql.DB) errorfunction that executes these PRAGMAs in order:PRAGMA foreign_keys = ON(already exists in NewDB — extract it)PRAGMA synchronous = NORMALPRAGMA cache_size = -8000PRAGMA mmap_size = 67108864
Use a slice of PRAGMA strings and loop over them with
db.ExecContext. Wrap errors withfmt.Errorf("could not apply PRAGMA %q: %w", pragma, err). -
Modify
NewDB()to callapplyPRAGMAs(dbCtx, db)instead of the inlinePRAGMA foreign_keys = ONexec. Insert the call right afterdb.SetMaxOpenConns(1)— PRAGMAs before schema creation, per CONTEXT.md decision. -
Remove the standalone
foreign_keysPRAGMA block that currently exists inNewDB()(lines 58-65) since it's now handled byapplyPRAGMAs. -
Add a doc comment on
applyPRAGMAs:// applyPRAGMAs configures SQLite connection settings. Called by both NewDB and NewTestDB to ensure identical behavior.
Follow existing conventions: error wrapping with fmt.Errorf, blank line after early returns (nlreturn), keep lines under 100 chars (golines).
cd /mnt/vault/dev/golang/yellowjacket && go build -tags webkit2_41 ./backend/database/ && go vet -tags webkit2_41 ./backend/database/
applyPRAGMAsfunction exists indatabase.gowith all 4 PRAGMAs (foreign_keys, synchronous, cache_size, mmap_size)NewDB()callsapplyPRAGMAsinstead of inline foreign_keys PRAGMA- Package compiles and passes vet
-
Package declaration:
package database -
Imports:
context,database/sql,fmt,io/fs,log/slog,path,testing,modernc.org/sqlite(blank import for driver), andyellowjacket/backend/database/sql/sqlcgen. -
Exported function
NewTestDB(t *testing.T) *DB:- Call
t.Helper()at the start - Open in-memory SQLite:
sql.Open("sqlite", ":memory:?_busy_timeout=5000&_journal_mode=WAL") - If open fails,
t.Fatalf("could not open test database: %v", err) db.SetMaxOpenConns(1)— same as production- Create context:
ctx := t.Context()(uset.Context()per usetesting linter) - Call
applyPRAGMAs(ctx, db)— if error,t.Fatalf("could not apply PRAGMAs: %v", err) - Apply schemas: iterate
schemas.ReadDir("sql/schemas"), read each .sql file,db.ExecContext(ctx, string(sqlContent))— mirror the exact loop fromNewDB(). If error,t.Fatalf. - Call
runMigrations(ctx, db, slog.Default())— if error,t.Fatalf("could not run migrations: %v", err) - Do NOT run orphan cleanup query (CONTEXT.md decision: "test DBs start empty, no orphans to clean")
- Create queries:
queries := sqlcgen.New(db) - Register cleanup:
t.Cleanup(func() { db.Close() }) - Return
&DB{db: db, Ctx: ctx, Queries: queries, logger: slog.Default()}
- Call
-
Add doc comment:
// NewTestDB returns an in-memory SQLite database that mirrors the production setup (PRAGMAs + all migrations). The database is automatically closed when the test completes via t.Cleanup.
Note: Do NOT expose raw *sql.DB — tests use DB.ExecContext() / DB.Queries like production code (per CONTEXT.md decision). No functional options. No error return — failures are fatal via t.Fatalf.
Follow conventions: t.Helper(), t.Context(), blank import comment, doc comments ending with period, nlreturn spacing.
cd /mnt/vault/dev/golang/yellowjacket && go build -tags webkit2_41 ./backend/database/ && go vet -tags webkit2_41 ./backend/database/ && go test -tags webkit2_41 -race -count=1 -run TestNewTestDB ./backend/database/ 2>&1 || echo "No test yet — build+vet passed"
backend/database/testhelper.goexists with exportedNewTestDB(t *testing.T) *DB- Function opens
:memory:DB, applies PRAGMAs via sharedapplyPRAGMAs, applies schemas, runs migrations - No orphan cleanup, no health check, no error return, no functional options
- Cleanup registered via
t.Cleanup() - Package compiles, passes vet, and passes
-raceflag
# 1. Build the database package
go build -tags webkit2_41 ./backend/database/
# 2. Vet the database package
go vet -tags webkit2_41 ./backend/database/
# 3. Run all existing tests with race detector to confirm no regressions
make test
# 4. Verify applyPRAGMAs is called from both NewDB and NewTestDB
grep -n "applyPRAGMAs" backend/database/database.go backend/database/testhelper.go
# 5. Verify production PRAGMAs are all present
grep -c "PRAGMA" backend/database/database.go
<success_criteria>
backend/database/database.gohas a sharedapplyPRAGMAsfunction with all 4 PRAGMAsNewDB()callsapplyPRAGMAs(no more inline foreign_keys PRAGMA)backend/database/testhelper.goexportsNewTestDB(t *testing.T) *DBNewTestDBuses:memory:with same connection params, callsapplyPRAGMAs+ schema loop +runMigrationsNewTestDBregisterst.Cleanup(func() { db.Close() })make testpasses (all existing tests green, race detector clean)- No orphan cleanup in
NewTestDB, no health check, no functional options </success_criteria>