batchCooking/apps/api/test-support/reset-db.ts
Nicolas ec01fc0107
All checks were successful
CI / build (push) Successful in 2m44s
CI / lint (push) Successful in 2m50s
CI / intent-service-test (push) Successful in 4m36s
CI / e2e (push) Successful in 8m12s
CI / test (push) Successful in 14m57s
fix(recipes): persiste les métadonnées de clause des tech-steps à l'édition
`createRecipeInternal` écrivait, pour chaque technique détectée, ses
ingrédients / ustensiles / températures de clause ; `updateRecipe` et le
script `backfill-tech-steps.ts` n'écrivaient que le span de la technique.
Toute recette ré-enregistrée via l'UI (ou touchée par le backfill) perdait
donc silencieusement ces métadonnées — et `cooking-optimizer.ts` ne pouvait
plus détecter de conflit de four entre deux préchauffages dès qu'une recette
avait été éditée une fois (ni sur les imports antérieurs à la détection des
températures, jamais ré-enregistrés).

- `toStepTechStepCreateInputs` (nouveau, `lib/recipe-matching/`) : seule
  source de vérité du payload `techSteps.create`, utilisée par les trois
  appelants pour qu'ils ne divergent plus.
- `backfill-tech-steps.ts` : `createMany` -> `create` par ligne (createMany
  ne fait pas les écritures imbriquées) + doc à jour.
- Rattrapage automatique au déploiement : `model BackfillState` + migration
  `backfill_state`, script `backfill-runtime.ts` ajouté au CMD Docker après
  `seed-runtime`. Gardé par un marqueur (tourne une seule fois), n'échoue
  jamais le boot (une panne du backfill est loggée, le serveur démarre
  quand même, retente au prochain start).
- Test `PATCH /recipes/:id` : une température de clause survit à l'édition.
- `reset-db.ts` : `backfill_state` ajouté au TRUNCATE.

Vérifié : `tsc --noEmit` + `pnpm --filter api build` + `pnpm lint` OK ;
migration = SQL généré par Prisma pour le modèle, à l'identique. Mocha
`recipe.test.ts` non lancé (Postgres + intent-service requis, indisponibles
ici) — à jouer en CI / sur la machine de dev.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-04 09:08:15 +02:00

56 lines
2.8 KiB
TypeScript

import { env } from "../src/config/env.js";
import { prisma } from "../src/db/prisma.js";
import { syncRecipeSources } from "../src/db/recipe-source-sync.js";
import { seedReferenceData } from "../src/db/reference-seed-data.js";
/**
* Refuses to run outside a database that's obviously a test one — belt and
* braces alongside `config/env.ts` loading `.env.test` (not `.env`) under
* `NODE_ENV=test`: this already wiped a real local dev database once, when
* both env files shared one `DATABASE_URL`. `resetDatabase()` below
* TRUNCATEs almost the entire schema before every single test, so a
* misconfigured/missing `.env.test` must fail loudly here rather than
* silently truncate whatever `DATABASE_URL` happens to be set.
*/
function assertRunningAgainstTestDatabase() {
if (env.NODE_ENV !== "test") {
throw new Error(
`resetDatabase() TRUNCATEs almost the whole schema — refusing to run outside NODE_ENV=test (currently "${env.NODE_ENV}").`,
);
}
// "test" covers a local `.env.test` (`batchcooking_test`); "ci" covers
// CI's own service database (`batchcooking_ci`, set directly via the
// workflow's `env:`, not a `.env.test` file — see ci.yml). Neither
// matches the real dev database's name (`batchcooking`), which is the
// one case this must actually catch.
if (!env.DATABASE_URL?.includes("test") && !env.DATABASE_URL?.includes("ci")) {
throw new Error(
`resetDatabase() refuses to run against a DATABASE_URL that doesn't look like a test database (got "${env.DATABASE_URL}", expected it to contain "test" or "ci") — see .env.test.example.`,
);
}
}
// Single TRUNCATE ... CASCADE covers FK ordering and resets identity
// sequences — used between tests/scenarios to start from a clean slate.
// Re-seeds the Diet/Category/Allergy/Unit reference data right after
// truncating it, so every test starts from the same realistic reference
// data the real app seeds (`prisma/seed.ts`) rather than empty tables —
// tests exercising dietId/allergyIds/unitId need real rows to reference.
// `syncRecipeSources` runs last, for the same reason: `sources` should
// reflect whatever adapters this test run happens to have registered
// (usually none — see recipe-source-registry.ts).
export async function resetDatabase() {
assertRunningAgainstTestDatabase();
await prisma.$executeRawUnsafe(`
TRUNCATE TABLE
"user_profile_allergy", "user_preference", "allergy", "category",
"planning_item", "planning",
"recipe_ingredient", "step_tech_step", "step", "tech_step",
"recipe", "ingredients", "sources", "unit",
"user_profiles", "diet", "house",
"admin_users", "analytics_events", "worker_heartbeats", "backfill_state"
RESTART IDENTITY CASCADE;
`);
await seedReferenceData(prisma);
await syncRecipeSources(prisma);
}