mirror of
https://github.com/block/buzz.git
synced 2026-10-29 05:21:31 +00:00
test(sidebar): make the two accepted-residual tests faithful to their contract
The hard-eviction-branch test asserted only the rev tuple outcome on two one-entry stores while its comment claimed the >500-entry eviction/remount setup. Build the real fixture: >500 equal-updatedAt entries so the bound evicts the target by the id tiebreak, then a remounted rev-1 click merged against the retained rev-100 remote, asserting the deterministic rev-100 outcome in both merge orders. Stars and mutes. The unobserved-future mixed-fleet residual was stated but never exercised directly: the fast-clock suites cover only the observed-future fix. Add a click with an empty high-water at t, then a genuinely unobserved opposite-value head at t+300 that wins on the primary updatedAt key. Both hook suites. Test-only; no production source changes. Co-authored-by: Will Pfleger <pfleger.will@gmail.com> Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
This commit is contained in:
@@ -378,19 +378,47 @@ test("finding 3 easy branch: a fresh click beats an evicted high-rev entry at an
|
||||
// here as the hard branch (not disguised as safety).
|
||||
test("finding 3 hard branch: equal-second evicted click (rev 1) loses to observed remote (rev 100)", () => {
|
||||
const NOW = 777;
|
||||
// Remount click on the evicted channel: empty high-water → rev 1, updatedAt=NOW.
|
||||
const click = S(E(true, NOW, 1));
|
||||
// The previously observed remote for the same channel at the same second,
|
||||
// rev 100 (it may precede the remount — not genuinely concurrent).
|
||||
const remote = S(E(false, NOW, 100));
|
||||
const merged = mergeStores(click, remote);
|
||||
const TARGET = "aaa-target"; // lexicographically small → evicted by the id tiebreak
|
||||
|
||||
// >500 entries all at the CURRENT second (NOW). With MAX+1 equal-updatedAt
|
||||
// entries, boundMuteStore sorts ascending by (updatedAt, id) and keeps the
|
||||
// highest MAX, so the lowest id is evicted — TARGET, NOT because it is old.
|
||||
const channels = { [TARGET]: E(true, NOW, 7) };
|
||||
for (let i = 0; i < MAX_CHANNEL_MUTE_ENTRIES; i++) {
|
||||
channels[`z-${String(i).padStart(3, "0")}`] = E(true, NOW, 0);
|
||||
}
|
||||
const bounded = boundMuteStore({ version: 1, channels });
|
||||
assert.equal(
|
||||
Object.keys(bounded.channels).length,
|
||||
MAX_CHANNEL_MUTE_ENTRIES,
|
||||
"bound trims to the cap",
|
||||
);
|
||||
assert.equal(
|
||||
bounded.channels[TARGET],
|
||||
undefined,
|
||||
"TARGET evicted by the id tiebreak at equal updatedAt",
|
||||
);
|
||||
|
||||
// Remount in the same second: TARGET's rev high-water is gone with the entry,
|
||||
// so a fresh click mints rev 1 at updatedAt=NOW.
|
||||
const click = { version: 1, channels: { [TARGET]: E(true, NOW, 1) } };
|
||||
// The previously observed remote for TARGET at the same second, rev 100 (it
|
||||
// may precede the remount — not genuinely concurrent).
|
||||
const remote = { version: 1, channels: { [TARGET]: E(false, NOW, 100) } };
|
||||
|
||||
// equal updatedAt → rev decides → 100 > 1: the click LOSES. Documented
|
||||
// deterministic residual, proven here through real eviction+remount, not a
|
||||
// pre-shrunk tuple. Deterministic in both merge orders — a lost click, never
|
||||
// a divergence.
|
||||
assert.deepEqual(
|
||||
merged.channels.c,
|
||||
mergeStores(click, remote).channels[TARGET],
|
||||
E(false, NOW, 100),
|
||||
"equal updatedAt → higher rev wins deterministically (documented residual)",
|
||||
);
|
||||
// Deterministic either merge order — a lost click, never a divergence.
|
||||
assert.deepEqual(mergeStores(remote, click).channels.c, E(false, NOW, 100));
|
||||
assert.deepEqual(
|
||||
mergeStores(remote, click).channels[TARGET],
|
||||
E(false, NOW, 100),
|
||||
);
|
||||
});
|
||||
|
||||
// ── mutedChannelIdsFromStore ────────────────────────────────────────────────
|
||||
|
||||
@@ -378,19 +378,47 @@ test("finding 3 easy branch: a fresh click beats an evicted high-rev entry at an
|
||||
// here as the hard branch (not disguised as safety).
|
||||
test("finding 3 hard branch: equal-second evicted click (rev 1) loses to observed remote (rev 100)", () => {
|
||||
const NOW = 777;
|
||||
// Remount click on the evicted channel: empty high-water → rev 1, updatedAt=NOW.
|
||||
const click = S(E(true, NOW, 1));
|
||||
// The previously observed remote for the same channel at the same second,
|
||||
// rev 100 (it may precede the remount — not genuinely concurrent).
|
||||
const remote = S(E(false, NOW, 100));
|
||||
const merged = mergeStores(click, remote);
|
||||
const TARGET = "aaa-target"; // lexicographically small → evicted by the id tiebreak
|
||||
|
||||
// >500 entries all at the CURRENT second (NOW). With MAX+1 equal-updatedAt
|
||||
// entries, boundStarStore sorts ascending by (updatedAt, id) and keeps the
|
||||
// highest MAX, so the lowest id is evicted — TARGET, NOT because it is old.
|
||||
const channels = { [TARGET]: E(true, NOW, 7) };
|
||||
for (let i = 0; i < MAX_CHANNEL_STAR_ENTRIES; i++) {
|
||||
channels[`z-${String(i).padStart(3, "0")}`] = E(true, NOW, 0);
|
||||
}
|
||||
const bounded = boundStarStore({ version: 1, channels });
|
||||
assert.equal(
|
||||
Object.keys(bounded.channels).length,
|
||||
MAX_CHANNEL_STAR_ENTRIES,
|
||||
"bound trims to the cap",
|
||||
);
|
||||
assert.equal(
|
||||
bounded.channels[TARGET],
|
||||
undefined,
|
||||
"TARGET evicted by the id tiebreak at equal updatedAt",
|
||||
);
|
||||
|
||||
// Remount in the same second: TARGET's rev high-water is gone with the entry,
|
||||
// so a fresh click mints rev 1 at updatedAt=NOW.
|
||||
const click = { version: 1, channels: { [TARGET]: E(true, NOW, 1) } };
|
||||
// The previously observed remote for TARGET at the same second, rev 100 (it
|
||||
// may precede the remount — not genuinely concurrent).
|
||||
const remote = { version: 1, channels: { [TARGET]: E(false, NOW, 100) } };
|
||||
|
||||
// equal updatedAt → rev decides → 100 > 1: the click LOSES. Documented
|
||||
// deterministic residual, proven here through real eviction+remount, not a
|
||||
// pre-shrunk tuple. Deterministic in both merge orders — a lost click, never
|
||||
// a divergence.
|
||||
assert.deepEqual(
|
||||
merged.channels.c,
|
||||
mergeStores(click, remote).channels[TARGET],
|
||||
E(false, NOW, 100),
|
||||
"equal updatedAt → higher rev wins deterministically (documented residual)",
|
||||
);
|
||||
// Deterministic either merge order — a lost click, never a divergence.
|
||||
assert.deepEqual(mergeStores(remote, click).channels.c, E(false, NOW, 100));
|
||||
assert.deepEqual(
|
||||
mergeStores(remote, click).channels[TARGET],
|
||||
E(false, NOW, 100),
|
||||
);
|
||||
});
|
||||
|
||||
// ── starredChannelIdsFromStore ────────────────────────────────────────────────
|
||||
|
||||
@@ -327,6 +327,69 @@ test("empty-store click survives a later higher-rev head with an older updatedAt
|
||||
}
|
||||
});
|
||||
|
||||
// Unobserved-future residual (accepted mixed-fleet case, MINOR-2 contrast to the
|
||||
// fast-clock fix): a click mints at wall-clock t with an empty high-water; a
|
||||
// genuinely UNOBSERVED opposite-value head then arrives at t+300 and wins on the
|
||||
// primary updatedAt key. The logical-monotonic stamp only defends against state
|
||||
// the replica already observed — a future head it never saw before clicking is
|
||||
// not covered, exactly as under today's shipped LWW.
|
||||
test("unobserved future head wins over an empty-high-water click on primary updatedAt", async () => {
|
||||
const { act, cleanup, renderHook } = await import("@testing-library/react");
|
||||
const { relayClient } = await import("@/shared/api/relayClient");
|
||||
const { useChannelMutes } = await import("./useChannelMutes.ts");
|
||||
|
||||
const live = {};
|
||||
const restore = stubRelay(relayClient, { live });
|
||||
const origTauri = window.__TAURI_INTERNALS__;
|
||||
const origDateNow = Date.now;
|
||||
Date.now = () => 1000 * 1_000; // click at updatedAt 1000 (t)
|
||||
window.__TAURI_INTERNALS__ = {
|
||||
invoke: (cmd) => {
|
||||
// fetchEvents is stubbed empty, so bootstrap/pre-publish never decrypt;
|
||||
// the only decrypt is the live head — the genuinely unobserved future
|
||||
// entry at updatedAt 1300, delivered after the empty-high-water click.
|
||||
if (cmd === "nip44_decrypt_from_self")
|
||||
return Promise.resolve(
|
||||
mutePayload({ shared: { muted: false, updatedAt: 1300, rev: 1 } }),
|
||||
);
|
||||
return Promise.reject(new Error(`unmocked ${cmd}`));
|
||||
},
|
||||
};
|
||||
const pubkey = "pk-unobserved-future";
|
||||
let hook = null;
|
||||
try {
|
||||
await act(async () => {
|
||||
hook = renderHook(() => useChannelMutes(pubkey, "wss://r"));
|
||||
for (let i = 0; i < 20; i++) await Promise.resolve();
|
||||
});
|
||||
await act(async () => hook.result.current.muteChannel("shared")); // empty store → rev 1 @ 1000
|
||||
await act(async () => {
|
||||
// Genuinely unobserved head at t+300 (updatedAt 1300), opposite value.
|
||||
live.cb({
|
||||
id: "unobserved-future",
|
||||
pubkey,
|
||||
created_at: 1300,
|
||||
content: "cipher",
|
||||
kind: 30078,
|
||||
tags: [["d", "channel-mutes"]],
|
||||
sig: "s",
|
||||
});
|
||||
for (let i = 0; i < 20; i++) await Promise.resolve();
|
||||
});
|
||||
assert.equal(
|
||||
hook.result.current.mutedChannelIds.has("shared"),
|
||||
false,
|
||||
"unobserved future head wins on primary updatedAt (accepted residual)",
|
||||
);
|
||||
hook.unmount();
|
||||
} finally {
|
||||
cleanup();
|
||||
Date.now = origDateNow;
|
||||
window.__TAURI_INTERNALS__ = origTauri;
|
||||
restore();
|
||||
}
|
||||
});
|
||||
|
||||
// Cross-window storage: a peer window's write is observed into the high-water
|
||||
// and max-merged, so a following click sees the peer's rev and no edit is lost.
|
||||
test("cross-window storage event is observed and max-merged", async () => {
|
||||
|
||||
@@ -327,6 +327,68 @@ test("empty-store click survives a later higher-rev head with an older updatedAt
|
||||
}
|
||||
});
|
||||
|
||||
// Unobserved-future residual (accepted mixed-fleet case, MINOR-2 contrast to the
|
||||
// fast-clock fix): a click mints at wall-clock t with an empty high-water; a
|
||||
// genuinely UNOBSERVED opposite-value head then arrives at t+300 and wins on the
|
||||
// primary updatedAt key. The logical-monotonic stamp only defends against state
|
||||
// the replica already observed — a future head it never saw before clicking is
|
||||
// not covered, exactly as under today's shipped LWW.
|
||||
test("unobserved future head wins over an empty-high-water click on primary updatedAt", async () => {
|
||||
const { act, cleanup, renderHook } = await import("@testing-library/react");
|
||||
const { relayClient } = await import("@/shared/api/relayClient");
|
||||
const { useChannelStars } = await import("./useChannelStars.ts");
|
||||
|
||||
const live = {};
|
||||
const restore = stubRelay(relayClient, { live });
|
||||
const origTauri = window.__TAURI_INTERNALS__;
|
||||
const origDateNow = Date.now;
|
||||
Date.now = () => 1000 * 1_000; // click at updatedAt 1000 (t)
|
||||
window.__TAURI_INTERNALS__ = {
|
||||
invoke: (cmd) => {
|
||||
// fetchEvents is stubbed empty, so bootstrap/pre-publish never decrypt;
|
||||
// the only decrypt is the live head — the genuinely unobserved future
|
||||
// entry at updatedAt 1300, delivered after the empty-high-water click.
|
||||
if (cmd === "nip44_decrypt_from_self")
|
||||
return Promise.resolve(
|
||||
starPayload({ shared: { starred: false, updatedAt: 1300, rev: 1 } }),
|
||||
);
|
||||
return Promise.reject(new Error(`unmocked ${cmd}`));
|
||||
},
|
||||
};
|
||||
const pubkey = "pk-unobserved-future";
|
||||
let hook = null;
|
||||
try {
|
||||
await act(async () => {
|
||||
hook = renderHook(() => useChannelStars(pubkey, "wss://r"));
|
||||
for (let i = 0; i < 20; i++) await Promise.resolve();
|
||||
});
|
||||
await act(async () => hook.result.current.starChannel("shared")); // empty store → rev 1 @ 1000
|
||||
await act(async () => {
|
||||
// Genuinely unobserved head at t+300 (updatedAt 1300), opposite value.
|
||||
live.cb({
|
||||
id: "unobserved-future",
|
||||
pubkey,
|
||||
created_at: 1300,
|
||||
content: "cipher",
|
||||
kind: 30078,
|
||||
tags: [["d", "channel-stars"]],
|
||||
sig: "s",
|
||||
});
|
||||
for (let i = 0; i < 20; i++) await Promise.resolve();
|
||||
});
|
||||
assert.equal(
|
||||
hook.result.current.starredChannelIds.has("shared"),
|
||||
false,
|
||||
"unobserved future head wins on primary updatedAt (accepted residual)",
|
||||
);
|
||||
hook.unmount();
|
||||
} finally {
|
||||
cleanup();
|
||||
Date.now = origDateNow;
|
||||
window.__TAURI_INTERNALS__ = origTauri;
|
||||
restore();
|
||||
}
|
||||
});
|
||||
// Cross-window storage: a peer window's write is observed into the high-water
|
||||
// and max-merged, so a following click sees the peer's rev and no edit is lost.
|
||||
test("cross-window storage event is observed and max-merged", async () => {
|
||||
|
||||
Reference in New Issue
Block a user