import { describe, expect, test, beforeAll, afterAll } from "bun:test"; import { dbGlobal } from "drizzle-pkg/lib/db"; import { agentDocuments } from "drizzle-pkg/lib/schema/agent-document"; import { eq } from "drizzle-orm"; import { createDocument, getDocumentById, listDocuments, updateDocument, deleteDocument, generateSummary, deserializeTags, serializeTags, } from "./index"; const TEST_USER_A = 9999001; const TEST_USER_B = 9999002; async function cleanup() { await dbGlobal .delete(agentDocuments) .where(eq(agentDocuments.userId, TEST_USER_A)); await dbGlobal .delete(agentDocuments) .where(eq(agentDocuments.userId, TEST_USER_B)); } beforeAll(async () => { await cleanup(); }); afterAll(async () => { await cleanup(); }); describe("generateSummary", () => { test("returns null when content is short", () => { expect(generateSummary("short content")).toBeNull(); }); test("returns truncated summary when content exceeds maxChars", () => { const long = "a".repeat(300); const summary = generateSummary(long, 200); expect(summary).toBe("a".repeat(200) + "..."); }); test("respects custom maxChars", () => { const long = "a".repeat(100); const summary = generateSummary(long, 50); expect(summary).toBe("a".repeat(50) + "..."); }); }); describe("serializeTags / deserializeTags", () => { test("serialize null returns null", () => { expect(serializeTags(null)).toBeNull(); }); test("serialize empty array returns null", () => { expect(serializeTags([])).toBeNull(); }); test("serialize and deserialize roundtrip", () => { const tags = ["bug", "feature", "urgent"]; const serialized = serializeTags(tags); expect(serialized).toBe(JSON.stringify(tags)); expect(deserializeTags(serialized)).toEqual(tags); }); test("deserialize null returns empty array", () => { expect(deserializeTags(null)).toEqual([]); }); test("deserialize invalid JSON returns empty array", () => { expect(deserializeTags("not json")).toEqual([]); }); }); describe("createDocument", () => { test("creates document with all fields", async () => { const doc = await createDocument({ userId: TEST_USER_A, type: "memory", title: "Test Memory", content: "This is test memory content", tags: ["test", "memory"], }); expect(doc.id).toBeDefined(); expect(doc.userId).toBe(TEST_USER_A); expect(doc.type).toBe("memory"); expect(doc.title).toBe("Test Memory"); expect(doc.content).toBe("This is test memory content"); expect(doc.tags).toBe(JSON.stringify(["test", "memory"])); expect(doc.createdAt).toBeDefined(); expect(doc.updatedAt).toBeDefined(); }); test("creates document with default type 'document'", async () => { const doc = await createDocument({ userId: TEST_USER_A, title: "Default Type Doc", content: "content", }); expect(doc.type).toBe("document"); }); test("auto-generates summary for long content", async () => { const longContent = "x".repeat(300); const doc = await createDocument({ userId: TEST_USER_A, title: "Long Content Doc", content: longContent, }); expect(doc.summary).toBe("x".repeat(200) + "..."); }); test("summary is null for short content", async () => { const doc = await createDocument({ userId: TEST_USER_A, title: "Short Content Doc", content: "short", }); expect(doc.summary).toBeNull(); }); test("stores sessionId when provided", async () => { const doc = await createDocument({ userId: TEST_USER_A, sessionId: "session-123", title: "With Session", content: "content", }); expect(doc.sessionId).toBe("session-123"); }); }); describe("getDocumentById", () => { test("returns document for correct user", async () => { const created = await createDocument({ userId: TEST_USER_A, title: "Get Test", content: "content", }); const fetched = await getDocumentById(created.id, TEST_USER_A); expect(fetched).not.toBeNull(); expect(fetched!.title).toBe("Get Test"); }); test("returns null for wrong user (permission isolation)", async () => { const created = await createDocument({ userId: TEST_USER_A, title: "Isolation Test", content: "content", }); const fetched = await getDocumentById(created.id, TEST_USER_B); expect(fetched).toBeNull(); }); test("returns null for non-existent id", async () => { const fetched = await getDocumentById(99999999, TEST_USER_A); expect(fetched).toBeNull(); }); }); describe("listDocuments", () => { test("returns documents for user only", async () => { await createDocument({ userId: TEST_USER_A, title: "List A1", content: "content1", }); await createDocument({ userId: TEST_USER_B, title: "List B1", content: "content2", }); const result = await listDocuments({ userId: TEST_USER_A }); expect(result.items.every((item) => item.userId === TEST_USER_A)).toBe(true); expect(result.items.some((item) => item.title === "List A1")).toBe(true); expect(result.items.some((item) => item.title === "List B1")).toBe(false); }); test("list results exclude content field", async () => { const result = await listDocuments({ userId: TEST_USER_A }); expect(result.items.length).toBeGreaterThan(0); for (const item of result.items) { expect(item).not.toHaveProperty("content"); } }); test("filters by type", async () => { await createDocument({ userId: TEST_USER_A, type: "memory", title: "Memory Type Filter", content: "content", }); const result = await listDocuments({ userId: TEST_USER_A, type: "memory" }); expect(result.items.every((item) => item.type === "memory")).toBe(true); }); test("keyword search matches title", async () => { await createDocument({ userId: TEST_USER_A, title: "UniqueKeywordDoc", content: "some content", }); const result = await listDocuments({ userId: TEST_USER_A, keyword: "UniqueKeyword", }); expect(result.items.some((item) => item.title === "UniqueKeywordDoc")).toBe(true); }); test("keyword search matches content", async () => { await createDocument({ userId: TEST_USER_A, title: "Content Search Title", content: "VerySpecialContentToken12345", }); const result = await listDocuments({ userId: TEST_USER_A, keyword: "VerySpecialContentToken", }); expect(result.items.some((item) => item.title === "Content Search Title")).toBe(true); }); test("pagination works correctly", async () => { const result = await listDocuments({ userId: TEST_USER_A, page: 1, pageSize: 2, }); expect(result.items.length).toBeLessThanOrEqual(2); expect(result.page).toBe(1); expect(result.pageSize).toBe(2); expect(typeof result.total).toBe("number"); expect(typeof result.hasMore).toBe("boolean"); }); test("returns correct total count", async () => { const result = await listDocuments({ userId: TEST_USER_A }); expect(result.total).toBe(result.items.length + (result.hasMore ? result.pageSize * (result.page - 1) : 0)); }); }); describe("updateDocument", () => { test("updates title successfully", async () => { const created = await createDocument({ userId: TEST_USER_A, title: "Before Update", content: "content", }); const updated = await updateDocument(created.id, TEST_USER_A, { title: "After Update", }); expect(updated).not.toBeNull(); expect(updated!.title).toBe("After Update"); }); test("updates content and regenerates summary", async () => { const created = await createDocument({ userId: TEST_USER_A, title: "Content Update Test", content: "short", }); expect(created.summary).toBeNull(); const longContent = "y".repeat(300); const updated = await updateDocument(created.id, TEST_USER_A, { content: longContent, }); expect(updated).not.toBeNull(); expect(updated!.content).toBe(longContent); expect(updated!.summary).toBe("y".repeat(200) + "..."); }); test("returns null for wrong user", async () => { const created = await createDocument({ userId: TEST_USER_A, title: "Update Isolation", content: "content", }); const updated = await updateDocument(created.id, TEST_USER_B, { title: "Hacked", }); expect(updated).toBeNull(); }); test("returns null for non-existent document", async () => { const updated = await updateDocument(99999999, TEST_USER_A, { title: "Nope", }); expect(updated).toBeNull(); }); test("updates tags", async () => { const created = await createDocument({ userId: TEST_USER_A, title: "Tags Update", content: "content", }); const updated = await updateDocument(created.id, TEST_USER_A, { tags: ["new-tag", "updated"], }); expect(updated).not.toBeNull(); expect(updated!.tags).toBe(JSON.stringify(["new-tag", "updated"])); }); test("no-op update returns existing document", async () => { const created = await createDocument({ userId: TEST_USER_A, title: "Noop Test", content: "content", }); const updated = await updateDocument(created.id, TEST_USER_A, {}); expect(updated).not.toBeNull(); expect(updated!.title).toBe("Noop Test"); }); }); describe("deleteDocument", () => { test("deletes document for correct user", async () => { const created = await createDocument({ userId: TEST_USER_A, title: "Delete Me", content: "content", }); const deleted = await deleteDocument(created.id, TEST_USER_A); expect(deleted).toBe(true); const fetched = await getDocumentById(created.id, TEST_USER_A); expect(fetched).toBeNull(); }); test("returns false for wrong user", async () => { const created = await createDocument({ userId: TEST_USER_A, title: "Delete Isolation", content: "content", }); const deleted = await deleteDocument(created.id, TEST_USER_B); expect(deleted).toBe(false); const fetched = await getDocumentById(created.id, TEST_USER_A); expect(fetched).not.toBeNull(); }); test("returns false for non-existent document", async () => { const deleted = await deleteDocument(99999999, TEST_USER_A); expect(deleted).toBe(false); }); });