mirror of
https://github.com/PR0M3TH3AN/bitvid.git
synced 2026-10-27 07:52:24 +00:00
test(watch-history): regression test for fingerprint emit-on-change
Locks in the fix for the infinite feed-refresh loop: asserts "fingerprint" is emitted only when the value actually changes (unchanged re-evaluation must not emit, or the For You feed would refresh -> reload -> emit forever). Verified to fail when the change-guard is removed. Co-Authored-By: Claude Opus 4.8 <[email protected]>
This commit is contained in:
1 parent
915b77b740
commit
96da135042
1 file changed
+73
@@ -0,0 +1,73 @@
|
||||
// Regression test for the infinite feed-refresh loop that pegged the CPU.
|
||||
//
|
||||
// Scenario (SCN-fingerprint-emit-on-change-only):
|
||||
// Given the For You feed re-runs whenever watch history emits "fingerprint",
|
||||
// When watch history is re-evaluated but its fingerprint is UNCHANGED,
|
||||
// Then no "fingerprint" event is emitted — so the feed does not refresh,
|
||||
// which would re-evaluate watch history, re-emit, and loop forever.
|
||||
// And when the fingerprint genuinely changes, exactly one event is emitted.
|
||||
//
|
||||
// The bug: getFingerprint/updateFingerprintCache emitted "fingerprint" on every
|
||||
// evaluation regardless of change, so the feed's refresh -> reload -> emit chain
|
||||
// never settled (a full feed rebuild ~2.5x/second).
|
||||
|
||||
import "./test-helpers/setup-localstorage.mjs";
|
||||
import { test, mock } from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
|
||||
const { nostrClient } = await import("../js/nostrClientFacade.js");
|
||||
const { watchHistoryService } = await import("../js/watchHistoryService.js");
|
||||
const { WATCH_HISTORY_CACHE_TTL_MS } = await import("../js/config.js");
|
||||
|
||||
const ACTOR = "a".repeat(64);
|
||||
|
||||
test("emits 'fingerprint' only when it actually changes", async () => {
|
||||
const originalGet = nostrClient.getWatchHistoryFingerprint;
|
||||
const originalPubkey = nostrClient.pubkey;
|
||||
const originalSession = nostrClient.sessionActor;
|
||||
|
||||
// Logged-in, non-session actor so the feature is enabled for ACTOR.
|
||||
nostrClient.pubkey = ACTOR;
|
||||
nostrClient.sessionActor = null;
|
||||
watchHistoryService.resetProgress(ACTOR); // start from a clean fingerprint cache
|
||||
|
||||
let currentFingerprint = "fp-1";
|
||||
nostrClient.getWatchHistoryFingerprint = async () => currentFingerprint;
|
||||
|
||||
let emitCount = 0;
|
||||
const unsubscribe = watchHistoryService.subscribe("fingerprint", () => {
|
||||
emitCount += 1;
|
||||
});
|
||||
|
||||
// Fake the clock so we can expire the (24h) fingerprint cache between calls
|
||||
// and thus force a genuine re-evaluation each time (otherwise the fresh-cache
|
||||
// early-return would mask whether the change-guard works).
|
||||
mock.timers.enable({ apis: ["Date"] });
|
||||
try {
|
||||
// 1) First evaluation: undefined -> "fp-1" is a change => one emit.
|
||||
await watchHistoryService.getFingerprint(ACTOR);
|
||||
assert.equal(emitCount, 1, "first fingerprint should emit once");
|
||||
|
||||
// 2) Re-evaluate with the SAME fingerprint after the cache expires.
|
||||
mock.timers.tick(WATCH_HISTORY_CACHE_TTL_MS + 1000);
|
||||
await watchHistoryService.getFingerprint(ACTOR);
|
||||
assert.equal(
|
||||
emitCount,
|
||||
1,
|
||||
"unchanged fingerprint must NOT emit (this is what broke the loop)",
|
||||
);
|
||||
|
||||
// 3) Re-evaluate after a real change => exactly one more emit.
|
||||
mock.timers.tick(WATCH_HISTORY_CACHE_TTL_MS + 1000);
|
||||
currentFingerprint = "fp-2";
|
||||
await watchHistoryService.getFingerprint(ACTOR);
|
||||
assert.equal(emitCount, 2, "a changed fingerprint should emit once more");
|
||||
} finally {
|
||||
mock.timers.reset();
|
||||
unsubscribe();
|
||||
nostrClient.getWatchHistoryFingerprint = originalGet;
|
||||
nostrClient.pubkey = originalPubkey;
|
||||
nostrClient.sessionActor = originalSession;
|
||||
watchHistoryService.resetProgress(ACTOR);
|
||||
}
|
||||
});
|
||||
Reference in new issue
Block a user