evOo-local/packages/memory/src/index.test.ts

112 lines
3.8 KiB
TypeScript

import type { AdapterContext } from "@rakazo/adapter-kit";
import { describe, expect, it, vi } from "vitest";
import { appendMarkdown, MarkdownMemoryStore, rankMemoryDocuments } from "./index.js";
const context: AdapterContext = {
operationId: "read-memory",
traceId: "read-memory",
spaceId: "workspace-1",
userId: "user-1",
signal: new AbortController().signal,
};
describe("memory store contract shape", () => {
it("declares markdown portability", () => {
const store = new MarkdownMemoryStore({} as never);
expect(store.describe().capabilities.markdownPortable).toBe(true);
});
it("reads the most recently updated documents first", async () => {
const updatedAt = new Date("2026-08-16T10:00:00.000Z");
const findMany = vi
.fn()
.mockResolvedValue([
{ id: "memory-1", path: "facts.md", content: "A fact", revision: 3, updatedAt },
]);
const store = new MarkdownMemoryStore({ memoryDocument: { findMany } } as never);
await expect(store.read({ scope: "bot", botId: "bot-1" }, context)).resolves.toEqual({
documents: [
{
id: "memory-1",
path: "facts.md",
content: "A fact",
revision: 3,
updatedAt: updatedAt.toISOString(),
},
],
});
expect(findMany).toHaveBeenCalledWith({
where: {
spaceId: "workspace-1",
userId: "user-1",
scope: "bot",
botId: "bot-1",
},
orderBy: [{ updatedAt: "desc" }, { path: "asc" }],
});
});
});
describe("appendMarkdown", () => {
it("adds the note below a heading or paragraph, and right below a list", () => {
expect(appendMarkdown("# Chief\n\n", "- 2026-10-03: Prefers tea")).toBe(
"# Chief\n\n- 2026-10-03: Prefers tea\n",
);
expect(appendMarkdown("- a\n- b", "- c")).toBe("- a\n- b\n- c\n");
expect(appendMarkdown("- a\n more about a\n", "- c")).toBe("- a\n more about a\n- c\n");
expect(appendMarkdown("", "- first")).toBe("- first\n");
});
it("leaves the document unchanged for an empty note", () => {
expect(appendMarkdown("# Chief\n", " ")).toBe("# Chief\n");
});
it("deduplicates an identical single-line note when appended repeatedly", () => {
const existing = "# Chief\n\n- 2026-10-03: Prefers tea\n";
expect(appendMarkdown(existing, "- 2026-10-03: Prefers tea")).toBe(existing);
});
});
describe("rankMemoryDocuments & search", () => {
it("ranks path and heading matches above body-only or partial token matches", async () => {
const docs = [
{
id: "1",
path: "notes/general.md",
content: "We discussed the deployment pipeline briefly.",
revision: 1,
updatedAt: new Date("2026-10-01T00:00:00.000Z"),
},
{
id: "2",
path: "ops/deployment-guide.md",
content: "# Deployment Guide\n\nRun the production deployment script with rollback checks.",
revision: 2,
updatedAt: new Date("2026-10-02T00:00:00.000Z"),
},
{
id: "3",
path: "notes/random.md",
content: "Unrelated lunch menu.",
revision: 1,
updatedAt: new Date("2026-10-03T00:00:00.000Z"),
},
];
const findMany = vi.fn().mockResolvedValue(docs);
const store = new MarkdownMemoryStore({ memoryDocument: { findMany } } as never);
const results = await store.search({ scope: "user", query: "deployment rollback" }, context);
expect(results.map((r) => r.path)).toEqual(["ops/deployment-guide.md", "notes/general.md"]);
expect(results[0]!.score).toBe(1);
expect(results[1]!.score).toBeLessThan(1);
expect(results[0]!.snippet).toContain("rollback");
});
it("returns empty array when no tokens match", () => {
expect(
rankMemoryDocuments([{ path: "a.md", content: "alpha beta" }], "nonexistent token"),
).toEqual([]);
});
});