Files
2026-07-13 13:39:12 +08:00

175 lines
6.3 KiB
TypeScript

import { describe, it } from "node:test";
import assert from "node:assert/strict";
import { applyAging } from "../../../open-sse/services/compression/progressiveAging.ts";
import type { AgingThresholds, Summarizer } from "../../../open-sse/services/compression/types.ts";
function makeMessages(
count: number,
role: string = "user",
prefix: string = "Message"
): Array<{ role: string; content: string }> {
return Array.from({ length: count }, (_, i) => ({
role: i % 2 === 0 ? "user" : "assistant",
content: `${prefix} ${i}: ${"x".repeat(100)}`,
}));
}
describe("applyAging", () => {
const defaultThresholds: AgingThresholds = { fullSummary: 5, moderate: 3, light: 2, verbatim: 2 };
it("returns empty array for empty input", () => {
const result = applyAging([]);
assert.deepEqual(result.messages, []);
assert.equal(result.saved, 0);
});
it("returns single message unchanged (verbatim tier)", () => {
const msgs = [{ role: "user", content: "Hello world" }];
const result = applyAging(msgs, defaultThresholds);
assert.equal(result.messages.length, 1);
assert.equal((result.messages[0] as { content: string }).content, "Hello world");
});
it("keeps last 2 messages as verbatim", () => {
const msgs = makeMessages(10);
const result = applyAging(msgs, defaultThresholds);
const last2 = result.messages.slice(-2);
for (const msg of last2) {
const content =
typeof (msg as { content: unknown }).content === "string"
? (msg as { content: string }).content
: "";
assert.ok(
!content.startsWith("[COMPRESSED:aging:"),
`Last message should be verbatim, got: ${content.slice(0, 40)}`
);
}
});
it("applies fullSummary tier to oldest messages", () => {
const msgs = makeMessages(10);
const result = applyAging(msgs, defaultThresholds);
const first = result.messages[0] as { content: string };
const content = typeof first.content === "string" ? first.content : "";
assert.ok(
content.startsWith("[COMPRESSED:aging:fullSummary]"),
`Expected fullSummary marker, got: ${content.slice(0, 60)}`
);
});
it("skips messages with [COMPRESSED: prefix (idempotent)", () => {
const msgs: Array<{ role: string; content: string }> = [
{ role: "assistant", content: "[COMPRESSED:aging:fullSummary] prior summary" },
{ role: "user", content: "New message" },
];
const result = applyAging(msgs, defaultThresholds);
const first = result.messages[0] as { content: string };
const content = typeof first.content === "string" ? first.content : "";
assert.ok(content.startsWith("[COMPRESSED:aging:fullSummary]"));
assert.ok(!content.includes("[COMPRESSED:aging:fullSummary][COMPRESSED:"));
});
it("uses custom thresholds", () => {
const customThresholds: AgingThresholds = {
fullSummary: 1,
moderate: 1,
light: 1,
verbatim: 1,
};
const msgs = makeMessages(6);
const result = applyAging(msgs, customThresholds);
assert.ok(result.messages.length > 0);
});
it("accepts custom summarizer", () => {
const customSummarizer: Summarizer = {
summarize: () => "[CUSTOM SUMMARY]",
};
const msgs = makeMessages(10);
const result = applyAging(msgs, defaultThresholds, customSummarizer);
const fullSummaryMsgs = result.messages.filter((m) => {
const content =
typeof (m as { content: unknown }).content === "string"
? (m as { content: string }).content
: "";
return content.includes("[CUSTOM SUMMARY]");
});
assert.ok(fullSummaryMsgs.length > 0, "Expected at least one message with custom summary");
});
it("computes saved tokens", () => {
const msgs = makeMessages(10);
const result = applyAging(msgs, defaultThresholds);
assert.ok(typeof result.saved === "number");
});
it("handles 2-message conversation (all verbatim)", () => {
const msgs = [
{ role: "user", content: "Hello" },
{ role: "assistant", content: "Hi there" },
];
const result = applyAging(msgs, defaultThresholds);
assert.equal(result.messages.length, 2);
for (const msg of result.messages) {
const content =
typeof (msg as { content: unknown }).content === "string"
? (msg as { content: string }).content
: "";
assert.ok(!content.startsWith("[COMPRESSED:"));
}
});
it("handles 5-message conversation with light tier", () => {
const msgs = makeMessages(5);
const thresholds: AgingThresholds = { fullSummary: 10, moderate: 10, light: 3, verbatim: 1 };
const result = applyAging(msgs, thresholds);
assert.ok(result.messages.length > 0);
});
it("handles messages with array content", () => {
const msgs = [{ role: "user", content: [{ type: "text", text: "Hello from array" }] }];
const result = applyAging(msgs, defaultThresholds);
assert.equal(result.messages.length, 1);
});
it("does not mutate original messages array", () => {
const original = makeMessages(5);
const originalCopy = JSON.parse(JSON.stringify(original));
applyAging(original, defaultThresholds);
assert.deepEqual(original, originalCopy);
});
it("handles all-assistant messages", () => {
const msgs = Array.from({ length: 8 }, (_, i) => ({
role: "assistant" as const,
content: `Response ${i}: ${"y".repeat(100)}`,
}));
const result = applyAging(msgs, defaultThresholds);
assert.ok(result.messages.length > 0);
});
it("handles mixed user/assistant/tool messages", () => {
const msgs = [
{ role: "user", content: "Fix the bug" },
{ role: "assistant", content: "I'll fix it" },
{ role: "tool", content: "Result: fixed" },
{ role: "assistant", content: "Done" },
];
const result = applyAging(msgs, defaultThresholds);
assert.ok(result.messages.length > 0);
});
it("idempotent — running twice produces same output", () => {
const msgs = makeMessages(10);
const first = applyAging(msgs, defaultThresholds);
const second = applyAging(first.messages, defaultThresholds);
const firstContent = JSON.stringify(
first.messages.map((m) => (m as { content: string }).content)
);
const secondContent = JSON.stringify(
second.messages.map((m) => (m as { content: string }).content)
);
assert.equal(firstContent, secondContent);
});
});