refactor(api): extract comment router support

This commit is contained in:
2026-03-31 14:28:07 +02:00
parent 73cfc9341b
commit ab46eca8b3
3 changed files with 265 additions and 64 deletions
@@ -0,0 +1,109 @@
import { describe, expect, it } from "vitest";
import { TRPCError } from "@trpc/server";
import {
assertCommentManageableByActor,
buildCommentCreateData,
buildCommentMentionNotifications,
commentAuthorSelect,
commentBelongsToEntity,
commentThreadInclude,
commentWithAuthorInclude,
parseCommentMentions,
} from "../router/comment-support.js";
describe("comment support", () => {
it("parses unique mention ids from comment markdown", () => {
expect(parseCommentMentions("Hi @[Alice](user_1) and @[Bob](user_2) and @[Alice](user_1)")).toEqual([
"user_1",
"user_2",
]);
});
it("exposes stable include definitions for comment reads and writes", () => {
expect(commentAuthorSelect).toEqual({
id: true,
name: true,
email: true,
image: true,
});
expect(commentWithAuthorInclude).toEqual({
author: { select: commentAuthorSelect },
});
expect(commentThreadInclude).toEqual({
author: { select: commentAuthorSelect },
replies: {
include: {
author: { select: commentAuthorSelect },
},
orderBy: { createdAt: "asc" },
},
});
});
it("builds comment create payloads and mention notifications", () => {
expect(buildCommentCreateData({
entityType: "estimate",
entityId: "est_1",
parentId: "comment_parent",
authorId: "user_1",
body: "Please review @[Bob](user_2)",
mentions: ["user_2"],
})).toEqual({
entityType: "estimate",
entityId: "est_1",
parentId: "comment_parent",
authorId: "user_1",
body: "Please review @[Bob](user_2)",
mentions: ["user_2"],
});
expect(buildCommentMentionNotifications({
mentionedUserIds: ["user_2"],
authorName: "Alice Example",
body: "x".repeat(121),
entityId: "est_1",
entityType: "estimate",
senderId: "user_1",
link: "/estimates/est_1",
})).toEqual([
{
userId: "user_2",
type: "COMMENT_MENTION",
title: "Alice Example mentioned you in a comment",
body: `${"x".repeat(120)}...`,
entityId: "est_1",
entityType: "estimate",
senderId: "user_1",
link: "/estimates/est_1",
channel: "in_app",
},
]);
});
it("checks entity ownership and author-or-admin permissions", () => {
expect(commentBelongsToEntity({
comment: {
entityType: "resource",
entityId: "res_1",
},
entityType: "resource",
entityId: "res_1",
})).toBe(true);
expect(() => assertCommentManageableByActor({
authorId: "user_2",
actorUserId: "user_1",
isAdmin: false,
action: "delete",
})).toThrowError(TRPCError);
expect(() => assertCommentManageableByActor({
authorId: "user_2",
actorUserId: "user_1",
isAdmin: true,
action: "resolve",
})).not.toThrow();
});
});