Clean external references for tombstoned comments

This commit is contained in:
Dotta committed 2026-06-23 01:12:39 +00:00
1 parent 68efc373a2
commit 04b3c6483b
3 files changed
+33 -3

No files matched your search

@@ -57,6 +57,24 @@ const mockIssueReferenceService = vi.hoisted(() => ({
syncDocument: vi.fn(async () => undefined),
syncIssue: vi.fn(async () => undefined),
}));
const mockExternalObjectService = vi.hoisted(() => ({
getIssueSummaries: vi.fn(async () => ({ summaries: {} })),
getIssueSummary: vi.fn(async () => ({
authRequiredCount: 0,
byLiveness: {},
byStatusCategory: {},
highestSeverity: "muted",
objects: [],
staleCount: 0,
total: 0,
unreachableCount: 0,
})),
listForIssue: vi.fn(async () => []),
refreshIssueObjects: vi.fn(async () => []),
syncCommentSafely: vi.fn(async () => undefined),
syncDocumentSafely: vi.fn(async () => undefined),
syncIssueSafely: vi.fn(async () => undefined),
}));
function registerModuleMocks() {
vi.doMock("@paperclipai/shared/telemetry", () => ({
@@ -92,6 +110,10 @@ function registerModuleMocks() {
issueService: () => mockIssueService,
}));
vi.doMock("../services/external-objects.js", () => ({
externalObjectService: () => mockExternalObjectService,
}));
vi.doMock("../services/index.js", () => ({
companyService: () => ({
getById: vi.fn(async () => ({ id: "company-1", attachmentMaxBytes: 10 * 1024 * 1024 })),
@@ -181,6 +203,7 @@ describe.sequential("issue comment cancel routes", () => {
vi.doUnmock("../telemetry.js");
vi.doUnmock("../services/access.js");
vi.doUnmock("../services/activity-log.js");
vi.doUnmock("../services/external-objects.js");
vi.doUnmock("../services/feedback.js");
vi.doUnmock("../services/heartbeat.js");
vi.doUnmock("../services/index.js");
@@ -240,6 +263,7 @@ describe.sequential("issue comment cancel routes", () => {
});
mockIssueReferenceService.deleteCommentSource.mockResolvedValue(undefined);
mockIssueReferenceService.syncComment.mockResolvedValue(undefined);
mockExternalObjectService.syncCommentSafely.mockResolvedValue(undefined);
});
it("cancels a queued comment from its author and restores the deleted body", async () => {
@@ -352,6 +376,7 @@ describe.sequential("issue comment cancel routes", () => {
expect.objectContaining({ afterTombstone: expect.any(Function) }),
);
expect(mockIssueReferenceService.syncComment).toHaveBeenCalledWith("comment-1", "tx");
expect(mockExternalObjectService.syncCommentSafely).toHaveBeenCalledWith("comment-1", "tx");
expect(mockDocumentAnnotationService.cleanupForIssueCommentDeletion).toHaveBeenCalledWith(
"11111111-1111-4111-8111-111111111111",
"comment-1",
@@ -362,6 +387,7 @@ describe.sequential("issue comment cancel routes", () => {
"tx",
);
expect(mockIssueReferenceService.deleteCommentSource).toHaveBeenCalledWith("annotation-comment-1", "tx");
expect(mockExternalObjectService.syncCommentSafely).toHaveBeenCalledWith("annotation-comment-1", "tx");
const deletedActivity = mockLogActivity.mock.calls.find((call) => call[1]?.action === "issue.comment_deleted")?.[1];
expect(deletedActivity).toEqual(expect.objectContaining({
action: "issue.comment_deleted",
+5 -1
View File
@@ -7309,6 +7309,7 @@ export function issueRoutes(
{
afterTombstone: async (deletedComment, tx) => {
await issueReferencesSvc.syncComment(deletedComment.id, tx);
await externalObjectsSvc.syncCommentSafely(deletedComment.id, tx);
annotationCleanup = await documentAnnotationsSvc.cleanupForIssueCommentDeletion(issue.id, deletedComment.id, {
actorType: actor.actorType,
actorId: actor.actorId,
@@ -7318,7 +7319,10 @@ export function issueRoutes(
}, tx);
await Promise.all(
annotationCleanup.deletedCommentIds.map((annotationCommentId) =>
issueReferencesSvc.deleteCommentSource(annotationCommentId, tx)
Promise.all([
issueReferencesSvc.deleteCommentSource(annotationCommentId, tx),
externalObjectsSvc.syncCommentSafely(annotationCommentId, tx),
])
),
);
},
+2 -2
View File
@@ -581,8 +581,8 @@ export function externalObjectService(
await safeSync("issue", () => syncIssue(issueId));
}
async function syncCommentSafely(commentId: string) {
await safeSync("comment", () => syncComment(commentId));
async function syncCommentSafely(commentId: string, dbOrTx: any = db) {
await safeSync("comment", () => syncComment(commentId, dbOrTx));
}
async function syncDocumentSafely(documentId: string) {