mirror of
https://github.com/paperclipai/paperclip.git
synced 2026-10-25 09:41:04 +01:00
Cover buildReplyExcerpt (whitespace collapse, exact-length no-truncate, over-length clamp+flag, boundary trim), buildOptimisticReplyTo (identity mirror + null normalization), and formatReplyAuthorName (agent name, unknown agent fallback, System, You). Satisfies the PR quality gate's test requirement and locks the excerpt contract shared with the server snapshot. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
117 lines
3.8 KiB
TypeScript
117 lines
3.8 KiB
TypeScript
import type { Agent, IssueComment } from "@paperclipai/shared";
|
|
import { describe, expect, it } from "vitest";
|
|
import {
|
|
REPLY_EXCERPT_MAX_LENGTH,
|
|
buildOptimisticReplyTo,
|
|
buildReplyExcerpt,
|
|
formatReplyAuthorName,
|
|
} from "./comment-reply";
|
|
|
|
function makeComment(overrides: Partial<IssueComment> = {}): IssueComment {
|
|
const now = new Date("2026-04-06T13:00:00.000Z");
|
|
return {
|
|
id: "comment-1",
|
|
companyId: "company-1",
|
|
issueId: "issue-1",
|
|
authorType: "agent",
|
|
authorAgentId: "agent-1",
|
|
authorUserId: null,
|
|
body: "Hello there",
|
|
presentation: null,
|
|
metadata: null,
|
|
createdAt: now,
|
|
updatedAt: now,
|
|
...overrides,
|
|
};
|
|
}
|
|
|
|
describe("buildReplyExcerpt", () => {
|
|
it("collapses runs of whitespace and newlines into a single line", () => {
|
|
const { excerpt, truncated } = buildReplyExcerpt(" line one\n\n line two\t");
|
|
expect(excerpt).toBe("line one line two");
|
|
expect(truncated).toBe(false);
|
|
});
|
|
|
|
it("does not truncate a body at exactly the max length", () => {
|
|
const body = "a".repeat(REPLY_EXCERPT_MAX_LENGTH);
|
|
const { excerpt, truncated } = buildReplyExcerpt(body);
|
|
expect(excerpt).toHaveLength(REPLY_EXCERPT_MAX_LENGTH);
|
|
expect(truncated).toBe(false);
|
|
});
|
|
|
|
it("clamps and flags a body longer than the max length", () => {
|
|
const body = "b".repeat(REPLY_EXCERPT_MAX_LENGTH + 50);
|
|
const { excerpt, truncated } = buildReplyExcerpt(body);
|
|
expect(excerpt).toHaveLength(REPLY_EXCERPT_MAX_LENGTH);
|
|
expect(truncated).toBe(true);
|
|
});
|
|
|
|
it("trims trailing whitespace left at the clamp boundary", () => {
|
|
const body = `${"word ".repeat(60)}`; // well over the limit, boundary lands on a space
|
|
const { excerpt } = buildReplyExcerpt(body);
|
|
expect(excerpt.length).toBeLessThanOrEqual(REPLY_EXCERPT_MAX_LENGTH);
|
|
expect(excerpt).toBe(excerpt.trimEnd());
|
|
});
|
|
});
|
|
|
|
describe("buildOptimisticReplyTo", () => {
|
|
it("mirrors the target's identity and a clamped excerpt", () => {
|
|
const target = makeComment({
|
|
id: "comment-src",
|
|
authorType: "agent",
|
|
authorAgentId: "agent-7",
|
|
authorUserId: null,
|
|
body: "c".repeat(REPLY_EXCERPT_MAX_LENGTH + 10),
|
|
});
|
|
const snapshot = buildOptimisticReplyTo(target);
|
|
expect(snapshot).toEqual({
|
|
commentId: "comment-src",
|
|
authorType: "agent",
|
|
authorAgentId: "agent-7",
|
|
authorUserId: null,
|
|
excerpt: "c".repeat(REPLY_EXCERPT_MAX_LENGTH),
|
|
excerptTruncated: true,
|
|
});
|
|
});
|
|
|
|
it("normalizes undefined author ids to null for a user comment", () => {
|
|
const target = makeComment({
|
|
id: "comment-user",
|
|
authorType: "user",
|
|
authorAgentId: undefined as unknown as string | null,
|
|
authorUserId: "user-9",
|
|
body: "short",
|
|
});
|
|
const snapshot = buildOptimisticReplyTo(target);
|
|
expect(snapshot.authorAgentId).toBeNull();
|
|
expect(snapshot.authorUserId).toBe("user-9");
|
|
expect(snapshot.excerptTruncated).toBe(false);
|
|
});
|
|
});
|
|
|
|
describe("formatReplyAuthorName", () => {
|
|
const agentMap = new Map<string, Agent>([
|
|
["agent-1", { id: "agent-1", name: "CodexCoder" } as Agent],
|
|
]);
|
|
|
|
it("uses the agent name when the target is an agent in the map", () => {
|
|
expect(
|
|
formatReplyAuthorName({ authorType: "agent", authorAgentId: "agent-1" }, agentMap),
|
|
).toBe("CodexCoder");
|
|
});
|
|
|
|
it("falls back to a short agent id when the agent is unknown", () => {
|
|
expect(
|
|
formatReplyAuthorName({ authorType: "agent", authorAgentId: "agent-abcdef123" }, agentMap),
|
|
).toBe("agent-ab");
|
|
});
|
|
|
|
it("labels system authors as System", () => {
|
|
expect(formatReplyAuthorName({ authorType: "system", authorAgentId: null })).toBe("System");
|
|
});
|
|
|
|
it("labels user authors as You", () => {
|
|
expect(formatReplyAuthorName({ authorType: "user", authorAgentId: null })).toBe("You");
|
|
});
|
|
});
|