Merge pull request #1054 from PR0M3TH3AN/codex/add-tests-for-mixed-case-ids-and-exceptions

Add comment thread and modal regression tests
This commit is contained in:
thePR0M3TH3AN authored and GitHub committed 2025-12-29 22:08:32 -05:00
commit 4b8c149eb4
2 files changed
+185

No files matched your search

+78
View File
@@ -187,6 +187,39 @@ test("CommentThreadService surfaces cache write failures", () => {
);
});
test("CommentThreadService persists caches safely during teardown failures", async () => {
const warnings = [];
const service = new CommentThreadService({
fetchVideoComments: async () => [
createComment({ id: "persist-me", pubkey: "pk-cache", createdAt: 1 }),
],
subscribeVideoComments: () => () => {},
logger: {
warn: (...args) => warnings.push(args),
dev: { warn: (...args) => warnings.push(args) },
user: { warn: (...args) => warnings.push(args) },
},
});
await service.loadThread({ video: createBaseVideo() });
globalThis.localStorage.setItem = () => {
throw new Error("storage blocked");
};
assert.doesNotThrow(() => service.teardown());
assert.equal(
warnings.some((args) =>
args.some?.((value) =>
typeof value === "string" &&
value.includes("Failed to write comment cache for video123"),
),
),
true,
"teardown should warn when persistence fails but still reset state",
);
});
test("CommentThreadService logs cache usage and fallback decisions", async () => {
const initialFlag = FEATURE_IMPROVED_COMMENT_FETCHING;
const devLogs = [];
@@ -416,6 +449,51 @@ test("CommentThreadService normalizes mixed-case pubkeys during hydration", asyn
);
});
test("CommentThreadService deduplicates mixed-case event ids and pubkeys", async () => {
const hydrationRequests = [];
const service = new CommentThreadService({
fetchVideoComments: async () => [],
subscribeVideoComments: () => () => {},
batchFetchProfiles: async (pubkeys) => {
hydrationRequests.push([...pubkeys]);
},
hydrationDebounceMs: 0,
});
await service.loadThread({ video: createBaseVideo() });
const mixedCaseEvent = createComment({
id: "CoMmEnT-123", // intentionally mixed case
pubkey: "PuBkEy-123",
createdAt: 10,
});
const lowerCaseEvent = createComment({
id: "comment-123",
pubkey: "pubkey-123",
createdAt: 20,
});
service.processIncomingEvent(mixedCaseEvent);
service.processIncomingEvent(lowerCaseEvent);
await service.flushProfileQueue();
assert.deepEqual(
service.getCommentIdsForParent(null),
["comment-123"],
"mixed-case duplicates should collapse to a single normalized id",
);
assert.equal(
service.getCommentEvent("COMMENT-123")?.created_at,
20,
"newer updates should replace existing mixed-case entries",
);
assert.deepEqual(
hydrationRequests,
[["pubkey-123"]],
"hydration should only request the normalized pubkey once",
);
});
test("CommentThreadService retries profile hydration before succeeding", async () => {
const profiles = new Map();
const hydrationCalls = [];
+107
View File
@@ -699,6 +699,113 @@ test(
},
);
test(
"VideoModalCommentController disposes safely during in-flight comment loads",
async () => {
const renderCalls = [];
const appendCalls = [];
const teardownCalls = [];
const setCallbacksCalls = [];
let onThreadReady = null;
let onCommentsAppended = null;
let resolveLoadThread;
const commentThreadService = {
defaultLimit: 5,
setCallbacks: ({ onThreadReady: ready, onCommentsAppended: appended }) => {
onThreadReady = ready;
onCommentsAppended = appended;
},
loadThread: () =>
new Promise((resolve) => {
resolveLoadThread = resolve;
}),
teardown: () => {
teardownCalls.push("teardown");
},
processIncomingEvent: () => {},
};
const videoModal = {
setCommentSectionCallbacks: (callbacks) => {
setCallbacksCalls.push(callbacks);
},
setCommentsVisibility: () => {},
hideCommentsDisabledMessage: () => {},
showCommentsDisabledMessage: () => {},
clearComments: () => {},
resetCommentComposer: () => {},
setCommentStatus: () => {},
setCommentComposerState: () => {},
renderComments: (payload) => renderCalls.push(payload),
appendComment: (payload) => appendCalls.push(payload),
};
const controller = new VideoModalCommentController({
commentThreadService,
videoModal,
auth: { isLoggedIn: () => true },
callbacks: {
showError: () => {},
showStatus: () => {},
muteAuthor: () => Promise.resolve(),
shouldHideAuthor: () => false,
},
services: {
publishComment: async () => ({ ok: true, event: { id: "event", tags: [] } }),
},
});
controller.load({
id: "video-inflight",
pubkey: "AUTHORPK",
enableComments: true,
kind: 30078,
tags: [],
});
await Promise.resolve();
teardownCalls.length = 0; // ignore the initial dispose inside load
controller.dispose();
assert.equal(teardownCalls.length, 1, "dispose should teardown the active thread");
assert.equal(
setCallbacksCalls.length > 0,
true,
"comment callbacks should be registered even before load resolves",
);
resolveLoadThread?.();
await Promise.resolve();
onThreadReady?.({
videoEventId: "video-inflight",
parentCommentId: null,
commentsById: new Map([["event-1", { id: "event-1", pubkey: "pk1" }]]),
childrenByParent: new Map([[null, ["event-1"]]]),
profiles: new Map(),
});
assert.equal(renderCalls.length, 0, "stale thread payloads should be ignored after dispose");
onCommentsAppended?.({
videoEventId: "video-inflight",
parentCommentId: null,
commentIds: ["event-1"],
commentsById: new Map([["event-1", { id: "event-1", pubkey: "pk1" }]]),
childrenByParent: new Map([[null, ["event-1"]]]),
profiles: new Map(),
});
assert.equal(
appendCalls.length,
0,
"stale appends should not render duplicate comments after dispose",
);
},
);
test(
"VideoModalCommentController prompts login when publish requires authentication",
async () => {