test: stop wiping admin user during integration tests #29

Merged
bvandeusen merged 262 commits from dev into main 2026-04-29 12:24:09 -04:00
bvandeusen commented 2026-04-29 12:06:54 -04:00 (Migrated from git.fabledsword.com)

Test-isolation fix. Integration tests previously TRUNCATEd the users table at setup, which wiped the operator's admin login every time the suite ran against a Postgres instance shared with their dev environment. This had been informally tracked as Fable #339 since M3.

What this changes

internal/dbtest/reset.go (new)

Shared helper exposing:

  • ResetDB(t, pool) — TRUNCATEs every data table EXCEPT users, then DELETE FROM users WHERE username LIKE 'test-%'. Operator's admin row (and any other non-test row) is left untouched.
  • TestUserPrefix = "test-" — required prefix for any user row a test creates.

Test files migrated to ResetDB (9 files)

  • internal/api/auth_test.goseedUser helper now auto-prefixes usernames.
  • internal/api/me_test.go — assertion updated for prefixed username.
  • internal/playevents/writer_test.go
  • internal/playsessions/service_test.go
  • internal/recommendation/candidates_test.go (covers candidates_v2_test.go via shared fixture)
  • internal/scrobble/queue_test.go
  • internal/similarity/worker_integration_test.go
  • internal/subsonic/scrobble_test.go
  • internal/subsonic/star_test.go

Hardcoded test usernames ("tester", "bob") now use dbtest.TestUserPrefix + "...". The "alice" / "bob" literals in api/auth_test.go flow through the seedUser helper which prefixes internally — caller-side names stay readable.

internal/auth/bootstrap_test.go (special case)

This test fundamentally requires an empty users table — it exercises the "first time admin is created" path that Bootstrap() guards with a count == 0 check. So it still TRUNCATEs users. To keep the operator's admin intact:

  1. Before TRUNCATE, save every user that doesn't start with test-.
  2. Use cfg.Username = "test-admin" for the bootstrap call so the test row is itself test-prefixed.
  3. t.Cleanup restores the saved rows after assertions complete.

Net effect: operator's admin/<password> login keeps working between test runs.

Test plan

  • go build ./... clean
  • go vet ./... clean
  • golangci-lint run ./... clean
  • go test -short -race ./... all packages green
  • Integration suite (in-network docker run, go test -race -p 1 ./...) — all packages green except library/scanner_test which races with the operator's running minstrel container scanning their music library (pre-existing local-only flake; CI's ephemeral DB doesn't see it)
  • Manual verify: operator's admin/admin login still works after running the full integration suite locally
  • Manual verify: bootstrap_test.go save/restore round-trip preserves admin row

Out of scope (for follow-up)

The "proper" long-term fix is to make tests refuse to run when MINSTREL_TEST_DATABASE_URL is missing or equal to DATABASE_URL — i.e., enforce a separate test database. This PR is the minimal, surgical fix that stops the bleeding without that infra change.

🤖 Generated with Claude Code

Test-isolation fix. Integration tests previously TRUNCATEd the `users` table at setup, which wiped the operator's admin login every time the suite ran against a Postgres instance shared with their dev environment. This had been informally tracked as Fable #339 since M3. ## What this changes ### `internal/dbtest/reset.go` (new) Shared helper exposing: - **`ResetDB(t, pool)`** — TRUNCATEs every data table EXCEPT `users`, then `DELETE FROM users WHERE username LIKE 'test-%'`. Operator's admin row (and any other non-test row) is left untouched. - **`TestUserPrefix = "test-"`** — required prefix for any user row a test creates. ### Test files migrated to `ResetDB` (9 files) - `internal/api/auth_test.go` — `seedUser` helper now auto-prefixes usernames. - `internal/api/me_test.go` — assertion updated for prefixed username. - `internal/playevents/writer_test.go` - `internal/playsessions/service_test.go` - `internal/recommendation/candidates_test.go` (covers `candidates_v2_test.go` via shared fixture) - `internal/scrobble/queue_test.go` - `internal/similarity/worker_integration_test.go` - `internal/subsonic/scrobble_test.go` - `internal/subsonic/star_test.go` Hardcoded test usernames (`"tester"`, `"bob"`) now use `dbtest.TestUserPrefix + "..."`. The `"alice"` / `"bob"` literals in `api/auth_test.go` flow through the `seedUser` helper which prefixes internally — caller-side names stay readable. ### `internal/auth/bootstrap_test.go` (special case) This test fundamentally requires an empty `users` table — it exercises the "first time admin is created" path that `Bootstrap()` guards with a `count == 0` check. So it still TRUNCATEs `users`. To keep the operator's admin intact: 1. Before TRUNCATE, save every user that doesn't start with `test-`. 2. Use `cfg.Username = "test-admin"` for the bootstrap call so the test row is itself test-prefixed. 3. `t.Cleanup` restores the saved rows after assertions complete. Net effect: operator's `admin/<password>` login keeps working between test runs. ## Test plan - [x] `go build ./...` clean - [x] `go vet ./...` clean - [x] `golangci-lint run ./...` clean - [x] `go test -short -race ./...` all packages green - [x] Integration suite (in-network docker run, `go test -race -p 1 ./...`) — all packages green except `library/scanner_test` which races with the operator's running minstrel container scanning their music library (pre-existing local-only flake; CI's ephemeral DB doesn't see it) - [x] **Manual verify: operator's `admin/admin` login still works after running the full integration suite locally** - [x] **Manual verify: `bootstrap_test.go` save/restore round-trip preserves admin row** ## Out of scope (for follow-up) The "proper" long-term fix is to make tests refuse to run when `MINSTREL_TEST_DATABASE_URL` is missing or equal to `DATABASE_URL` — i.e., enforce a separate test database. This PR is the minimal, surgical fix that stops the bleeding without that infra change. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Sign in to join this conversation.
No Reviewers
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: bvandeusen/minstrel#29