Le matching par regex ne généralisait jamais au-delà de son propre vocabulaire — une étape décrivant la fonte du beurre comme "jusqu'à ce que le beurre ait disparu dans la poêle" ne contient aucun verbe sur lequel une regex pourrait s'ancrer, alors que le sens est sans ambiguïté. Nouveau pipeline en 3 étapes (TechStepClassifierService, node-nlp 4.27.0 — la 5.x est encore alpha, non retenue) : 1. NER (entités enum) trouve les mentions candidates + leur position exacte, à partir de listes de synonymes (tech-step-training-data.ts) plutôt que de regex écrites à la main. ner.threshold: 1 (exact, après normalisation) — le défaut à 0.8 faisait matcher "faire" (verbe auxiliaire omniprésent) contre "frire" par pure proximité de chaîne. 2. La description est découpée en clauses autour de ces candidats (splitIntoClauses, pure/testable sans modèle). 3. Le NlpManager classe chaque clause individuellement, entraîné sur des phrases qui n'emploient jamais le verbe de la technique — c'est ce qui apporte la compréhension du sens. En dessous de CONFIDENCE_THRESHOLD (0.65, ajusté empiriquement), retombe sur la technique impliquée par l'ancre NER plutôt que d'abandonner un match clairement ancré sur un mot-clé. TechStepMapping (table de regex par technique/locale) supprimée — migration 20260821130000_drop_tech_step_mapping — plus aucune table n'est interrogée à l'exécution, les données de matching vivent en code. TECH_STEPS (reference-seed-data.ts) simplifié en simple liste de uid, les mappings ayant disparu. Deux pièges trouvés en construisant ce pipeline, corrigés à la source : - db/prisma.ts construisait PrismaClient sans importer config/env.ts — un run de test isolé pouvait faire gagner la course au .env interne de Prisma (dev) contre .env.test. Fixé en important config/env.js en tout premier, pour effet de bord. - NlpManager a autoSave/autoLoad: true par défaut — persiste le modèle entraîné dans model.nlp et le recharge au lieu de ré-entraîner au prochain démarrage. Les deux désactivés explicitement (sinon un modèle obsolète masquerait silencieusement toute mise à jour du corpus/seuil) ; model.nlp ajouté au .gitignore en garde-fou. apps/api/src/db/prisma.ts, recipe.service.ts, sources.service.ts et recipe-translation.ts adaptés à la matching async (le classifieur entraîné remplace le couple loadTechStepMappingRules+matchTechStepSpans synchrone) ; server.ts appelle techStepClassifier.warmUp() avant d'accepter du trafic (le tout premier appel réel à NlpManager.process() charge les ressources par langue de node-nlp, plusieurs secondes). Vérifié : tsc --noEmit, biome check (0 erreur), build complet des 6 packages, 308 tests API (dont un test-support/reset-db.ts corrigé — référençait encore tech_step_mapping dans son TRUNCATE). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
55 lines
2.7 KiB
TypeScript
55 lines
2.7 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"
|
|
RESTART IDENTITY CASCADE;
|
|
`);
|
|
await seedReferenceData(prisma);
|
|
await syncRecipeSources(prisma);
|
|
}
|