test(nip-rs-unread-ui): compress mechanical test matrices to table-driven loops

applyOverrideUnread refusal reasons (budget_exhausted, uint32_overflow,
load_incomplete) and fetchCommunityUnread thread-gate cases folded into
const arrays with per-row test names. Test count unchanged at 4,062.
Adversarial witnesses keep their names and structure verbatim.

Co-authored-by: Will Pfleger <pfleger.will@gmail.com>
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
This commit is contained in:
npub1g8493u0xfsjrvflg4n08ezd7vec99mnwzlv0qgwpr9d7gvjwhuzqx59rhw
2026-08-03 14:29:01 -04:00
parent 4e65142a0e
commit 560fc479a5
2 changed files with 65 additions and 111 deletions

View File

@@ -112,32 +112,20 @@ test("applyOverrideUnread_success_returns_true", () => {
assert.equal(toasts.length, 0);
});
test("applyOverrideUnread_budget_exhausted_returns_false", () => {
const result = applyOverrideUnread("ch-1", {
markChannelUnread: () => ({ success: false, reason: "budget_exhausted" }),
markChannelRead: () => ({ success: true }),
getOverrideLiveness: () => null,
for (const reason of [
"budget_exhausted",
"uint32_overflow",
"load_incomplete",
]) {
test(`applyOverrideUnread_${reason}_returns_false`, () => {
const result = applyOverrideUnread("ch-1", {
markChannelUnread: () => ({ success: false, reason }),
markChannelRead: () => ({ success: true }),
getOverrideLiveness: () => null,
});
assert.equal(result, false);
});
assert.equal(result, false);
});
test("applyOverrideUnread_uint32_overflow_returns_false", () => {
const result = applyOverrideUnread("ch-1", {
markChannelUnread: () => ({ success: false, reason: "uint32_overflow" }),
markChannelRead: () => ({ success: true }),
getOverrideLiveness: () => null,
});
assert.equal(result, false);
});
test("applyOverrideUnread_load_incomplete_returns_false", () => {
const result = applyOverrideUnread("ch-1", {
markChannelUnread: () => ({ success: false, reason: "load_incomplete" }),
markChannelRead: () => ({ success: true }),
getOverrideLiveness: () => null,
});
assert.equal(result, false);
});
}
// ── Tests: applyOverrideRead ─────────────────────────────────────────────────
//

View File

@@ -544,102 +544,68 @@ function baseRelay(unreadEvent, mutesPayload = null) {
]);
}
test("fetchCommunityUnread threaded reply in untracked root → hasUnread:false", async () => {
const relay = baseRelay(threadedReplyEvent());
const result = await fetchCommunityUnread({
client: relay,
pubkey: PUBKEY,
nowSeconds: 100,
decryptReadState: async (v) => v,
decryptMutes: async (v) => v,
// No root in any set → gate rejects the threaded reply
readThreadRelationships: readRelationships(),
});
assert.deepEqual(result, { hasUnread: false, mentionCount: 0 });
});
test("fetchCommunityUnread threaded reply in participatedRootIds → hasUnread:true", async () => {
const relay = baseRelay(threadedReplyEvent());
const result = await fetchCommunityUnread({
client: relay,
pubkey: PUBKEY,
nowSeconds: 100,
decryptReadState: async (v) => v,
decryptMutes: async (v) => v,
readThreadRelationships: readRelationships({
participatedRootIds: new Set([THREAD_ROOT_2]),
}),
});
assert.deepEqual(result, { hasUnread: true, mentionCount: 0 });
});
test("fetchCommunityUnread #p-mention reply in untracked root → hasUnread:true (mention overrides)", async () => {
// A @mention of the user bypasses the follow/participation gate
const relay = baseRelay(
threadedReplyEvent({
const THREAD_GATE_CASES = [
{
name: "threaded reply in untracked root → hasUnread:false",
unreadEvent: threadedReplyEvent(),
relationships: {},
expected: { hasUnread: false, mentionCount: 0 },
},
{
name: "threaded reply in participatedRootIds → hasUnread:true",
unreadEvent: threadedReplyEvent(),
relationships: { participatedRootIds: new Set([THREAD_ROOT_2]) },
expected: { hasUnread: true, mentionCount: 0 },
},
{
name: "#p-mention reply in untracked root → hasUnread:true (mention overrides)",
unreadEvent: threadedReplyEvent({
id: "mention-reply".padEnd(64, "0"),
extraTags: [["p", PUBKEY]],
}),
);
const result = await fetchCommunityUnread({
client: relay,
pubkey: PUBKEY,
nowSeconds: 100,
decryptReadState: async (v) => v,
decryptMutes: async (v) => v,
readThreadRelationships: readRelationships(),
});
assert.deepEqual(result, { hasUnread: true, mentionCount: 0 });
});
test("fetchCommunityUnread top-level post → hasUnread:true (no thread gate)", async () => {
// Top-level posts have no parentId — shouldNotifyForEvent returns true
const relay = baseRelay(
event({
relationships: {},
expected: { hasUnread: true, mentionCount: 0 },
},
{
name: "top-level post → hasUnread:true (no thread gate)",
unreadEvent: event({
id: "toplevel".padEnd(64, "0"),
created_at: 20,
tags: [["h", CHANNEL_ID]],
}),
);
const result = await fetchCommunityUnread({
client: relay,
pubkey: PUBKEY,
nowSeconds: 100,
decryptReadState: async (v) => v,
decryptMutes: async (v) => v,
readThreadRelationships: readRelationships(),
});
assert.deepEqual(result, { hasUnread: true, mentionCount: 0 });
});
test("fetchCommunityUnread threaded reply whose root is in mutedRootIds → hasUnread:false", async () => {
const relay = baseRelay(
threadedReplyEvent({ id: "muted-reply".padEnd(64, "0") }),
);
const result = await fetchCommunityUnread({
client: relay,
pubkey: PUBKEY,
nowSeconds: 100,
decryptReadState: async (v) => v,
decryptMutes: async (v) => v,
// Root is participated but also muted — mute wins
readThreadRelationships: readRelationships({
relationships: {},
expected: { hasUnread: true, mentionCount: 0 },
},
{
name: "threaded reply whose root is in mutedRootIds → hasUnread:false",
unreadEvent: threadedReplyEvent({ id: "muted-reply".padEnd(64, "0") }),
relationships: {
participatedRootIds: new Set([THREAD_ROOT_2]),
mutedRootIds: new Set([THREAD_ROOT_2]),
}),
});
},
expected: { hasUnread: false, mentionCount: 0 },
},
];
assert.deepEqual(result, { hasUnread: false, mentionCount: 0 });
});
for (const {
name,
unreadEvent,
relationships,
expected,
} of THREAD_GATE_CASES) {
test(`fetchCommunityUnread ${name}`, async () => {
const relay = baseRelay(unreadEvent);
const result = await fetchCommunityUnread({
client: relay,
pubkey: PUBKEY,
nowSeconds: 100,
decryptReadState: async (v) => v,
decryptMutes: async (v) => v,
readThreadRelationships: readRelationships(relationships),
});
assert.deepEqual(result, expected);
});
}
// ── Override register authoritative source tests ──────────────────────────