import { DatabaseSync } from "node:sqlite"; import { test } from "node:test"; import assert from "node:assert/strict"; import { Timeline } from "../src/timeline.js"; test("timeline is idempotent, indexed across old history, bounded and expandable", () => { const db = new DatabaseSync(":memory:"); const timeline = new Timeline(db); try { timeline.append({ id: "old", kind: "user", at: 1, text: "财务报销会议" + "材料".repeat(1000), source: { channel: "feishu", sender: "A" } }); timeline.append({ id: "old", kind: "user", at: 2, text: "overwrite", source: {} }); for (let i = 0; i < 250; i++) timeline.append({ id: `new-${i}`, kind: "assistant", at: i + 2, text: "other", source: {} }); const result = timeline.query({ query: "报销", limit: 5 }); assert.equal(result.entries[0].id, "old"); assert.equal(result.embedding, false); assert.equal(result.entries[0].expandable, true); assert.equal(timeline.read({ id: "old" }).at, 1); assert.equal(timeline.read({ id: "old", offset: 0, limit: 2 }).text, "财务"); assert.equal(timeline.query({ from: 2, to: 2 }).entries.length, 1); assert.throws(() => timeline.query({ limit: 999 })); timeline.append({ id: "tail", kind: "user", at: 500, text: "padding ".repeat(2500) + "uniqueTail", source: {} }); assert.equal(timeline.query({ query: "uniqueTail" }).entries[0].id, "tail"); } finally { db.close(); } }); test("timeline excludes secrets and rejects unbounded data", () => { const db = new DatabaseSync(":memory:"); const timeline = new Timeline(db); try { timeline.append({ id: "secret", kind: "user", at: 1, text: "password=example", source: {} }); assert.equal(timeline.read({ id: "secret" }).text, "[敏感内容未写入记忆]"); assert.throws(() => timeline.append({ id: "large", kind: "user", at: 1, text: "x".repeat(32001) })); } finally { db.close(); } });