import { describe, expect, it } from "vitest"; import { buildMemoryOverview, deriveMemoryUpdates, renderMemorySummary } from "../src/index.js"; import type { AamosClient, AamosResult } from "@app/ai-contracts"; import type { AamosTaskType } from "@app/ai-contracts"; import type { MemoryItem } from "@app/shared-types"; function makeItem(overrides: Partial = {}): MemoryItem { return { id: "m1", userId: "u1", kind: "structured_fact", key: "favorite_cuisine_swedish", summarySv: "Gillar svensk mat", value: { favoriteCuisine: "swedish" }, origin: "user_stated", confidence: 1, verifiedByUser: true, paused: false, createdAt: "2026-08-01T00:00:00.000Z", updatedAt: "2026-08-01T00:00:00.000Z", ...overrides, }; } describe("buildMemoryOverview", () => { it("grupperar minnen efter kind", () => { const items: MemoryItem[] = [ makeItem({ id: "a", kind: "structured_fact", summarySv: "Fakta A" }), makeItem({ id: "b", kind: "recipe_memory", summarySv: "Recept B" }), makeItem({ id: "c", kind: "structured_fact", summarySv: "Fakta C" }), ]; const overview = buildMemoryOverview(items, "sv-SE"); const kinds = overview.sections.map((s) => s.kind); expect(kinds).toContain("structured_fact"); expect(kinds).toContain("recipe_memory"); const factSection = overview.sections.find((s) => s.kind === "structured_fact")!; expect(factSection.items).toHaveLength(2); }); it("visar origin och confidence tydligt", () => { const items: MemoryItem[] = [ makeItem({ origin: "observed", confidence: 0.75 }), ]; const overview = buildMemoryOverview(items, "sv-SE"); const item = overview.sections[0]!.items[0]!; expect(item.originLabel).toBe("Vi har sett"); expect(item.confidencePercent).toBe(75); }); it("markerar pausade poster synligt", () => { const items: MemoryItem[] = [makeItem({ paused: true })]; const overview = buildMemoryOverview(items, "sv-SE"); const item = overview.sections[0]!.items[0]!; expect(item.pausedLabel).toBe("Pausad"); expect(overview.pausedCount).toBe(1); }); it("markerar ai_inferred som gissning", () => { const items: MemoryItem[] = [makeItem({ origin: "ai_inferred", confidence: 0.5 })]; const overview = buildMemoryOverview(items, "sv-SE"); const item = overview.sections[0]!.items[0]!; expect(item.originLabel).toBe("Gissning"); expect(item.guessLabel).toBe("Detta är en gissning – bekräfta eller ändra om det stämmer."); }); it("sorterar senast uppdaterade överst", () => { const items: MemoryItem[] = [ makeItem({ id: "old", updatedAt: "2026-08-01T00:00:00.000Z" }), makeItem({ id: "new", updatedAt: "2026-08-10T00:00:00.000Z" }), ]; const overview = buildMemoryOverview(items, "sv-SE"); expect(overview.sections[0]!.items[0]!.id).toBe("new"); expect(overview.sections[0]!.items[1]!.id).toBe("old"); }); it("stödjer engelska texter", () => { const items: MemoryItem[] = [makeItem({ origin: "user_stated", paused: true })]; const overview = buildMemoryOverview(items, "en-US"); const item = overview.sections[0]!.items[0]!; expect(item.originLabel).toBe("You said"); expect(item.pausedLabel).toBe("Paused"); expect(item.summary).toBe("Favorite cuisine: swedish"); }); it("renderMemorySummary stödjer S5-value-former", () => { expect(renderMemorySummary({ summarySv: "", value: { favoriteCuisine: "thai" } }, "en-US")).toBe("Favorite cuisine: thai"); expect(renderMemorySummary({ summarySv: "", value: { avoidIngredientId: "broccoli" } }, "en-US")).toBe("Avoids ingredient: broccoli"); expect(renderMemorySummary({ summarySv: "", value: { goal: "less_waste" } }, "en-US")).toBe("Goal: less_waste"); expect(renderMemorySummary({ summarySv: "", value: { primaryGoal: "less_waste" } }, "en-US")).toBe("Primary goal: less_waste"); expect(renderMemorySummary({ summarySv: "", value: { allergen: "gluten" } }, "en-US")).toBe("Allergy: gluten"); expect(renderMemorySummary({ summarySv: "", value: { spiceLevelMax: 2 } }, "en-US")).toBe("Max spice level: 2"); }); }); describe("deriveMemoryUpdates", () => { function fakeAamos(output: unknown): AamosClient { return { async runTask(): Promise> { return { status: "ok", output: output as AamosResult["output"], modelVersion: "fake", promptVersion: "fake", latencyMs: 10, costUsd: 0, inputTokens: 0, outputTokens: 0, }; }, async healthCheck() { return { ok: true }; }, }; } it("avvisar fabricerade minnen utan event-stöd", async () => { const aamos = fakeAamos({ memoryUpdates: [ { key: "fabricated", kind: "structured_fact", summarySv: "Påhittad favoriträtt", value: { recipeId: "ghost" }, origin: "ai_inferred", confidence: 0.6, expiresAt: null, sourceEventIds: ["event-does-not-exist"], }, ], }); const result = await deriveMemoryUpdates(aamos, { scope: "user", scopeId: "u1", events: [{ id: "evt-real", type: "recipe_cooked", occurredAt: "2026-08-01T12:00:00Z", payload: {} }], existingMemoryKeys: [], consentFlags: { personalization: true, anonymizedImprovement: false, imageTraining: false }, }); expect(result.proposals).toHaveLength(0); }); it("behåller grundade minnen med kända event-id", async () => { const aamos = fakeAamos({ memoryUpdates: [ { key: "grounded", kind: "structured_fact", summarySv: "Ggrundat minne", value: { recipeId: "r1" }, origin: "observed", confidence: 0.8, expiresAt: null, sourceEventIds: ["evt-real"], }, ], }); const result = await deriveMemoryUpdates(aamos, { scope: "user", scopeId: "u1", events: [{ id: "evt-real", type: "recipe_cooked", occurredAt: "2026-08-01T12:00:00Z", payload: {} }], existingMemoryKeys: [], consentFlags: { personalization: true, anonymizedImprovement: false, imageTraining: false }, }); expect(result.proposals).toHaveLength(1); expect(result.proposals[0]!.key).toBe("grounded"); }); });