You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

377 lines
10 KiB

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);
});
});