Files
bitvid/tests/nostr-service-access-control-ready.test.mjs
T
thePR0M3TH3ANandClaude Opus 4.8 122d806ad2 perf(feed): don't block fresh load on remote admin-list fetch
loadVideos awaited ensureAccessControlReady -> waitForReady -> _performRefresh
-> loadAdminState, which awaits a remote admin-list fetch. On a cold load that
waits for relays to connect (~5s) plus the fetch, stalling the feed ~10s before
any video renders. The cached admin state IS hydrated synchronously at
construction, but with markLoaded:false, so waitForReady blocked on a full
remote refresh even when a usable cache existed ("refresh fixes it").

ensureAccessControlReady now blocks only on a first-ever cold load WITH whitelist
mode (where showing non-whitelisted content before the whitelist arrives would be
wrong). In every other case — admin state hydrated from the localStorage cache,
already loaded, or blacklist-only mode — it kicks the refresh off in the
background (ensureReady) and renders immediately; the reactive
onWhitelist/onBlacklistChange handlers re-filter the grid once fresh lists land.

Adds accessControl.isHydrated() and a branching test
(nostr-service-access-control-ready.test.mjs).

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-06-12 14:31:38 -04:00

71 lines
2.5 KiB
JavaScript

// Verifies ensureAccessControlReady no longer blocks the feed on the remote
// admin-list fetch except on a first-ever cold load in whitelist mode.
//
// Scenario (SCN-access-control-non-blocking-feed):
// - admin state hydrated (cache or loaded) => background refresh (ensureReady)
// - cold + whitelist mode => block (waitForReady)
// - cold + blacklist-only mode => background refresh (ensureReady)
import assert from "node:assert/strict";
import test from "node:test";
import "./test-helpers/setup-localstorage.mjs";
import moderationService from "../js/services/moderationService.js";
import { NostrService } from "../js/services/nostrService.js";
// Stub background timers the service may schedule.
if (moderationService) {
moderationService.scheduleTrustedMuteSubscriptionRefresh = () => {};
moderationService.refreshTrustedMuteSubscriptions = async () => {};
}
if (typeof globalThis.window === "undefined") globalThis.window = {};
function makeAccessControl({ hydrated, whitelist }) {
const calls = { ensureReady: 0, waitForReady: 0 };
return {
calls,
isHydrated: () => hydrated,
whitelistMode: () => whitelist,
ensureReady: async () => {
calls.ensureReady += 1;
},
waitForReady: async () => {
calls.waitForReady += 1;
},
};
}
test("hydrated => background refresh, never blocks on waitForReady", async () => {
const service = new NostrService();
const ac = makeAccessControl({ hydrated: true, whitelist: true });
service.accessControl = ac;
await service.ensureAccessControlReady();
assert.equal(ac.calls.ensureReady, 1);
assert.equal(ac.calls.waitForReady, 0);
});
test("cold + whitelist mode => blocks on waitForReady (correctness)", async () => {
const service = new NostrService();
const ac = makeAccessControl({ hydrated: false, whitelist: true });
service.accessControl = ac;
await service.ensureAccessControlReady();
assert.equal(ac.calls.waitForReady, 1);
assert.equal(ac.calls.ensureReady, 0);
});
test("cold + blacklist-only mode => background refresh (no block)", async () => {
const service = new NostrService();
const ac = makeAccessControl({ hydrated: false, whitelist: false });
service.accessControl = ac;
await service.ensureAccessControlReady();
assert.equal(ac.calls.ensureReady, 1);
assert.equal(ac.calls.waitForReady, 0);
});
test("missing access control is a no-op", async () => {
const service = new NostrService();
service.accessControl = null;
await service.ensureAccessControlReady(); // must not throw
assert.ok(true);
});