fix(recipes): synchronise les sources en base au démarrage de l'image de prod
Le conteneur de prod ne peuplait jamais la table Source : seed-runtime.ts (l'entrée seed de l'image Docker, exécutée après `prisma migrate deploy`) n'appelait que seedReferenceData(), jamais registerAllRecipeSources()/ syncRecipeSources() — contrairement à prisma/seed.ts (dev). server.ts enregistre bien les adaptateurs dans son propre registre en mémoire, mais c'est un processus distinct de celui qui lance seed-runtime.js dans la chaîne CMD du Dockerfile ; sans ce sync, GET /reference/sources renvoyait toujours [], et HouseholdSettingsPage masquait silencieusement toute la section sources (sources.length === 0 → return null). C'est ce que l'utilisateur a remarqué : impossible de paramétrer les sources visibles du foyer en prod. Vérifié en local : Source/HouseSource vidées, seed-runtime.js compilé relancé exactement comme le ferait le conteneur (migrate deploy déjà appliqué, puis ce script) → les deux sources (TheMealDB, JSON-LD) sont bien resynchronisées. Ajoute aussi la couverture Cypress du parcours "sources" qui manquait : - onboarding.feature : nouveau scénario où le catalogue de sources n'est pas vide — l'étape /onboarding/sources s'affiche et se soumet, au lieu du seul scénario existant qui la voyait toujours skippée (catalogue vide). - household-settings.feature : nouveaux scénarios pour la section sources de /parametres/foyer — affichage + sauvegarde (autosave incluse) quand des sources existent, et disparition complète de la section quand le catalogue est vide. - Nouvelles steps partagées (reference-data.steps.ts pour le catalogue, household-mutations.steps.ts pour la sélection par foyer). Non exécutés localement : Chromium/Electron headless plante au lancement du process GPU dans cet environnement (limitation documentée du README, reproductible sur main, sans lien avec ce changement) — vérifiés par relecture attentive contre le code source réel (libellés de traduction, routes, formes de requête/réponse) et en suivant le même gabarit que les scénarios existants déjà verts en CI. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
parent
6178205603
commit
deec91c5a3
6 changed files with 124 additions and 4 deletions
|
|
@ -6,6 +6,12 @@
|
|||
"runtimeExecutable": "pnpm",
|
||||
"runtimeArgs": ["--filter", "web", "dev"],
|
||||
"port": 5173
|
||||
},
|
||||
{
|
||||
"name": "api",
|
||||
"runtimeExecutable": "pnpm",
|
||||
"runtimeArgs": ["--filter", "api", "dev"],
|
||||
"port": 3000
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
import { prisma } from "../db/prisma.js";
|
||||
import { syncRecipeSources } from "../db/recipe-source-sync.js";
|
||||
import { seedReferenceData } from "../db/reference-seed-data.js";
|
||||
import { registerAllRecipeSources } from "../sources/index.js";
|
||||
|
||||
/**
|
||||
* Runtime seed entry point for the production Docker image — run via
|
||||
|
|
@ -14,11 +16,25 @@ import { seedReferenceData } from "../db/reference-seed-data.js";
|
|||
* alongside everything else, and it runs under plain `node`, no tsx
|
||||
* needed at runtime.
|
||||
*
|
||||
* Also registers and syncs the recipe-source registry
|
||||
* (`registerAllRecipeSources`/`syncRecipeSources`) — mirroring
|
||||
* `prisma/seed.ts`'s own two calls. Without this, `server.ts`'s own
|
||||
* `registerAllRecipeSources()` call only populates *that* process' in-memory
|
||||
* registry (each `node` invocation in the Docker CMD chain is a separate
|
||||
* process), so the `Source` table itself would stay permanently empty in
|
||||
* production and `GET /reference/sources` would always return `[]` — which
|
||||
* is exactly what silently hid the whole "sources" section of
|
||||
* `HouseholdSettingsPage` (`apps/web`) until this was added.
|
||||
*
|
||||
* Safe to run on every container start: `seedReferenceData` upserts by
|
||||
* each row's unique name, so re-running it against a database that
|
||||
* each row's unique name, and `syncRecipeSources` is equally idempotent
|
||||
* (see its own doc comment) — re-running both against a database that
|
||||
* already has this data is a no-op.
|
||||
*/
|
||||
registerAllRecipeSources();
|
||||
|
||||
seedReferenceData(prisma)
|
||||
.then(() => syncRecipeSources(prisma))
|
||||
.then(() => prisma.$disconnect())
|
||||
.catch(async (err) => {
|
||||
console.error(err);
|
||||
|
|
|
|||
|
|
@ -53,6 +53,32 @@ Feature: Household settings
|
|||
Then the household deletion request should have been made
|
||||
And I should see "Créer un foyer"
|
||||
|
||||
Scenario: Shows and saves the household's enabled recipe sources
|
||||
Given I am signed in as "Alice" "Martin"
|
||||
And my household id is 1
|
||||
And the household request returns the two-member household
|
||||
And the sources reference list has options
|
||||
And the household's enabled sources are empty
|
||||
And saving the source selection will succeed
|
||||
When I visit "/parametres/foyer"
|
||||
Then I should see the section "Sources disponibles"
|
||||
And I should see "TheMealDB"
|
||||
And I should see "Officielle"
|
||||
And I should see "Import générique (JSON-LD)"
|
||||
And I should see "Non officielle"
|
||||
When I check the checkbox "TheMealDB"
|
||||
Then the source selection update request should have been made with source id 1
|
||||
And I should see "Enregistré ✓"
|
||||
|
||||
Scenario: Hides the sources section entirely when no source is implemented yet
|
||||
Given I am signed in as "Alice" "Martin"
|
||||
And my household id is 1
|
||||
And the household request returns the two-member household
|
||||
And the sources reference list is empty
|
||||
And the household's enabled sources are empty
|
||||
When I visit "/parametres/foyer"
|
||||
Then I should not see "Sources de recettes"
|
||||
|
||||
Scenario: Leaves the household
|
||||
Given I am signed in as "Bob" "Dupont"
|
||||
And my user id is 2
|
||||
|
|
|
|||
|
|
@ -52,6 +52,31 @@ Feature: Onboarding wizard
|
|||
Then the allergies update request should have been made with no allergy ids
|
||||
And the URL should be the home page
|
||||
|
||||
Scenario: Shows and saves the sources step instead of skipping it, when sources are available
|
||||
Given the diets reference list is empty
|
||||
And selecting the diet will succeed
|
||||
And the household request returns no household
|
||||
And creating a household will succeed
|
||||
And the sources reference list has options
|
||||
And the household's enabled sources are empty
|
||||
And saving the source selection will succeed
|
||||
And the allergies reference list is empty
|
||||
And updating allergies will succeed
|
||||
And I have signed up
|
||||
When I click the button "Continuer"
|
||||
Then the URL should include "/onboarding/foyer"
|
||||
When I fill in the "houseName" field with "Chez Alice"
|
||||
And I click the button "Créer"
|
||||
Then the household creation request should have been made with name "Chez Alice"
|
||||
And the URL should include "/onboarding/sources"
|
||||
And I should see "Étape 3 sur 4"
|
||||
And I should see the section "Sources disponibles"
|
||||
And I should see "TheMealDB"
|
||||
When I check the checkbox "TheMealDB"
|
||||
And I click the button "Continuer"
|
||||
Then the source selection update request should have been made with source id 1
|
||||
And the URL should include "/onboarding/allergenes"
|
||||
|
||||
Scenario: Lets the household step be completed by joining an existing household instead of creating one
|
||||
Given the diets reference list is empty
|
||||
And selecting the diet will succeed
|
||||
|
|
|
|||
|
|
@ -61,3 +61,25 @@ Then(
|
|||
cy.wait("@joinHouse").its("request.body").should("deep.equal", { inviteCode: code });
|
||||
},
|
||||
);
|
||||
|
||||
// Shared between onboarding.feature's sources step and household-settings.feature's
|
||||
// sources section — both read/write the same `/house/current/sources` endpoint.
|
||||
|
||||
Given("the household's enabled sources are empty", () => {
|
||||
cy.intercept("GET", "**/house/current/sources", { statusCode: 200, body: [] });
|
||||
});
|
||||
|
||||
Given("saving the source selection will succeed", () => {
|
||||
cy.intercept("PATCH", "**/house/current/sources", (req) => {
|
||||
req.reply({ statusCode: 200, body: req.body.sourceIds });
|
||||
}).as("updateSources");
|
||||
});
|
||||
|
||||
Then(
|
||||
"the source selection update request should have been made with source id {int}",
|
||||
(id: number) => {
|
||||
cy.wait("@updateSources")
|
||||
.its("request.body")
|
||||
.should("deep.equal", { sourceIds: [id] });
|
||||
},
|
||||
);
|
||||
|
|
|
|||
|
|
@ -35,9 +35,34 @@ Given("the allergies reference list is empty", () => {
|
|||
// Every onboarding scenario that reaches the household step also reaches
|
||||
// `/onboarding/sources` right after (when a household got created/joined —
|
||||
// see `OnboardingHouseholdPage`'s `goToNextStep`), which reads this before
|
||||
// self-skipping to `/onboarding/allergenes`. No "has options" counterpart
|
||||
// yet — no source is implemented in the app itself, so there's nothing
|
||||
// real to mock a populated catalog with.
|
||||
// self-skipping to `/onboarding/allergenes` if it comes back empty.
|
||||
Given("the sources reference list is empty", () => {
|
||||
cy.intercept("GET", "**/reference/sources", { statusCode: 200, body: [] });
|
||||
});
|
||||
|
||||
// Mirrors what's actually seeded (`reference-seed-data.ts`'s
|
||||
// `registerAllRecipeSources`/`syncRecipeSources`) — one official API source
|
||||
// with an icon, one unofficial scraper without one — so the sources step
|
||||
// (onboarding and `/parametres/foyer` alike) has something real to show
|
||||
// instead of self-skipping.
|
||||
Given("the sources reference list has options", () => {
|
||||
cy.intercept("GET", "**/reference/sources", {
|
||||
statusCode: 200,
|
||||
body: [
|
||||
{
|
||||
id: 1,
|
||||
key: "theMealDb",
|
||||
name: "TheMealDB",
|
||||
official: true,
|
||||
iconUrl: "https://www.themealdb.com/images/logo.svg",
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
key: "jsonLdRecipe",
|
||||
name: "Import générique (JSON-LD)",
|
||||
official: false,
|
||||
iconUrl: null,
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
|
|
|
|||
Loading…
Reference in a new issue