fix(recipe-generation): BRAND.name-interpolation för creatorDisplayName + rubriker/filnamn
feat(recommendation-engine,api): S4 Smak/Hälsa/Lager-vyer för 'Vad ska vi äta?' - Ersätter hårdkodat 'Cibello' i scale-batch, scale-smoke-test och export-verified. - DB-backfill: 224 recept hade 'Cibello AI' i creator_display_name (värdena motsvarar nuvarande BRAND.name, ingen rad ändrades men kontrollen är gjord). - Lägger till view-query-param (default|taste|health|pantry) med fördefinierade ScoringWeights och samtyckesgrind. - Unit-tester för vyer; integrationstester för vy-param, validering och fallback utan personalization-samtycke. - brand-guard grön; pnpm typecheck 19/19; pnpm test --force x2 grönt (34 tasks, 275 tester).
This commit is contained in:
@@ -5,14 +5,18 @@ import {
|
||||
containsForbiddenCopy,
|
||||
DEFAULT_WEIGHTS,
|
||||
easterSunday,
|
||||
HEALTH_VIEW_WEIGHTS,
|
||||
isEventActive,
|
||||
midsummerEve,
|
||||
NON_PERSONALIZED_WEIGHTS,
|
||||
parseCraving,
|
||||
PANTRY_VIEW_WEIGHTS,
|
||||
rankAll,
|
||||
renderProvenance,
|
||||
scoreCandidate,
|
||||
seasonForDate,
|
||||
TASTE_VIEW_WEIGHTS,
|
||||
viewWeights,
|
||||
type CookingAssumption,
|
||||
type RecommendationCandidate,
|
||||
type RecommendationContext,
|
||||
@@ -329,3 +333,74 @@ describe("S1 personalisering", () => {
|
||||
expect(NON_PERSONALIZED_WEIGHTS.coverage).toBe(DEFAULT_WEIGHTS.coverage);
|
||||
});
|
||||
});
|
||||
|
||||
describe("S4 rekommendationsvyer", () => {
|
||||
const viewCtx: RecommendationContext = {
|
||||
...ctx,
|
||||
personalizationEnabled: true,
|
||||
memoryItems: [],
|
||||
tasteSignals: [],
|
||||
cookingAssumptions: [],
|
||||
};
|
||||
|
||||
const tasty = candidate({
|
||||
recipeId: "tasty",
|
||||
titleSv: "Svensk köttbullsgryta",
|
||||
cuisine: "swedish",
|
||||
coverage: fullCoverage,
|
||||
nutritionPerPortion: { ...nutrition, proteinG: 20 },
|
||||
});
|
||||
|
||||
const pantry = candidate({
|
||||
recipeId: "pantry",
|
||||
titleSv: "Italiensk pastarätt",
|
||||
cuisine: "italian",
|
||||
coverage: {
|
||||
...fullCoverage,
|
||||
expiringUsed: [
|
||||
{
|
||||
canonicalIngredientId: "pasta",
|
||||
displayNameSv: "pastan",
|
||||
required: 200,
|
||||
unit: "GRAM",
|
||||
availableInUnit: 250,
|
||||
covered: true,
|
||||
optional: false,
|
||||
mostUrgentDaysLeft: 1,
|
||||
usesExpiringItem: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
nutritionPerPortion: { ...nutrition, proteinG: 15 },
|
||||
});
|
||||
|
||||
const healthy = candidate({
|
||||
recipeId: "healthy",
|
||||
titleSv: "Kyckling och quinoa",
|
||||
cuisine: "greek",
|
||||
coverage: fullCoverage,
|
||||
nutritionPerPortion: { ...nutrition, proteinG: 50 },
|
||||
});
|
||||
|
||||
it("smak-vyn höjer recept i favoritkök", () => {
|
||||
const ranked = rankAll([pantry, healthy, tasty], viewCtx, TASTE_VIEW_WEIGHTS);
|
||||
expect(ranked[0]?.recipeId).toBe("tasty");
|
||||
});
|
||||
|
||||
it("lager-vyn höjer recept som räddar varor nära utgångsdatum", () => {
|
||||
const ranked = rankAll([tasty, healthy, pantry], viewCtx, PANTRY_VIEW_WEIGHTS);
|
||||
expect(ranked[0]?.recipeId).toBe("pantry");
|
||||
});
|
||||
|
||||
it("hälsa-vyn höjer recept som matchar näringsmål", () => {
|
||||
const ranked = rankAll([tasty, pantry, healthy], viewCtx, HEALTH_VIEW_WEIGHTS);
|
||||
expect(ranked[0]?.recipeId).toBe("healthy");
|
||||
});
|
||||
|
||||
it("viewWeights returnerar rätt vikter för varje vy", () => {
|
||||
expect(viewWeights("default").taste).toBe(DEFAULT_WEIGHTS.taste);
|
||||
expect(viewWeights("taste").taste).toBe(TASTE_VIEW_WEIGHTS.taste);
|
||||
expect(viewWeights("health").nutritionFit).toBe(HEALTH_VIEW_WEIGHTS.nutritionFit);
|
||||
expect(viewWeights("pantry").coverage).toBe(PANTRY_VIEW_WEIGHTS.coverage);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user