Add deleted comment regression coverage

This commit is contained in:
Dotta
2026-06-03 17:33:47 +00:00
parent 7f70759e61
commit 17fa6fe0fe
2 changed files with 94 additions and 0 deletions
@@ -9,6 +9,7 @@ import {
documentAnnotationThreads,
documentRevisions,
documents,
issueComments,
issueDocuments,
issues,
} from "@paperclipai/db";
@@ -58,6 +59,7 @@ describeEmbeddedPostgres("documentAnnotationService", () => {
await db.delete(documentRevisions);
await db.delete(issueDocuments);
await db.delete(documents);
await db.delete(issueComments);
await db.delete(issues);
await db.delete(companies);
});
@@ -180,4 +182,52 @@ describeEmbeddedPostgres("documentAnnotationService", () => {
const threads = await db.select().from(documentAnnotationThreads);
expect(threads).toHaveLength(0);
});
it("removes linked annotation comments and resolves empty threads when an issue comment is deleted", async () => {
const { companyId, issueId, document } = await createIssueWithDocument();
const [issueComment] = await db
.insert(issueComments)
.values({
companyId,
issueId,
authorType: "user",
authorUserId: "board-user",
body: "Delete this linked comment",
})
.returning();
const thread = await annotations.createThread(
issueId,
"plan",
{
baseRevisionId: document.latestRevisionId!,
baseRevisionNumber: document.latestRevisionNumber,
selector: {
quote: { exact: "selected text", prefix: "Alpha ", suffix: " omega" },
position: { normalizedStart: 6, normalizedEnd: 19, markdownStart: 6, markdownEnd: 19 },
},
body: "Linked annotation body",
issueCommentId: issueComment.id,
},
{ actorType: "user", actorId: "board-user", userId: "board-user" },
);
const cleanup = await annotations.cleanupForIssueCommentDeletion(
issueId,
issueComment.id,
{ actorType: "user", actorId: "board-user", userId: "board-user" },
);
expect(cleanup.deletedCommentIds).toEqual([thread.comments[0]!.id]);
expect(cleanup.resolvedThreadIds).toEqual([thread.id]);
await expect(
db.select().from(documentAnnotationComments).where(eq(documentAnnotationComments.id, thread.comments[0]!.id)),
).resolves.toHaveLength(0);
const [updatedThread] = await db
.select()
.from(documentAnnotationThreads)
.where(eq(documentAnnotationThreads.id, thread.id));
expect(updatedThread?.status).toBe("resolved");
expect(updatedThread?.resolvedByUserId).toBe("board-user");
});
});
@@ -1370,6 +1370,50 @@ describe("IssueChatThread", () => {
});
});
it("clears a deleted comment deep-link hash instead of highlighting it", () => {
const root = createRoot(container);
const replaceStateSpy = vi.spyOn(window.history, "replaceState").mockImplementation(() => {});
act(() => {
root.render(
<MemoryRouter initialEntries={["/issues/PAP-1#comment-comment-deleted"]}>
<IssueChatThread
comments={[{
id: "comment-deleted",
companyId: "company-1",
issueId: "issue-1",
authorAgentId: null,
authorUserId: "user-board",
body: "Sensitive deleted body",
authorType: "user",
presentation: null,
metadata: null,
deletedAt: new Date("2026-04-06T12:05:00.000Z"),
deletedByType: "user",
deletedByUserId: "user-board",
createdAt: new Date("2026-04-06T12:00:00.000Z"),
updatedAt: new Date("2026-04-06T12:05:00.000Z"),
}]}
currentUserId="user-board"
linkedRuns={[]}
timelineEvents={[]}
liveRuns={[]}
onAdd={async () => {}}
showComposer={false}
enableLiveTranscriptPolling={false}
/>
</MemoryRouter>,
);
});
expect(replaceStateSpy).toHaveBeenCalledWith(null, "", "/issues/PAP-1");
replaceStateSpy.mockRestore();
act(() => {
root.unmount();
});
});
it("shows explicit follow-up badges and event copy", () => {
const root = createRoot(container);