Files
Cibello-app/packages/database/test/extra-recipes.test.ts
T
Claude 654174e188 feat(recept): bredda ingredienskatalogen (+25) + 13 nya recept, ratta kategorier
- Katalog 120 -> 145 ingredienser: vaniljsocker, bakpulver, bikarbonat, kakao,
  mork choklad, flor-/parlsocker, kokos, russin, blabar, hallon, paron, apelsin,
  rabarber, valnot, hasselnot, blomkal, sotpotatis, gronkal, vitkal, haricots
  verts, tonfisk, couscous, keso, kalkon. Rimliga seed_estimate-varden.
- 13 nya recept (nu mojliga tack vare katalogen): kladdkaka, chokladbollar,
  blabarssmulpaj, sockerkaka, morotskaka, vaniljbullar, fruktsallad,
  rabarberkompott, keso m. bar, tonfiskrora, couscoussallad, blomkalssoppa,
  ugnsbakad sotpotatis. Totalt 29 extrarecept.
- Rattade kategorier (din notis): risgrynsgrot -> frukost (+jul), inte efterratt;
  kanel-/kardemummabullar -> mellanmal (inte efterratt).
- Endast verifierade ingredienser + berakningsbara enheter. Invariant-testet
  kor riktiga computeRecipeNutrition pa alla 29 (grona). Typecheck grront.
2026-08-18 23:50:23 +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 29 recept", () => {
expect(EXTRA_RECIPES.length).toBeGreaterThanOrEqual(29);
});
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);
});
});