mirror of
https://github.com/block/buzz.git
synced 2026-10-31 22:41:12 +00:00
174 lines
4.5 KiB
JavaScript
174 lines
4.5 KiB
JavaScript
import assert from "node:assert/strict";
|
|
import test from "node:test";
|
|
import { finalizeEvent, getPublicKey } from "nostr-tools/pure";
|
|
|
|
import {
|
|
buildProjectRelatedChannelChangeTemplate,
|
|
changeProjectRelatedChannels,
|
|
fetchProjectState,
|
|
parseProjectState,
|
|
} from "./projectState.ts";
|
|
|
|
const RELAY_SECRET = new Uint8Array(32).fill(7);
|
|
const RELAY_PUBKEY = getPublicKey(RELAY_SECRET);
|
|
const OWNER = "a".repeat(64);
|
|
const IDENTITY_ID = "b".repeat(64);
|
|
const COORDINATE = `30621:${OWNER}:buzz`;
|
|
const CHANNEL_A = "11111111-1111-4111-8111-111111111111";
|
|
const CHANNEL_B = "22222222-2222-4222-8222-222222222222";
|
|
|
|
function stateEvent({
|
|
revision = "1",
|
|
identityId = IDENTITY_ID,
|
|
content,
|
|
} = {}) {
|
|
return finalizeEvent(
|
|
{
|
|
kind: 30623,
|
|
created_at: 100,
|
|
content:
|
|
content ??
|
|
JSON.stringify({
|
|
v: 1,
|
|
deleted: false,
|
|
project_tags: [
|
|
["d", "buzz"],
|
|
["name", "Buzz"],
|
|
["buzz-related-channel", CHANNEL_A],
|
|
],
|
|
}),
|
|
tags: [
|
|
["d", "c".repeat(64)],
|
|
["a", COORDINATE],
|
|
["rev", revision],
|
|
["e", identityId, "", "identity"],
|
|
["e", "d".repeat(64), "", "change"],
|
|
],
|
|
},
|
|
RELAY_SECRET,
|
|
);
|
|
}
|
|
|
|
test("parseProjectState accepts a signed exact-coordinate strict v1 projection", () => {
|
|
const state = parseProjectState(stateEvent(), RELAY_PUBKEY, COORDINATE);
|
|
assert.equal(state.revision, "1");
|
|
assert.equal(state.identityEventId, IDENTITY_ID);
|
|
assert.deepEqual(state.projectTags.at(-1), [
|
|
"buzz-related-channel",
|
|
CHANNEL_A,
|
|
]);
|
|
});
|
|
|
|
test("parseProjectState rejects wrong authors, signatures, revisions, coordinates, and unknown content fields", () => {
|
|
assert.throws(
|
|
() => parseProjectState(stateEvent(), "e".repeat(64), COORDINATE),
|
|
/signed by this relay/,
|
|
);
|
|
const badSignature = JSON.parse(JSON.stringify(stateEvent()));
|
|
badSignature.sig = "0".repeat(128);
|
|
assert.throws(
|
|
() => parseProjectState(badSignature, RELAY_PUBKEY, COORDINATE),
|
|
/signed by this relay/,
|
|
);
|
|
assert.throws(
|
|
() =>
|
|
parseProjectState(
|
|
stateEvent({ revision: "01" }),
|
|
RELAY_PUBKEY,
|
|
COORDINATE,
|
|
),
|
|
/revision is not canonical/,
|
|
);
|
|
assert.throws(
|
|
() => parseProjectState(stateEvent(), RELAY_PUBKEY, `30621:${OWNER}:other`),
|
|
/requested coordinate/,
|
|
);
|
|
assert.throws(
|
|
() =>
|
|
parseProjectState(
|
|
stateEvent({
|
|
content: JSON.stringify({
|
|
v: 1,
|
|
deleted: false,
|
|
project_tags: [],
|
|
extra: true,
|
|
}),
|
|
}),
|
|
RELAY_PUBKEY,
|
|
COORDINATE,
|
|
),
|
|
/strict version 1/,
|
|
);
|
|
});
|
|
|
|
test("buildProjectRelatedChannelChangeTemplate emits the exact sorted kind:47010 command", () => {
|
|
assert.deepEqual(
|
|
buildProjectRelatedChannelChangeTemplate(
|
|
COORDINATE,
|
|
{ add: [CHANNEL_B, CHANNEL_A], remove: [] },
|
|
"7",
|
|
),
|
|
{
|
|
kind: 47010,
|
|
tags: [
|
|
["a", COORDINATE],
|
|
["expected-revision", "7"],
|
|
],
|
|
content: JSON.stringify({
|
|
v: 1,
|
|
patch: {
|
|
related_channels: { add: [CHANNEL_A, CHANNEL_B], remove: [] },
|
|
},
|
|
}),
|
|
},
|
|
);
|
|
});
|
|
|
|
test("fetchProjectState distinguishes absence from a present untrusted projection", async () => {
|
|
const deps = {
|
|
getRelayPubkey: async () => RELAY_PUBKEY,
|
|
fetchEvents: async () => [],
|
|
};
|
|
assert.equal(await fetchProjectState(COORDINATE, deps), null);
|
|
|
|
await assert.rejects(
|
|
fetchProjectState(COORDINATE, {
|
|
...deps,
|
|
fetchEvents: async () => [{ ...stateEvent(), content: "{}" }],
|
|
}),
|
|
/untrusted or unsupported/,
|
|
);
|
|
});
|
|
|
|
test("changeProjectRelatedChannels refetches and retries one revision conflict", async () => {
|
|
const states = [stateEvent({ revision: "4" }), stateEvent({ revision: "5" })];
|
|
const signedTemplates = [];
|
|
let fetchCount = 0;
|
|
let publishCount = 0;
|
|
await changeProjectRelatedChannels(
|
|
{ projectAddress: COORDINATE },
|
|
{ add: [CHANNEL_B], remove: [] },
|
|
{
|
|
getRelayPubkey: async () => RELAY_PUBKEY,
|
|
fetchEvents: async () => [states[fetchCount++]],
|
|
signEvent: async (template) => {
|
|
signedTemplates.push(template);
|
|
return stateEvent();
|
|
},
|
|
publishEvent: async () => {
|
|
publishCount += 1;
|
|
if (publishCount === 1) {
|
|
throw new Error("conflict: Project revision is 5");
|
|
}
|
|
},
|
|
},
|
|
);
|
|
|
|
assert.equal(fetchCount, 2);
|
|
assert.equal(publishCount, 2);
|
|
assert.deepEqual(
|
|
signedTemplates.map((template) => template.tags[1][1]),
|
|
["4", "5"],
|
|
);
|
|
});
|