import { describe, it, expect } from "vitest"; import { emptyArtifact, type Provenance } from "../src/model"; import { addThread, appendMessage, setStatus } from "../src/threadModel"; const BEN: Provenance = { kind: "human", id: "ben" }; const fp = { text: "anchored", before: "a", after: "b", lineHint: 3 }; describe("addThread", () => { it("adds an anchor + an open thread with the first message and returns their ids", () => { const a = emptyArtifact("docs/x.md"); const { threadId, anchorId } = addThread(a, fp, { author: BEN, body: "first" }); expect(Object.keys(a.anchors)).toContain(anchorId); expect(a.anchors[anchorId].fingerprint).toEqual(fp); const t = a.threads.find((x) => x.id === threadId)!; expect(t.anchorId).toBe(anchorId); expect(t.status).toBe("open"); expect(t.messages).toHaveLength(1); expect(t.messages[0].body).toBe("first"); expect(t.messages[0].author).toEqual(BEN); expect(t.messages[0].id).toMatch(/^m_/); expect(t.messages[0].createdAt).toMatch(/^\d{4}-\d{2}-\d{2}T/); }); }); describe("appendMessage", () => { it("appends a message to the named thread", () => { const a = emptyArtifact("docs/x.md"); const { threadId } = addThread(a, fp, { author: BEN, body: "first" }); appendMessage(a, threadId, { author: BEN, body: "reply" }); const t = a.threads.find((x) => x.id === threadId)!; expect(t.messages.map((m) => m.body)).toEqual(["first", "reply"]); }); it("throws for an unknown thread id", () => { const a = emptyArtifact("docs/x.md"); expect(() => appendMessage(a, "nope", { author: BEN, body: "x" })).toThrow(/nope/); }); }); describe("setStatus", () => { it("flips a thread between open and resolved", () => { const a = emptyArtifact("docs/x.md"); const { threadId } = addThread(a, fp, { author: BEN, body: "first" }); setStatus(a, threadId, "resolved"); expect(a.threads[0].status).toBe("resolved"); setStatus(a, threadId, "open"); expect(a.threads[0].status).toBe("open"); }); });