Merge pull request #42 from kyuno053/fix/runtime-seed-source-sync
fix(recipes): synchronise les sources en base au démarrage de l'image de prod
This commit is contained in:
commit
f58ac82a07
7 changed files with 133 additions and 4 deletions
|
|
@ -6,6 +6,12 @@
|
||||||
"runtimeExecutable": "pnpm",
|
"runtimeExecutable": "pnpm",
|
||||||
"runtimeArgs": ["--filter", "web", "dev"],
|
"runtimeArgs": ["--filter", "web", "dev"],
|
||||||
"port": 5173
|
"port": 5173
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "api",
|
||||||
|
"runtimeExecutable": "pnpm",
|
||||||
|
"runtimeArgs": ["--filter", "api", "dev"],
|
||||||
|
"port": 3000
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
import { prisma } from "../db/prisma.js";
|
import { prisma } from "../db/prisma.js";
|
||||||
|
import { syncRecipeSources } from "../db/recipe-source-sync.js";
|
||||||
import { seedReferenceData } from "../db/reference-seed-data.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
|
* 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
|
* alongside everything else, and it runs under plain `node`, no tsx
|
||||||
* needed at runtime.
|
* 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
|
* 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.
|
* already has this data is a no-op.
|
||||||
*/
|
*/
|
||||||
|
registerAllRecipeSources();
|
||||||
|
|
||||||
seedReferenceData(prisma)
|
seedReferenceData(prisma)
|
||||||
|
.then(() => syncRecipeSources(prisma))
|
||||||
.then(() => prisma.$disconnect())
|
.then(() => prisma.$disconnect())
|
||||||
.catch(async (err) => {
|
.catch(async (err) => {
|
||||||
console.error(err);
|
console.error(err);
|
||||||
|
|
|
||||||
|
|
@ -53,6 +53,33 @@ Feature: Household settings
|
||||||
Then the household deletion request should have been made
|
Then the household deletion request should have been made
|
||||||
And I should see "Créer un foyer"
|
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"
|
||||||
|
And I scroll to the section "Sources disponibles"
|
||||||
|
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
|
Scenario: Leaves the household
|
||||||
Given I am signed in as "Bob" "Dupont"
|
Given I am signed in as "Bob" "Dupont"
|
||||||
And my user id is 2
|
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
|
Then the allergies update request should have been made with no allergy ids
|
||||||
And the URL should be the home page
|
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
|
Scenario: Lets the household step be completed by joining an existing household instead of creating one
|
||||||
Given the diets reference list is empty
|
Given the diets reference list is empty
|
||||||
And selecting the diet will succeed
|
And selecting the diet will succeed
|
||||||
|
|
|
||||||
|
|
@ -125,6 +125,14 @@ Then("I should see the section {string}", (legend: string) => {
|
||||||
cy.contains("legend", legend).should("be.visible");
|
cy.contains("legend", legend).should("be.visible");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Needed for a section that can render below the fold of `.app-content`'s
|
||||||
|
// own scroll (see layout.cy.ts) — a bare `.should("be.visible")` doesn't
|
||||||
|
// auto-scroll (only interaction commands like `.click()`/`.check()` do),
|
||||||
|
// so a section low on a long page needs this before asserting on it.
|
||||||
|
When("I scroll to the section {string}", (legend: string) => {
|
||||||
|
cy.contains("legend", legend).scrollIntoView();
|
||||||
|
});
|
||||||
|
|
||||||
Then("the checkbox {string} should be checked", (label: string) => {
|
Then("the checkbox {string} should be checked", (label: string) => {
|
||||||
cy.contains("label", label).find("input[type=checkbox]").should("be.checked");
|
cy.contains("label", label).find("input[type=checkbox]").should("be.checked");
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -61,3 +61,25 @@ Then(
|
||||||
cy.wait("@joinHouse").its("request.body").should("deep.equal", { inviteCode: code });
|
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
|
// Every onboarding scenario that reaches the household step also reaches
|
||||||
// `/onboarding/sources` right after (when a household got created/joined —
|
// `/onboarding/sources` right after (when a household got created/joined —
|
||||||
// see `OnboardingHouseholdPage`'s `goToNextStep`), which reads this before
|
// see `OnboardingHouseholdPage`'s `goToNextStep`), which reads this before
|
||||||
// self-skipping to `/onboarding/allergenes`. No "has options" counterpart
|
// self-skipping to `/onboarding/allergenes` if it comes back empty.
|
||||||
// yet — no source is implemented in the app itself, so there's nothing
|
|
||||||
// real to mock a populated catalog with.
|
|
||||||
Given("the sources reference list is empty", () => {
|
Given("the sources reference list is empty", () => {
|
||||||
cy.intercept("GET", "**/reference/sources", { statusCode: 200, body: [] });
|
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