d267effd6c
Ratta slug-bugg: recept-taggen "grillsäsong" matchade aldrig event-sluggen "grillsasong" (ASCII) sa grillsäsong-knappen visade inget. Nu ratt. 15 nya hogtidsrecept taggade med exakt SE-event-slug: rodbetssallad, rodkal, senapssill, stekt prinskorv, kokta kraftor med krondill, aioli, ostbricka, farskpotatis med dill, agghalvor med rakror, rakcocktail, grillspett med halloumi, grillad kycklingfile, pastasallad i matladan, tillbehor till surstromming, kryddig kyckling med ris. Omtaggar befintliga recept till ratt hogtid: vasterbottensostpaj +kraftskiva, grekisk-sallad +grillsasong, chili/spaghetti +skolstart, chokladtarta +alla-hjartans-dag, tomatsoppa +halloween, korvgratang +oktoberfest, halloumiburgare +valborg. Alla 14 SE-hogtider har nu minst ett korrekt taggat recept. Naring och enheter ar berakningsbara for alla 74 extrarecept (invarianttest gront, repo-typecheck gront).
85 lines
3.2 KiB
TypeScript
85 lines
3.2 KiB
TypeScript
import { describe, expect, it } from "vitest";
|
||
import { computeRecipeNutrition, type IngredientNutritionSource } from "@app/nutrition-engine";
|
||
import { SEED_INGREDIENTS } from "../src/seed/data/ingredients.js";
|
||
import { EXTRA_RECIPES } from "../src/seed/data/extra-recipes.js";
|
||
import { VERIFIED_RECIPES } from "../src/seed/data/verified-recipes.js";
|
||
|
||
/**
|
||
* Rent invariant-test (ingen DB) för de nya extrarecepten. Speglar exakt de
|
||
* kontroller som seed-körningen gör (run.ts rad 675 + 691) så att ett trasigt
|
||
* recept fångas här i stället för mitt i en seed-körning:
|
||
* - varje ingrediens-id måste finnas i katalogen,
|
||
* - näringen måste gå att beräkna för varje ingrediens (rätt enhet),
|
||
* - inga slug-krockar med den befintliga katalogen.
|
||
*/
|
||
|
||
const byId = new Map(SEED_INGREDIENTS.map((i) => [i.id, i]));
|
||
|
||
function sourcesFor(
|
||
recipe: (typeof EXTRA_RECIPES)[number],
|
||
): Map<string, IngredientNutritionSource> {
|
||
const sources = new Map<string, IngredientNutritionSource>();
|
||
for (const ri of recipe.ingredients) {
|
||
const info = byId.get(ri.ing);
|
||
if (!info) continue; // fångas separat av id-testet nedan
|
||
sources.set(ri.ing, {
|
||
nutritionPer100: info.nutritionPer100,
|
||
densityGPerMl: info.densityGPerMl ?? null,
|
||
gramsPerPiece: info.gramsPerPiece ?? null,
|
||
});
|
||
}
|
||
return sources;
|
||
}
|
||
|
||
describe("extrarecept – seed-invarianter", () => {
|
||
it("har minst 74 recept", () => {
|
||
expect(EXTRA_RECIPES.length).toBeGreaterThanOrEqual(74);
|
||
});
|
||
|
||
it("alla ingrediens-id:n finns i katalogen (run.ts rad 675)", () => {
|
||
const unknown: string[] = [];
|
||
for (const recipe of EXTRA_RECIPES) {
|
||
for (const ri of recipe.ingredients) {
|
||
if (!byId.has(ri.ing)) unknown.push(`${recipe.slug}: ${ri.ing}`);
|
||
}
|
||
}
|
||
expect(unknown).toEqual([]);
|
||
});
|
||
|
||
it("näringen går att beräkna för varje recept (run.ts rad 691)", () => {
|
||
const failed: string[] = [];
|
||
for (const recipe of EXTRA_RECIPES) {
|
||
const calc = recipe.ingredients.map((ri) => ({
|
||
canonicalIngredientId: ri.ing,
|
||
quantity: ri.qty,
|
||
unit: ri.unit,
|
||
optional: ri.optional ?? false,
|
||
}));
|
||
const nutrition = computeRecipeNutrition(calc, recipe.portions, sourcesFor(recipe));
|
||
if (nutrition.uncomputableIngredientIds.length > 0) {
|
||
failed.push(`${recipe.slug}: ${nutrition.uncomputableIngredientIds.join(", ")}`);
|
||
}
|
||
// Rimlig näring per portion (fångar grova enhetsfel).
|
||
expect(nutrition.perPortion.kcal).toBeGreaterThan(0);
|
||
}
|
||
expect(failed).toEqual([]);
|
||
});
|
||
|
||
it("inga slug-krockar med befintliga recept och inga interna dubbletter", () => {
|
||
const existing = new Set(VERIFIED_RECIPES.map((r) => r.slug));
|
||
const seen = new Set<string>();
|
||
const collisions: string[] = [];
|
||
for (const recipe of EXTRA_RECIPES) {
|
||
if (existing.has(recipe.slug)) collisions.push(`krock: ${recipe.slug}`);
|
||
if (seen.has(recipe.slug)) collisions.push(`dubblett: ${recipe.slug}`);
|
||
seen.add(recipe.slug);
|
||
}
|
||
expect(collisions).toEqual([]);
|
||
});
|
||
|
||
it("varje bak-recept är taggat 'baking' (driver Baka-kategorin)", () => {
|
||
const baking = EXTRA_RECIPES.filter((r) => r.tags.includes("baking"));
|
||
expect(baking.length).toBeGreaterThanOrEqual(4);
|
||
});
|
||
});
|