Files
bitvid/tests/nostr/toolkit.test.mjs
T

255 lines
7.0 KiB
JavaScript

import assert from "node:assert/strict";
import test from "node:test";
const originalCanonical = globalThis.__BITVID_CANONICAL_NOSTR_TOOLS__;
const originalNostrTools = globalThis.NostrTools;
const originalNostrToolsReady = globalThis.nostrToolsReady;
class BootstrapSimplePool {
sub() {}
close() {}
}
const bootstrapToolkit = {
ok: true,
nip19: {
decode: (value) => value,
npubEncode: (value) => value,
},
SimplePool: BootstrapSimplePool,
};
globalThis.__BITVID_CANONICAL_NOSTR_TOOLS__ = bootstrapToolkit;
globalThis.NostrTools = bootstrapToolkit;
globalThis.nostrToolsReady = Promise.resolve(bootstrapToolkit);
const toolkitModule = await import("../../js/nostr/toolkit.js");
const {
rememberNostrTools,
getCachedNostrTools,
ensureNostrTools,
resolveSimplePoolConstructor,
shimLegacySimplePoolMethods,
} = toolkitModule;
const baselineToolkit = getCachedNostrTools() || bootstrapToolkit;
test.after(() => {
if (originalCanonical === undefined) {
delete globalThis.__BITVID_CANONICAL_NOSTR_TOOLS__;
} else {
globalThis.__BITVID_CANONICAL_NOSTR_TOOLS__ = originalCanonical;
}
if (originalNostrTools === undefined) {
delete globalThis.NostrTools;
} else {
globalThis.NostrTools = originalNostrTools;
}
if (originalNostrToolsReady === undefined) {
delete globalThis.nostrToolsReady;
} else {
globalThis.nostrToolsReady = originalNostrToolsReady;
}
rememberNostrTools(baselineToolkit);
});
test("ensureNostrTools resolves the cached toolkit", async () => {
class CachedSimplePool {
sub() {}
close() {}
}
const cachedToolkit = {
ok: true,
nip19: {
decode: (value) => value,
npubEncode: (value) => value,
},
SimplePool: CachedSimplePool,
};
const previousCanonicalForTest = globalThis.__BITVID_CANONICAL_NOSTR_TOOLS__;
const previousNostrToolsForTest = globalThis.NostrTools;
globalThis.__BITVID_CANONICAL_NOSTR_TOOLS__ = cachedToolkit;
globalThis.NostrTools = cachedToolkit;
rememberNostrTools(cachedToolkit);
try {
const ensured = await ensureNostrTools();
assert.strictEqual(ensured, cachedToolkit);
} finally {
if (previousCanonicalForTest === undefined) {
delete globalThis.__BITVID_CANONICAL_NOSTR_TOOLS__;
} else {
globalThis.__BITVID_CANONICAL_NOSTR_TOOLS__ = previousCanonicalForTest;
}
if (previousNostrToolsForTest === undefined) {
delete globalThis.NostrTools;
} else {
globalThis.NostrTools = previousNostrToolsForTest;
}
rememberNostrTools(baselineToolkit);
}
});
test("resolveSimplePoolConstructor handles supported permutations", () => {
class MockSimplePool {
sub() {}
close() {}
}
const variations = [
{ label: "tools.SimplePool", tools: { SimplePool: MockSimplePool } },
{ label: "tools.pool.SimplePool", tools: { pool: { SimplePool: MockSimplePool } } },
{ label: "tools.pool constructor", tools: { pool: MockSimplePool } },
{
label: "tools.SimplePool.SimplePool",
tools: { SimplePool: { SimplePool: MockSimplePool } },
},
{
label: "tools.SimplePool.default",
tools: { SimplePool: { default: MockSimplePool } },
},
{ label: "tools.pool.default", tools: { pool: { default: MockSimplePool } } },
{
label: "tools.default.SimplePool",
tools: { default: { SimplePool: MockSimplePool } },
},
{
label: "tools.default.pool.SimplePool",
tools: { default: { pool: { SimplePool: MockSimplePool } } },
},
{
label: "tools.default.pool constructor",
tools: { default: { pool: MockSimplePool } },
},
];
for (const { label, tools } of variations) {
assert.strictEqual(
resolveSimplePoolConstructor(tools),
MockSimplePool,
`expected ${label}`
);
}
assert.strictEqual(
resolveSimplePoolConstructor({}, { SimplePool: MockSimplePool }),
MockSimplePool,
"scope.SimplePool fallback"
);
assert.strictEqual(
resolveSimplePoolConstructor({}, { pool: { SimplePool: MockSimplePool } }),
MockSimplePool,
"scope.pool.SimplePool fallback"
);
assert.strictEqual(
resolveSimplePoolConstructor(
{},
{ NostrTools: { pool: { SimplePool: MockSimplePool } } }
),
MockSimplePool,
"scope.NostrTools.pool.SimplePool fallback"
);
assert.strictEqual(
resolveSimplePoolConstructor({}, {}),
null,
"returns null when no candidates are available"
);
});
test("shimLegacySimplePoolMethods adds legacy sub/list wrappers", async () => {
let capturedRequests = null;
let capturedParams = null;
const closeReasons = [];
const mockPool = {
subscribeMap(requests, params) {
capturedRequests = requests;
capturedParams = params;
return {
close(reason) {
closeReasons.push(reason);
},
};
},
};
shimLegacySimplePoolMethods(mockPool);
assert.strictEqual(typeof mockPool.sub, "function");
assert.strictEqual(typeof mockPool.list, "function");
const relays = ["wss://relay.one", "wss://relay.two"];
const filters = [{ kinds: [1] }, { authors: ["abc"] }];
const sub = mockPool.sub(relays, filters, {
alreadyHaveEvent(id) {
return id === "skip";
},
});
assert.ok(Array.isArray(capturedRequests));
assert.strictEqual(capturedRequests.length, relays.length * filters.length);
assert.ok(capturedRequests.every((entry) => entry && entry.url && entry.filter));
assert.ok(capturedParams);
assert.strictEqual(typeof capturedParams.onevent, "function");
assert.strictEqual(typeof capturedParams.oneose, "function");
assert.strictEqual(typeof capturedParams.onclose, "function");
assert.strictEqual(typeof capturedParams.alreadyHaveEvent, "function");
const receivedEvents = [];
const eosePromise = new Promise((resolve) => sub.on("eose", resolve));
const closePromise = new Promise((resolve) => sub.on("close", resolve));
sub.on("event", (event) => {
receivedEvents.push(event.id);
});
assert.strictEqual(capturedParams.alreadyHaveEvent("skip"), true);
assert.strictEqual(capturedParams.alreadyHaveEvent("id-1"), false);
assert.strictEqual(capturedParams.alreadyHaveEvent("id-1"), true);
capturedParams.onevent({ id: "id-1" });
capturedParams.onevent({ id: "id-2" });
capturedParams.oneose();
capturedParams.onclose(["done"]);
await eosePromise;
const closePayload = await closePromise;
assert.deepStrictEqual(receivedEvents, ["id-1", "id-2"]);
assert.deepStrictEqual(closePayload, ["done"]);
sub.unsub();
closeReasons.length = 0;
const immediateUnsub = mockPool.sub(["wss://relay.extra"], [{ kinds: [2] }]);
immediateUnsub.unsub();
assert.ok(closeReasons.includes("closed by caller"));
capturedParams = null;
capturedRequests = null;
closeReasons.length = 0;
const listPromise = mockPool.list(["wss://relay.example"], [{ kinds: [1] }]);
assert.ok(capturedParams);
capturedParams.onevent({ id: "evt-1" });
capturedParams.onevent({ id: "evt-2" });
capturedParams.oneose();
const listed = await listPromise;
assert.deepStrictEqual(
listed.map((event) => event.id),
["evt-1", "evt-2"]
);
});