Files
bitvid/tests/nostr-view-events.test.mjs
T

105 lines
3.4 KiB
JavaScript

// Run with: node tests/nostr-view-events.test.mjs
import "./test-helpers/setup-localstorage.mjs";
import assert from "node:assert/strict";
const {
deriveViewEventBucketIndex,
hasRecentViewPublish,
rememberViewPublish,
createVideoViewEventFilters,
getViewEventGuardWindowMs,
} = await import("../js/nostr/viewEvents.js");
async function withMockedNow(initialMs, callback) {
const originalNow = Date.now;
let nowMs = initialMs;
Date.now = () => nowMs;
const setNow = (nextMs) => {
nowMs = nextMs;
};
try {
await callback({ setNow, getNow: () => nowMs });
} finally {
Date.now = originalNow;
}
}
async function testRememberViewPublishHonorsGuardWindow() {
localStorage.clear();
const scope = "a:test-pointer";
const bucket = deriveViewEventBucketIndex(1_700_000_000);
const windowMs = getViewEventGuardWindowMs();
await withMockedNow(1_700_000_000_000, async ({ setNow }) => {
assert.equal(
hasRecentViewPublish(scope, bucket),
false,
"guard should not report recent publish before remembering",
);
rememberViewPublish(scope, bucket);
assert.equal(
hasRecentViewPublish(scope, bucket),
true,
"guard should report recent publish immediately after remembering",
);
const halfWindow = Math.max(1, Math.floor(windowMs / 2));
setNow(1_700_000_000_000 + halfWindow);
assert.equal(
hasRecentViewPublish(scope, bucket),
true,
"guard should continue blocking within the same window",
);
setNow(1_700_000_000_000 + windowMs + 5_000);
assert.equal(
hasRecentViewPublish(scope, bucket),
false,
"guard should allow publishes once the window has elapsed",
);
});
}
function assertPointerFilterStructure(filter, pointerValue, label) {
assert.ok(filter, `${label} filter should be defined`);
assert.ok(Array.isArray(filter.kinds), `${label} filter should specify kinds`);
assert.ok(
Number.isFinite(filter.kinds[0]),
`${label} filter should target a numeric kind`,
);
assert.deepEqual(filter["#t"], ["view"], `${label} filter should require the view tag`);
assert.deepEqual(filter["#a"] ?? filter["#e"], [pointerValue], `${label} filter should bind the pointer value`);
}
async function testCreateVideoViewEventFiltersForAddressPointer() {
const pointerValue = "30079:abc:def";
const pointer = { type: "a", value: pointerValue, relay: "wss://relay.example" };
const { pointer: descriptor, filters } = createVideoViewEventFilters(pointer);
assert.deepEqual(
descriptor,
{ type: "a", value: pointerValue, relay: "wss://relay.example" },
"descriptor should preserve pointer structure",
);
assert.ok(Array.isArray(filters) && filters.length === 1, "default filters should include only the pointer filter");
assertPointerFilterStructure(filters[0], pointerValue, "primary");
}
async function testCreateVideoViewEventFiltersForEventPointer() {
const pointerValue = "legacy-event";
const { filters } = createVideoViewEventFilters({ type: "e", value: pointerValue });
assert.ok(Array.isArray(filters) && filters.length === 1, "filters should include only the pointer filter");
assertPointerFilterStructure(filters[0], pointerValue, "primary");
}
await testRememberViewPublishHonorsGuardWindow();
await testCreateVideoViewEventFiltersForAddressPointer();
await testCreateVideoViewEventFiltersForEventPointer();
console.log("nostr view events tests passed");