mirror of
https://github.com/PR0M3TH3AN/bitgate.git
synced 2026-11-06 13:31:00 +00:00
Event formats live in codecs so the core never learns which kind stores roles, which d identifier is used, or how a legacy list is shaped. - bech32/NIP-19 decoding, verified against the NIP-19 test vector, so the core can stay hex-only and dependency-free - canonical v1 codec on kind:30078 with namespaced d identifiers, role rosters carrying both role labels and explicit capabilities, and size-bounded policy documents that return null rather than throwing on malformed JSON - NIP-56 reports, resolving the report type across the three tag layouts clients use and excluding relay hints from the type position - NIP-51 mute lists; encrypted entries are flagged, never decoded - legacy admin-list codec supporting all five migration stages, with editors surfaced separately so a UI permission cannot silently become signing authority - replaceable selection with a deterministic same-timestamp tiebreak, without which two relays returning events in different orders would produce different state on different clients 129 codec tests; 402 total.
187 lines
5.7 KiB
JavaScript
187 lines
5.7 KiB
JavaScript
import { describe, expect, it } from "vitest";
|
|
|
|
import { decodeContribution } from "../src/canonical.js";
|
|
import {
|
|
LEGACY_COMMUNITY_SOURCES_IDENTIFIER,
|
|
LEGACY_EDITORS_IDENTIFIER,
|
|
LEGACY_KIND,
|
|
decodeLegacyCommunitySources,
|
|
decodeLegacyEditors,
|
|
decodeLegacyList,
|
|
decodeMixedContributions,
|
|
isLegacyAdminEvent,
|
|
} from "../src/legacy.js";
|
|
|
|
const ROOT = "a1".repeat(32);
|
|
const CURATOR = "c3".repeat(32);
|
|
const CREATOR = "d4".repeat(32);
|
|
const EVENT_ID = "1b".repeat(32);
|
|
const NPUB = "npub180cvv07tjdrrgpa0j7j7tmnyl2yr6yr7l8j4s3evf6u64th6gkwsyjh6w6";
|
|
const NPUB_HEX = "3bf0c63fcb93463407af97a5e5ee64fa883d107ef9e558472c4eb9aaaefa459d";
|
|
|
|
/**
|
|
* @param {Partial<import('../src/replaceable.js').NostrEvent>} parts
|
|
* @returns {import('../src/replaceable.js').NostrEvent}
|
|
*/
|
|
const event = (parts) => ({
|
|
id: "00".repeat(32),
|
|
pubkey: ROOT,
|
|
kind: LEGACY_KIND,
|
|
created_at: 1000,
|
|
tags: [],
|
|
content: "",
|
|
...parts,
|
|
});
|
|
|
|
describe("isLegacyAdminEvent", () => {
|
|
it("recognizes the known legacy lists", () => {
|
|
for (const identifier of [
|
|
"bitvid:admin:whitelist",
|
|
"bitvid:admin:blacklist",
|
|
"bitvid:admin:event-blacklist",
|
|
LEGACY_EDITORS_IDENTIFIER,
|
|
LEGACY_COMMUNITY_SOURCES_IDENTIFIER,
|
|
]) {
|
|
expect(isLegacyAdminEvent(event({ tags: [["d", identifier]] }))).toBe(true);
|
|
}
|
|
});
|
|
|
|
it("rejects unrelated lists", () => {
|
|
expect(isLegacyAdminEvent(event({ tags: [["d", "bitvid:playlist"]] }))).toBe(false);
|
|
});
|
|
|
|
it("rejects the wrong kind", () => {
|
|
expect(isLegacyAdminEvent(event({ kind: 30078, tags: [["d", "bitvid:admin:blacklist"]] }))).toBe(
|
|
false,
|
|
);
|
|
});
|
|
});
|
|
|
|
describe("decodeLegacyList", () => {
|
|
it("maps the blacklist onto a user denial", () => {
|
|
const contribution = decodeLegacyList(
|
|
event({ tags: [["d", "bitvid:admin:blacklist"], ["p", CREATOR]] }),
|
|
);
|
|
expect(contribution).toMatchObject({
|
|
actor: ROOT,
|
|
kind: "user-deny",
|
|
targets: [{ type: "user", pubkey: CREATOR }],
|
|
});
|
|
});
|
|
|
|
it("maps the whitelist onto a user allowance", () => {
|
|
const contribution = decodeLegacyList(
|
|
event({ tags: [["d", "bitvid:admin:whitelist"], ["p", CREATOR]] }),
|
|
);
|
|
expect(contribution?.kind).toBe("user-allow");
|
|
});
|
|
|
|
it("maps the event blacklist onto an event denial", () => {
|
|
const contribution = decodeLegacyList(
|
|
event({ tags: [["d", "bitvid:admin:event-blacklist"], ["e", EVENT_ID]] }),
|
|
);
|
|
expect(contribution?.targets).toEqual([{ type: "event", id: EVENT_ID }]);
|
|
});
|
|
|
|
it("normalizes npub entries to hex", () => {
|
|
const contribution = decodeLegacyList(
|
|
event({ tags: [["d", "bitvid:admin:blacklist"], ["p", NPUB]] }),
|
|
);
|
|
expect(contribution?.targets).toEqual([{ type: "user", pubkey: NPUB_HEX }]);
|
|
});
|
|
|
|
it("treats hex and npub entries as the same target", () => {
|
|
const fromNpub = decodeLegacyList(
|
|
event({ tags: [["d", "bitvid:admin:blacklist"], ["p", NPUB]] }),
|
|
);
|
|
const fromHex = decodeLegacyList(
|
|
event({ tags: [["d", "bitvid:admin:blacklist"], ["p", NPUB_HEX]] }),
|
|
);
|
|
expect(fromNpub?.targets).toEqual(fromHex?.targets);
|
|
});
|
|
|
|
it("returns null for the editor roster", () => {
|
|
expect(decodeLegacyList(event({ tags: [["d", LEGACY_EDITORS_IDENTIFIER]] }))).toBeNull();
|
|
});
|
|
|
|
it("returns null for an unknown identifier", () => {
|
|
expect(decodeLegacyList(event({ tags: [["d", "bitvid:other"]] }))).toBeNull();
|
|
});
|
|
|
|
it("skips malformed entries", () => {
|
|
const contribution = decodeLegacyList(
|
|
event({ tags: [["d", "bitvid:admin:blacklist"], ["p", "junk"], ["p", CREATOR]] }),
|
|
);
|
|
expect(contribution?.targets).toHaveLength(1);
|
|
});
|
|
});
|
|
|
|
describe("decodeLegacyEditors", () => {
|
|
it("returns editors as plain pubkeys", () => {
|
|
expect(
|
|
decodeLegacyEditors(event({ tags: [["d", LEGACY_EDITORS_IDENTIFIER], ["p", CREATOR]] })),
|
|
).toEqual([CREATOR]);
|
|
});
|
|
|
|
it("deduplicates", () => {
|
|
expect(
|
|
decodeLegacyEditors(
|
|
event({ tags: [["d", LEGACY_EDITORS_IDENTIFIER], ["p", CREATOR], ["p", CREATOR]] }),
|
|
),
|
|
).toHaveLength(1);
|
|
});
|
|
|
|
it("returns nothing for other lists", () => {
|
|
expect(decodeLegacyEditors(event({ tags: [["d", "bitvid:admin:blacklist"]] }))).toEqual([]);
|
|
});
|
|
});
|
|
|
|
describe("decodeLegacyCommunitySources", () => {
|
|
it("decodes curator list references", () => {
|
|
const sources = decodeLegacyCommunitySources(
|
|
event({
|
|
tags: [
|
|
["d", LEGACY_COMMUNITY_SOURCES_IDENTIFIER],
|
|
["a", `30000:${CURATOR}:bitvid:admin:blacklist`],
|
|
],
|
|
}),
|
|
);
|
|
expect(sources).toEqual([
|
|
{ curator: CURATOR, identifier: "bitvid:admin:blacklist", kind: 30000 },
|
|
]);
|
|
});
|
|
|
|
it("skips malformed coordinates", () => {
|
|
const sources = decodeLegacyCommunitySources(
|
|
event({ tags: [["d", LEGACY_COMMUNITY_SOURCES_IDENTIFIER], ["a", "garbage"]] }),
|
|
);
|
|
expect(sources).toEqual([]);
|
|
});
|
|
|
|
it("returns nothing for other lists", () => {
|
|
expect(decodeLegacyCommunitySources(event({ tags: [["d", "bitvid:admin:blacklist"]] }))).toEqual(
|
|
[],
|
|
);
|
|
});
|
|
});
|
|
|
|
describe("decodeMixedContributions", () => {
|
|
it("reads legacy and canonical events side by side", () => {
|
|
const legacy = event({ tags: [["d", "bitvid:admin:blacklist"], ["p", CREATOR]] });
|
|
const canonical = event({
|
|
kind: 30078,
|
|
tags: [["d", "app:governance:user-deny:v1"], ["p", CREATOR]],
|
|
});
|
|
|
|
const contributions = decodeMixedContributions([legacy, canonical], decodeContribution);
|
|
expect(contributions).toHaveLength(2);
|
|
expect(contributions.every((entry) => entry.kind === "user-deny")).toBe(true);
|
|
});
|
|
|
|
it("ignores events neither codec recognizes", () => {
|
|
expect(decodeMixedContributions([event({ tags: [["d", "unknown"]] })], decodeContribution)).toEqual(
|
|
[],
|
|
);
|
|
});
|
|
});
|