Add repository and boot-path tests for Feature 02 review
Independent review Codex gpt-6-sol high: APPROVED (0 findings)
CI / ci (pull_request) Successful in 1m24s
CI / ci (push) Successful in 1m24s

Addresses feature-review findings F1 and F2 on PR #14.

F1: internal/store/repository_test.go inserts, reads back and updates a
row in week_plans, plan_days, suggestions, shopping_lists,
shopping_groups and shopping_items against a migrated temp database,
using raw SQL and seeding foreign-key parents.

F2: TestMigrateReopenSameDataDirIsNoOp reopens the same data directory
and proves Migrate leaves schema_migrations and the schema unchanged.
cmd/server extracts the open+migrate boot step into openStore over an
injectable bootMigrations set; tests prove a broken migration makes
openStore and run return the error before listening.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-03 09:23:29 -04:00
co-authored by Claude Opus 5.5
parent 17a69f4de7
commit 9db5542e9b
4 changed files with 352 additions and 4 deletions
+60
View File
@@ -3,6 +3,7 @@ package store
import (
"context"
"io/fs"
"slices"
"strings"
"testing"
"testing/fstest"
@@ -201,3 +202,62 @@ func TestMigrateBootstrapThenEmbeddedInitApplies(t *testing.T) {
t.Error("table from 0001_init was not created")
}
}
// schemaSQL returns every schema object's definition, so a test can prove a
// later Migrate created or altered nothing.
func schemaSQL(t *testing.T, s *Store) []string {
t.Helper()
rows, err := s.Reader.Query("SELECT type || ' ' || name || ': ' || coalesce(sql, '') FROM sqlite_schema ORDER BY type, name")
if err != nil {
t.Fatal(err)
}
defer rows.Close()
var out []string
for rows.Next() {
var def string
if err := rows.Scan(&def); err != nil {
t.Fatal(err)
}
out = append(out, def)
}
if err := rows.Err(); err != nil {
t.Fatal(err)
}
return out
}
func TestMigrateReopenSameDataDirIsNoOp(t *testing.T) {
ctx := context.Background()
dir := t.TempDir()
first, err := Open(ctx, dir)
if err != nil {
t.Fatal(err)
}
if err := Migrate(ctx, first.Writer, migrations.FS); err != nil {
t.Fatal(err)
}
before, beforeSchema := migrationRows(t, first), schemaSQL(t, first)
if err := first.Close(); err != nil {
t.Fatal(err)
}
second, err := Open(ctx, dir)
if err != nil {
t.Fatalf("reopen: %v", err)
}
t.Cleanup(func() {
if err := second.Close(); err != nil {
t.Error(err)
}
})
if err := Migrate(ctx, second.Writer, migrations.FS); err != nil {
t.Fatalf("migrate after reopen: %v", err)
}
if after := migrationRows(t, second); !slices.Equal(after, before) {
t.Errorf("schema_migrations changed on reopen:\n got %v\nwant %v", after, before)
}
if afterSchema := schemaSQL(t, second); !slices.Equal(afterSchema, beforeSchema) {
t.Errorf("schema changed on reopen:\n got %v\nwant %v", afterSchema, beforeSchema)
}
}