Tests wait for what they assert, not the first paint; backup tests build their own schema; one changelog block each (R5, B9) #803

Merged
claude-bot merged 4 commits from build/test-race-audit into main 2026-09-24 04:31:36 +00:00
Collaborator

R5 from the B-questions (B9: the test-race audit). There is no product change.

Frontend: 20 racy tests in 5 files

Every flaky test this week had the same shape: the test waited for the first thing, then asserted a later thing. The audit checked:

  • about 140 expect(await findBy…).toHave… sites;
  • about 750 find-then-get pairs;
  • about 200 click-then-assert sites;
  • every lazy route and every test timer.

It fixed the real ones:

  • Profile.test.jsx and Notifications.test.jsx: the shared open() helper waited for .doc, which is on the first paint. The sections behind it load after mounting, behind a Busy line. open() now also waits until nothing is busy. This covers 17 tests.
  • AppFrame.test.jsx: the footer's version comes from /api/meta a render after the footer exists. The test now waits on the text, not the element.
  • NewCampaign.test.jsx: it renders a lazy route. It now warm-imports the page in beforeAll, like Home.test.
  • GeneratorPanel.test.jsx: the clock is faked with shouldAdvanceTime, so real runner delay adds to it. The test now asserts at least 4s, not exactly 4s.

Sites judged not racy, where the element and its asserted state land in the same render, are listed in the audit report. No product race was found.

Backend: test_backup_tasks.py failed on its own

It writes through AsyncSessionLocal and never asked for the engine fixture, which is the only thing that creates the tables. It passed in a full run only because an earlier module had built the schema.

  • An autouse fixture now depends on engine, and clears the committed backup config after each test.
  • test_audio_tasks.py had the same bug, and is fixed the same way.
  • Before: 5 failed on its own (relation "app_settings" does not exist). After: passes on its own and in the full suite (4099 passed, 13 skipped).

CHANGELOG

[Unreleased] had grown a second **Added**/**Fixed** pair. Its entries are folded into the first blocks, with no wording changed.

Checks

  • Frontend. Five stress runs on the audit's tree, all 142/142 files:

    • three default runs;
    • one with --maxWorkers=1;
    • one with --maxWorkers=8.

    After merging main: 143 files, 2017 tests passing. Lint: 0 errors, and the warning count is unchanged at 228.

  • Backend. Ruff 0.4.4 format and check are clean.

🤖 Generated with Claude Code

R5 from the B-questions (B9: the test-race audit). There is no product change. ## Frontend: 20 racy tests in 5 files Every flaky test this week had the same shape: **the test waited for the first thing, then asserted a later thing.** The audit checked: - about 140 `expect(await findBy…).toHave…` sites; - about 750 find-then-get pairs; - about 200 click-then-assert sites; - every lazy route and every test timer. It fixed the real ones: - **`Profile.test.jsx` and `Notifications.test.jsx`:** the shared `open()` helper waited for `.doc`, which is on the first paint. The sections behind it load after mounting, behind a Busy line. `open()` now also waits until nothing is busy. This covers 17 tests. - **`AppFrame.test.jsx`:** the footer's version comes from `/api/meta` a render after the footer exists. The test now waits on the text, not the element. - **`NewCampaign.test.jsx`:** it renders a lazy route. It now warm-imports the page in `beforeAll`, like `Home.test`. - **`GeneratorPanel.test.jsx`:** the clock is faked with `shouldAdvanceTime`, so real runner delay adds to it. The test now asserts at least 4s, not exactly 4s. Sites judged not racy, where the element and its asserted state land in the same render, are listed in the audit report. No product race was found. ## Backend: `test_backup_tasks.py` failed on its own It writes through `AsyncSessionLocal` and never asked for the `engine` fixture, which is the only thing that creates the tables. It passed in a full run only because an earlier module had built the schema. - An autouse fixture now depends on `engine`, and clears the committed backup config after each test. - `test_audio_tasks.py` had the same bug, and is fixed the same way. - Before: 5 failed on its own (`relation "app_settings" does not exist`). After: passes on its own and in the full suite (4099 passed, 13 skipped). ## CHANGELOG `[Unreleased]` had grown a second `**Added**`/`**Fixed**` pair. Its entries are folded into the first blocks, with no wording changed. ## Checks - **Frontend.** Five stress runs on the audit's tree, all 142/142 files: - three default runs; - one with `--maxWorkers=1`; - one with `--maxWorkers=8`. After merging main: 143 files, 2017 tests passing. Lint: 0 errors, and the warning count is unchanged at 228. - **Backend.** Ruff 0.4.4 format and check are clean. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
test_backup_tasks.py and test_audio_tasks.py write through
AsyncSessionLocal, as the Celery tasks do, and requested no fixture that
creates the tables. Run on their own they failed with `relation
"app_settings" does not exist` / `relation "users" does not exist`; in a
full run they passed only because an earlier module had pulled in the
session-scoped `engine` fixture first.

Both now depend on `engine`, the pattern test_backup_offsite.py already
uses. The backup tests also clear the backup config they commit after
each test, so the file leaves nothing for the next module to trip on.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
An audit of the whole jsdom suite for the shape behind this week's flaky
runs: a wait that ends on something drawn at first paint, then a read of
something a request, a lazy import or a timer later. About 140 sites of
`expect(await findBy…).toHave…` and its two-line form were checked against
the component that draws them, plus every find-then-get pair, every
assertion straight after a user action, every lazy route and every real
timer. Most are sound: the element and its state land in the same commit.
Twenty tests in five files were not.

- AppFrame: the footer test found `contentinfo`, which is on the first
  paint, then read the version, which comes from /api/meta a render
  later. It now waits for the version.
- Profile: `open()` ended at `.doc`, the page's first paint. When you can
  play, Connected accounts and Calendar each read their own data after
  mounting, behind a Busy line, and nine tests read them straight after
  `open()`. It now also waits until nothing on the page is loading.
- Notifications: the same helper, and eight tests that then waited for the
  "Notifications" heading (the page title, first paint) and read the grid,
  which `useAsync`'s `immediate` fetches a tick after that paint. Same fix.
- NewCampaign: renders the lazy route and nothing else imports the page,
  so the first test paid the cold import inside its own wait. Warmed in a
  `beforeAll`, as Home's tests are (37e6eab).
- GeneratorPanel: asserted exactly "Elapsed 4s" after advancing 4 s on a
  clock faked with `shouldAdvanceTime`, which also moves with real time; a
  second of runner delay before the advance read "5s". It asserts at least
  four now.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
# Conflicts:
#	CHANGELOG.md
docs(changelog): one Added and one Fixed block under [Unreleased]
All checks were successful
CI / Backend lint (ruff) (pull_request) Successful in 37s
CI / Feedback site tests (pull_request) Successful in 54s
CI / Bot/backend version sync (pull_request) Successful in 29s
CI / Does this change touch the recording path? (pull_request) Successful in 17s
CI / Synthetic session harness (no GPU, no LLM) (pull_request) Has been skipped
CI / Bot tests and audit (pull_request) Successful in 2m8s
CI / Summarisation accuracy eval harness (stub provider) (pull_request) Successful in 3m40s
CI / Docker image build (pull_request) Successful in 3m2s
CI / Frontend tests, audit, and build (pull_request) Successful in 8m24s
CI / End-to-end journeys (desktop, phone, keyboard-only) (pull_request) Successful in 5m53s
CI / Backend migration, tests, and audit (pull_request) Successful in 20m41s
d56e2579f8
[Unreleased] had grown a second **Added**/**Fixed** pair below its
first three blocks. Their entries now sit at the end of the first
Added and Fixed blocks. No entry's wording changes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Sign in to join this conversation.
No description provided.