diff --git a/AGENTS.md b/AGENTS.md index b729a8e2..85885f00 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -71,6 +71,16 @@ were name-only across ALL libraries. Fixed; do not regress. ## Tests +- **THE DOCTRINE — tests are never run against a real/production + server.** The dev DB is the test environment, and the invariant every + test must uphold is: **leave the testuser experience alone** — its + settings, scans, and reading data must be exactly as the suite found + them. The helpers exist to enforce this; if a test needs protection + the helpers don't provide, EXPAND THE HELPERS rather than hand-rolling + (2026-10-21: `setSystemSettingRestored` — snapshot-and-restore for + system_settings — exists because a hardcoded ev restore could clobber + a deliberate dev bump, and the live dev server obeys that setting on + its next poll). - Use `setupTestServer()` from `cmd/server/tests/test_helpers_test.go` (see the Conventions section below for the pool/cleanup rules). - Integration tests run against the SHARED dev database: never write @@ -223,3 +233,25 @@ of a server bug: stop and flag the user. test pools use `max_conns=1`; never create a `pgxpool` per subtest (connection exhaustion); use `t.Cleanup()`-based helpers; table-driven subtests over duplicate functions. +7. **Scanner re-extraction is write-after-success** — a failed/empty + extraction must NEVER clear scan-derived columns. `updateMediaItem` + (generic full-row path) preserves `existing.AudioMetadata` when the + new extraction carries no audio payload, and `updateAudioBookItem` + refuses empty payloads over populated rows (both guarded 2026-10-21 + after an ev-gated re-extract NULLed every multi-file audiobook's + audio_metadata — books displayed as 1 second long and lost their + track manifests). New scan-owned column writes need the same guard. +8. **templ double-escapes style attribute values** — the codegen wraps + style exprs in a sanitizer AND `templ.EscapeString`, so ANY quote + character (even inside `templ.SafeCSS`) reaches the DOM as literal + `'` text and the CSS never parses (2026-10-21: every book-detail + hero backdrop broken + Firefox fetched `/media/&` → 500 spam). + Emit quote-free CSS: unquoted `url(...)` tokens via `SafeCSS` + + `cssURL()` in book_detail.templ. +9. **`vp test` runs the vite config** — plugins with build hooks fire + in the test context too. `cleanStaleChunks` guards on + `produced.size === 0` precisely because a bare sweep deleted every + hashed chunk in `web/static` when run under vitest (2026-10-21: + `npm run test` gutted the build; every page 404'd its imports). + Any future outDir-mutating plugin needs the same "am I a build?" + guard.