Files
Cibello-app/packages/database/test/extra-recipes.test.ts
T
Claude e25bdca4fa feat(recept): stor pafyllning i alla kategorier (+30 recept, totalt 59 extra)
Bred handskriven batch sa alla kategorier ar valfyllda (var extremt
middagstung forut):
- Frukost: omelett, amerikanska pannkakor, yoghurt m granola, avokadomacka,
  blabarssmoothie.
- Lunch: broccolisoppa, grekisk sallad, kycklingwrap, tomat-linssoppa,
  varm skinkmacka.
- Mellanmal: hummus, gronsaksstavar m dipp, notmix, fruktspett.
- Efterratt: chokladdoppade jordgubbar, hallonsorbet, chokladpudding,
  appelkompott, bar m vaniljyoghurt.
- Bak (sub-typer): vitt formbrod + scones (bread), havreflarn (cookie),
  blabarsmuffins + chokladtarta (cake), rabarberpaj (pie).
- Middag: ugnsbakad lax, kremig kycklinggryta, pastagratang, fiskgryta m kokos,
  korv-potatisgratang.

Alla med verifierade ingredienser + berakningsbara enheter + ratt kategori.
Ravaror med genomstekning/genomkokning har explicit matsakerhetssteg.
Invariant-testet kor riktig computeRecipeNutrition pa alla 59 (grona).
Typecheck gront.
2026-08-19 01:00:29 +00:00

85 lines
3.2 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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 59 recept", () => {
expect(EXTRA_RECIPES.length).toBeGreaterThanOrEqual(59);
});
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);
});
});