mirror of
https://github.com/PR0M3TH3AN/bitvid.git
synced 2026-10-27 07:52:24 +00:00
Second root cause of "watch history delete doesn't work / list isn't getting overwritten": reads UNION items across every event the relays return, and a publish only reports success when ALL relays accept. With a flaky relay list the reduced (post-removal) event reaches some relays while others keep serving the old, larger copy — and the reader deduped only by event id, so both the stale and fresh versions of the same month (d-tag) were unioned, resurrecting removed items. Add dedupeNewestPerReplaceableAddress and apply it at both read union sites (the chunk-events path and the pointer-events path): keep only the newest event per d-tag (month) before unioning, so a lagging relay can't reintroduce removed items. Distinct months are all preserved; events without a d-tag are kept by id. Tests + mutation-verified: newest-per-month wins (incl. the fewer-items removal case), distinct months preserved, newest-first ordering, no-d-tag kept by id. Reverting to id-dedup fails the resurrection scenarios. Build + full lint green (watchHistory.js back under file-size budget). Co-Authored-By: Claude Opus 4.8 <[email protected]>
65 lines
2.4 KiB
JavaScript
65 lines
2.4 KiB
JavaScript
// Reading watch history unions items across the events relays return. Watch-
|
|
// history months are replaceable (d-tag = month), so when relays disagree — a
|
|
// lagging relay still serving the pre-removal copy — the reader must keep only
|
|
// the NEWEST version per month, or removed items get resurrected by the union.
|
|
|
|
import assert from "node:assert/strict";
|
|
import test from "node:test";
|
|
import { dedupeNewestPerReplaceableAddress } from "../js/nostr/watchHistoryDedup.js";
|
|
|
|
const ev = (id, dTag, created_at) => ({
|
|
id,
|
|
created_at,
|
|
tags: [["d", dTag]],
|
|
});
|
|
|
|
test("keeps only the newest event per month (d-tag)", () => {
|
|
// Old full copy (created 100) and new reduced copy (created 200) of 2026-06,
|
|
// as if a lagging relay still served the old one alongside the fresh one.
|
|
const out = dedupeNewestPerReplaceableAddress([
|
|
ev("old", "2026-06", 100),
|
|
ev("new", "2026-06", 200),
|
|
]);
|
|
assert.equal(out.length, 1);
|
|
assert.equal(out[0].id, "new", "the newest version of the month wins");
|
|
});
|
|
|
|
test("preserves distinct months", () => {
|
|
const out = dedupeNewestPerReplaceableAddress([
|
|
ev("a", "2026-06", 200),
|
|
ev("b", "2026-05", 100),
|
|
]);
|
|
assert.deepEqual(
|
|
out.map((e) => e.id).sort(),
|
|
["a", "b"],
|
|
"different months are all kept",
|
|
);
|
|
});
|
|
|
|
test("result is sorted newest-first", () => {
|
|
const out = dedupeNewestPerReplaceableAddress([
|
|
ev("older", "2026-05", 100),
|
|
ev("newer", "2026-06", 300),
|
|
ev("mid", "2026-04", 200),
|
|
]);
|
|
assert.deepEqual(out.map((e) => e.id), ["newer", "mid", "older"]);
|
|
});
|
|
|
|
test("events without a d-tag are kept individually (by id)", () => {
|
|
const out = dedupeNewestPerReplaceableAddress([
|
|
{ id: "x", created_at: 1, tags: [] },
|
|
{ id: "y", created_at: 2, tags: [] },
|
|
]);
|
|
assert.equal(out.length, 2);
|
|
});
|
|
|
|
test("the newest month copy with FEWER items is what survives (removal sticks)", () => {
|
|
// Models the real bug: old copy has [a,b]; the removal published [a] to some
|
|
// relays. Dedup must surface only the [a] event so the union drops b.
|
|
const old = { id: "old", created_at: 100, tags: [["d", "2026-06"]], items: ["a", "b"] };
|
|
const fresh = { id: "fresh", created_at: 200, tags: [["d", "2026-06"]], items: ["a"] };
|
|
const out = dedupeNewestPerReplaceableAddress([old, fresh]);
|
|
assert.equal(out.length, 1);
|
|
assert.deepEqual(out[0].items, ["a"], "only the post-removal copy contributes");
|
|
});
|