Files
Cibello-app/packages/memory-client/test/overview.test.ts
T
Sven (AAMOS AI) c32a7e33c7
CI / Typecheck, test & build (push) Failing after 2s
ci: trigga på master + formatfix inför Gitea Actions
2026-08-13 17:25:14 +07:00

181 lines
6.4 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 { 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> = {}): 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<T extends AamosTaskType>(): Promise<AamosResult<T>> {
return {
status: "ok",
output: output as AamosResult<T>["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");
});
});