mirror of
https://github.com/PR0M3TH3AN/bitvid.git
synced 2026-11-10 06:43:00 +00:00
Chunk watch history payloads and rebuild fetch pipeline
This commit is contained in:
1 parent
dfa260c2d0
commit
034c4c3761
4 files changed
+636
-167
No files matched your search
@@ -24,8 +24,11 @@ const { nostrClient } = await import("../js/nostr.js");
|
||||
|
||||
function createPublishingClient(actorPubkey) {
|
||||
const client = new nostrClient.constructor();
|
||||
const publishedEvents = [];
|
||||
const payloads = [];
|
||||
client.pool = {
|
||||
publish(urls, event) {
|
||||
publishedEvents.push(event);
|
||||
return {
|
||||
on(eventName, handler) {
|
||||
if (eventName === "ok" || eventName === "seen") {
|
||||
@@ -39,14 +42,14 @@ function createPublishingClient(actorPubkey) {
|
||||
client.pubkey = "";
|
||||
client.sessionActor = { pubkey: actorPubkey, privateKey: "unit-secret" };
|
||||
client.ensureSessionActor = async () => actorPubkey;
|
||||
client.encryptWatchHistoryPayload = async () => ({
|
||||
ok: true,
|
||||
ciphertext: "ciphertext",
|
||||
});
|
||||
client.encryptWatchHistoryPayload = async (_actor, payload) => {
|
||||
payloads.push(payload);
|
||||
return { ok: true, ciphertext: JSON.stringify(payload) };
|
||||
};
|
||||
client.persistWatchHistoryEntry = () => {};
|
||||
client.cancelWatchHistoryRepublish = () => {};
|
||||
client.scheduleWatchHistoryRepublish = () => {};
|
||||
return client;
|
||||
return { client, publishedEvents, payloads };
|
||||
}
|
||||
|
||||
function createDecryptClient(actorPubkey) {
|
||||
@@ -57,56 +60,102 @@ function createDecryptClient(actorPubkey) {
|
||||
}
|
||||
|
||||
const ACTOR = "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef";
|
||||
const POINTER_A = {
|
||||
type: "a",
|
||||
value: `30078:${ACTOR}:watch-history`,
|
||||
relay: "wss://relay.unit",
|
||||
};
|
||||
const POINTER_E = { type: "e", value: "event-id-123", relay: null };
|
||||
|
||||
const publishingClient = createPublishingClient(ACTOR);
|
||||
const { client: publishingClient, publishedEvents, payloads } =
|
||||
createPublishingClient(ACTOR);
|
||||
|
||||
publishingClient.watchHistoryPayloadMaxBytes = 400;
|
||||
publishingClient.watchHistoryFetchEventLimit = 10;
|
||||
|
||||
const longPointers = Array.from({ length: 8 }, (_, index) => {
|
||||
const relay = index % 3 === 0 ? "wss://relay.unit" : null;
|
||||
if (index % 2 === 0) {
|
||||
return {
|
||||
type: "e",
|
||||
value: `event-${index}-${"x".repeat(90)}`,
|
||||
relay,
|
||||
};
|
||||
}
|
||||
return {
|
||||
type: "a",
|
||||
value: `30078:${ACTOR}:history-${index}-${"y".repeat(60)}`,
|
||||
relay,
|
||||
};
|
||||
});
|
||||
|
||||
const publishResult = await publishingClient.publishWatchHistorySnapshot(
|
||||
ACTOR,
|
||||
[POINTER_A, POINTER_E]
|
||||
longPointers
|
||||
);
|
||||
|
||||
assert.equal(publishResult.ok, true, "publish should report success");
|
||||
assert(publishResult.events.length > 1, "snapshot should chunk into multiple events");
|
||||
assert.equal(
|
||||
publishResult.events.length,
|
||||
payloads.length,
|
||||
"each chunk should produce an encryption payload"
|
||||
);
|
||||
|
||||
const pointerTags = publishResult.event.tags.filter((tag) =>
|
||||
tag && (tag[0] === "a" || tag[0] === "e")
|
||||
payloads.forEach((payload) => {
|
||||
const size = JSON.stringify(payload).length;
|
||||
assert(
|
||||
size <= publishingClient.watchHistoryPayloadMaxBytes,
|
||||
`payload size ${size} should respect cap`
|
||||
);
|
||||
assert.equal(payload.version, 2, "chunk payload should use version 2");
|
||||
});
|
||||
|
||||
const snapshotIds = new Set(
|
||||
publishResult.events.map((event) =>
|
||||
event.tags.find((tag) => Array.isArray(tag) && tag[0] === "snapshot")?.[1]
|
||||
)
|
||||
);
|
||||
assert.equal(snapshotIds.size, 1, "all chunks should share a snapshot id");
|
||||
|
||||
const headTag = publishResult.event.tags.find(
|
||||
(tag) => Array.isArray(tag) && tag[0] === "head"
|
||||
);
|
||||
assert(headTag, "head chunk should include head tag");
|
||||
|
||||
delete globalThis.window.nostr;
|
||||
|
||||
if (!globalThis.window.NostrTools.nip04) {
|
||||
globalThis.window.NostrTools.nip04 = {};
|
||||
}
|
||||
|
||||
globalThis.window.NostrTools.nip04.decrypt = async (
|
||||
_priv,
|
||||
_pub,
|
||||
ciphertext
|
||||
) => ciphertext;
|
||||
|
||||
const decryptClient = createDecryptClient(ACTOR);
|
||||
decryptClient.relays = ["wss://unit.test"];
|
||||
decryptClient.watchHistoryFetchEventLimit = 10;
|
||||
decryptClient.pool = {
|
||||
list: async () => publishedEvents,
|
||||
};
|
||||
|
||||
const fetched = await decryptClient.fetchWatchHistory(ACTOR);
|
||||
|
||||
assert.equal(
|
||||
fetched.items.length,
|
||||
longPointers.length,
|
||||
"fetch should reassemble all chunked items"
|
||||
);
|
||||
assert.deepEqual(pointerTags, [
|
||||
["a", POINTER_A.value, POINTER_A.relay],
|
||||
["e", POINTER_E.value],
|
||||
]);
|
||||
|
||||
assert.deepEqual(
|
||||
publishResult.items.map((item) => ({
|
||||
fetched.items.map((item) => ({
|
||||
type: item.type,
|
||||
value: item.value,
|
||||
relay: item.relay || null,
|
||||
})),
|
||||
[
|
||||
{ type: "a", value: POINTER_A.value, relay: POINTER_A.relay },
|
||||
{ type: "e", value: POINTER_E.value, relay: null },
|
||||
]
|
||||
longPointers.map((item) => ({
|
||||
type: item.type,
|
||||
value: item.value,
|
||||
relay: item.relay || null,
|
||||
})),
|
||||
"chunked fetch should preserve pointer order"
|
||||
);
|
||||
|
||||
delete globalThis.window.nostr;
|
||||
delete globalThis.window.NostrTools.nip04;
|
||||
|
||||
const decryptClient = createDecryptClient(ACTOR);
|
||||
const decrypted = await decryptClient.decryptWatchHistoryEvent(
|
||||
publishResult.event,
|
||||
ACTOR
|
||||
);
|
||||
|
||||
assert.deepEqual(decrypted, {
|
||||
version: 0,
|
||||
items: [
|
||||
{ type: "a", value: POINTER_A.value, relay: POINTER_A.relay },
|
||||
{ type: "e", value: POINTER_E.value, relay: null },
|
||||
],
|
||||
});
|
||||
|
||||
console.log("watch history tests passed");
|
||||
Reference in new issue
Block a user