From b9c4b255e807e6436dbc17bc7bdd2120933448ed Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 6 Jul 2026 03:41:12 +0000 Subject: [PATCH] Fix flaky food-plan suggestion e2e tests by isolating them in an ephemeral household MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The deploy e2e workflow's suggestion tests asserted a freshly-created recipe ranks within the top N results of GET /api/food-plan/suggestions, which scores every recipe in the household. The shared deploy household had already accumulated 200+ recipes that permanently outscore a brand new "not planned yet" candidate, so no result count can make the assertion reliable there — a prior fix (raising MaxCount, cleaning up other specs' leaks) didn't hold because it couldn't undo data that had already leaked into the persistent household. Add useEphemeralHousehold() to food-plans.spec.ts: it creates a throwaway household via POST /api/households and points the page at it, so the two suggestion tests run against a household with zero pre-existing recipes and get deterministic rankings. Deleting the household in `finally` also makes its recipes/entries invisible to every other household's suggestions, so per-recipe/entry cleanup is no longer needed either. --- CLAUDE.md | 1 + anything-frontend/e2e/food-plans.spec.ts | 98 ++++++++++++++---------- 2 files changed, 57 insertions(+), 42 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 12b5a1f3..e77eda32 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -176,4 +176,5 @@ The CI workflow uses a fresh local database; the deploy workflow runs against th - **Read `env.apiUrl` for direct API calls in `page.evaluate`**, not a hard-coded `localhost` URL. In the deploy environment the API is proxied at the same origin as the frontend (`E2E_API_URL=E2E_BASE_URL`), so direct fetch calls inside tests must use `env.apiUrl` from `e2e/env.ts`. - **Shared `apiRequest` helper:** `e2e/apiRequest.ts` exports the authenticated `page.evaluate`-based fetch helper (reads the bearer token + household ID from `localStorage`). Import it instead of redefining a local copy per spec file. - **Uncleaned test recipes silently corrupt food-plan suggestion rankings.** `GET /api/food-plan/suggestions` scores every household recipe and returns only the top `count` (max 200, see `GetFoodPlanSuggestions.MaxCount`). A recipe with real planning history (rotation/favorites/seasonality) scores far higher than a brand-new "not planned yet" recipe (a fixed, low score). Any e2e test that creates a recipe (and especially one that also schedules it on the food plan) without deleting it in a `finally` block leaves a permanently-scoring competitor in the persistent deploy household — over many runs this pushes freshly-created test recipes out of the ranked suggestions window, causing unrelated suggestion tests in `food-plans.spec.ts` to fail. Always wrap recipe/food-plan-entry/shopping-list creation in `try {} finally { /* delete via apiRequest */ }`, even in tests that aren't primarily about food plans (e.g. `flows.spec.ts`, `recipes.spec.ts`). + - **Raising `MaxCount` and cleaning up leaks is not sufficient once the shared household is already crowded.** This was tried (`MaxCount` 50→200, cleanup added to `flows.spec.ts`/`recipes.spec.ts`) and the very next deploy run failed with the same errors — the shared `anything-e2e` household had *already* accumulated 200+ recipes that permanently outscore a fresh candidate, and a code fix can't undo data that already leaked in past runs. Any test asserting a specific recipe's rank/membership in `/api/food-plan/suggestions` is fundamentally unreliable against a real, ever-growing shared household — no `count` value fixes it. The actual fix (see `food-plans.spec.ts`'s `useEphemeralHousehold` helper): create a throwaway household via `POST /api/households`, point the page at it via `localStorage.setItem("householdId", ...)`, run the ranking-sensitive assertions there (guaranteed empty, so ranking is deterministic), then `DELETE /api/households/{id}` in `finally` — which also makes any recipes/entries created in it permanently invisible to every other household's suggestions, so no per-recipe cleanup is needed for those specific tests either. - **Don't let a dropdown auto-open over content it doesn't own.** The food-plan day dialog's ranked-suggestions `