Harden optimistic comment IDs

Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
dotta 2026-03-28 10:04:46 -05:00
parent 52bb4ea37a
commit cfb7dd4818
2 changed files with 34 additions and 2 deletions

View file

@ -1,4 +1,4 @@
import { describe, expect, it } from "vitest";
import { afterEach, describe, expect, it, vi } from "vitest";
import {
applyOptimisticIssueCommentUpdate,
createOptimisticIssueComment,
@ -7,6 +7,11 @@ import {
} from "./optimistic-issue-comments";
describe("optimistic issue comments", () => {
afterEach(() => {
vi.unstubAllGlobals();
vi.restoreAllMocks();
});
it("creates a pending optimistic comment for the current user", () => {
const comment = createOptimisticIssueComment({
companyId: "company-1",
@ -22,6 +27,25 @@ describe("optimistic issue comments", () => {
expect(comment.authorAgentId).toBeNull();
});
it("falls back when crypto.randomUUID is unavailable", () => {
vi.stubGlobal("crypto", {});
const nowSpy = vi.spyOn(Date, "now").mockReturnValue(1_746_000_000_000);
const mathSpy = vi.spyOn(Math, "random").mockReturnValue(0.123456789);
const comment = createOptimisticIssueComment({
companyId: "company-1",
issueId: "issue-1",
body: "Working on it",
authorUserId: "board-1",
});
expect(comment.id).toBe("optimistic-1746000000000-4fzzzxjy");
expect(comment.clientId).toBe(comment.id);
nowSpy.mockRestore();
mathSpy.mockRestore();
});
it("merges optimistic comments into the server thread in chronological order", () => {
const merged = mergeIssueComments(
[

View file

@ -16,6 +16,14 @@ function toTimestamp(value: Date | string) {
return new Date(value).getTime();
}
function createOptimisticCommentId() {
const randomUuid = globalThis.crypto?.randomUUID?.();
if (randomUuid) {
return `optimistic-${randomUuid}`;
}
return `optimistic-${Date.now()}-${Math.random().toString(36).slice(2, 10)}`;
}
export function sortIssueComments<T extends { createdAt: Date | string; id: string }>(comments: T[]) {
return [...comments].sort((a, b) => {
const createdAtDiff = toTimestamp(a.createdAt) - toTimestamp(b.createdAt);
@ -31,7 +39,7 @@ export function createOptimisticIssueComment(params: {
authorUserId: string | null;
}): OptimisticIssueComment {
const now = new Date();
const clientId = `optimistic-${crypto.randomUUID()}`;
const clientId = createOptimisticCommentId();
return {
id: clientId,
clientId,