Test: prefs::tests::put_prefs_* flake with "set sqlite pragmas" (database is locked) under the parallel suite #157

Closed
opened 2026-09-30 03:06:01 +00:00 by cameron · 0 comments
Owner

Problem

A different prefs::tests::put_prefs_* test fails on roughly every other full cargo test run (seen on master 79b339a and on the #155 / #156 branches):

thread 'prefs::tests::put_prefs_…' panicked at src/database/mod.rs:193:6:
set sqlite pragmas: … database is locked

Observed failing: put_prefs_requires_auth, put_prefs_owner_defaults_null_clears, put_prefs_valid_display_tz_persists, put_prefs_partial_update_leaves_omitted_fields_untouched, put_prefs_owner_defaults_set_and_persist. They always pass in isolation (cargo test --lib prefs::tests). Worse on a fresh auth.db (e.g. a new worktree). Because cargo stops at the first failing test binary, a flake also hides the integration tests unless --no-fail-fast is used.

Likely cause (unverified)

database::connect() runs one batch:

PRAGMA journal_mode = WAL;
PRAGMA busy_timeout = 5000;
...

The WAL switch needs a lock, but it runs before busy_timeout is set, so it uses SQLite's default timeout of 0 and fails immediately on any contention instead of waiting. AppState::test_state() opens ~10 DAO connections to the one shared DATABASE_URL file, and six test modules build it in parallel (files ×4, video ×5, ai::handlers ×3, prefs, memories, reels). ai::handlers::turn_replay_tests already works around this with a module-local DB_INIT mutex, which doesn't cover the other modules.

Fix direction

Set busy_timeout first in connect() (before journal_mode = WAL), so the switch waits out contention. That fixes it at the source for every test module (and for startup), and the DB_INIT workaround can probably go. If it doesn't fully fix it, fall back to a crate-wide init guard inside test_state() rather than per-module mutexes.

Acceptance

  • cargo test passes repeatedly (e.g. 10 consecutive full runs) with no set sqlite pragmas panic
  • Includes a fresh-DB run (delete the test auth.db first)
  • DB_INIT removed if the root fix makes it redundant

🤖 Generated with Claude Code

## Problem A different `prefs::tests::put_prefs_*` test fails on roughly every other full `cargo test` run (seen on master `79b339a` and on the #155 / #156 branches): thread 'prefs::tests::put_prefs_…' panicked at src/database/mod.rs:193:6: set sqlite pragmas: … database is locked Observed failing: `put_prefs_requires_auth`, `put_prefs_owner_defaults_null_clears`, `put_prefs_valid_display_tz_persists`, `put_prefs_partial_update_leaves_omitted_fields_untouched`, `put_prefs_owner_defaults_set_and_persist`. They always pass in isolation (`cargo test --lib prefs::tests`). Worse on a fresh `auth.db` (e.g. a new worktree). Because cargo stops at the first failing test binary, a flake also hides the integration tests unless `--no-fail-fast` is used. ## Likely cause (unverified) `database::connect()` runs one batch: PRAGMA journal_mode = WAL; PRAGMA busy_timeout = 5000; ... The WAL switch needs a lock, but it runs **before** `busy_timeout` is set, so it uses SQLite's default timeout of 0 and fails immediately on any contention instead of waiting. `AppState::test_state()` opens ~10 DAO connections to the one shared `DATABASE_URL` file, and six test modules build it in parallel (files ×4, video ×5, ai::handlers ×3, prefs, memories, reels). `ai::handlers::turn_replay_tests` already works around this with a module-local `DB_INIT` mutex, which doesn't cover the other modules. ## Fix direction Set `busy_timeout` first in `connect()` (before `journal_mode = WAL`), so the switch waits out contention. That fixes it at the source for every test module (and for startup), and the `DB_INIT` workaround can probably go. If it doesn't fully fix it, fall back to a crate-wide init guard inside `test_state()` rather than per-module mutexes. ## Acceptance - [ ] `cargo test` passes repeatedly (e.g. 10 consecutive full runs) with no `set sqlite pragmas` panic - [ ] Includes a fresh-DB run (delete the test `auth.db` first) - [ ] `DB_INIT` removed if the root fix makes it redundant 🤖 Generated with [Claude Code](https://claude.com/claude-code)
cameron added the needs-triage label 2026-09-30 03:06:01 +00:00
Sign in to join this conversation.