mirror of
https://github.com/PR0M3TH3AN/bitvid.git
synced 2026-10-27 07:52:24 +00:00
First slice of structured submissions (replacing the kind-4 encrypted DM forms). Pure, hermetic core — no UI or network yet. - nostrEventSchemas.js: SUBMISSION_KIND=30083 (+ NOTE_TYPES.SUBMISSION, BASE_SCHEMAS entry) and buildSubmissionEvent(). PUBLIC + addressable; data in tags: d (id), k (type), applicant (npub), p (admin), t=bitvid-submission; free-text in content. - js/submissions/submissionService.js: SUBMISSION_TYPES + labels, parseSubmissionEvent, buildSubmission, generateSubmissionId. Pure — unit-testable with no relays/signer. - constants.js: FEATURE_SUBMISSIONS (default OFF until the admin UI + forms ship). - tests/submission-service.test.mjs: 8 scenarios (build tags/type/default, d-tag required, parse structure/reject/unknown-type→other, build→parse round-trip of every type, id generation). - check-file-size.mjs: bump nostrEventSchemas grandfather 3250→3541 (adding a new event type to the schema registry is the prescribed pattern). - docs/nostr-event-schemas.md: catalogue row. Schema validation ✨, lint, build all pass; tests 8/8. Co-Authored-By: Claude Opus 4.8 <[email protected]>
119 lines
3.7 KiB
JavaScript
119 lines
3.7 KiB
JavaScript
import { test } from "node:test";
|
|
import assert from "node:assert/strict";
|
|
|
|
import {
|
|
SUBMISSION_KIND,
|
|
SUBMISSION_TYPES,
|
|
parseSubmissionEvent,
|
|
buildSubmission,
|
|
generateSubmissionId,
|
|
} from "../js/submissions/submissionService.js";
|
|
import { buildSubmissionEvent } from "../js/nostrEventSchemas.js";
|
|
|
|
const EPHEMERAL = "e".repeat(64);
|
|
const ADMIN = "a".repeat(64);
|
|
const APPLICANT = "npub1exampleapplicant";
|
|
|
|
test("buildSubmissionEvent emits kind, d, type, applicant, recipient, topic", () => {
|
|
const event = buildSubmissionEvent({
|
|
pubkey: EPHEMERAL,
|
|
created_at: 1000,
|
|
dTagValue: "sub-1",
|
|
submissionType: "application",
|
|
applicantNpub: APPLICANT,
|
|
recipientPubkey: ADMIN,
|
|
content: "Please whitelist me, I make Blender tutorials.",
|
|
});
|
|
|
|
assert.equal(event.kind, SUBMISSION_KIND);
|
|
assert.equal(event.tags.find((t) => t[0] === "d")?.[1], "sub-1");
|
|
assert.equal(event.tags.find((t) => t[0] === "t")?.[1], "bitvid-submission");
|
|
assert.equal(event.tags.find((t) => t[0] === "k")?.[1], "application");
|
|
assert.equal(event.tags.find((t) => t[0] === "applicant")?.[1], APPLICANT);
|
|
assert.equal(event.tags.find((t) => t[0] === "p")?.[1], ADMIN);
|
|
assert.equal(event.content, "Please whitelist me, I make Blender tutorials.");
|
|
});
|
|
|
|
test("buildSubmissionEvent requires a d-tag", () => {
|
|
assert.throws(
|
|
() => buildSubmissionEvent({ pubkey: EPHEMERAL, created_at: 1, dTagValue: " " }),
|
|
/requires a d-tag/i,
|
|
);
|
|
});
|
|
|
|
test("buildSubmissionEvent defaults type to application", () => {
|
|
const event = buildSubmissionEvent({
|
|
pubkey: EPHEMERAL,
|
|
created_at: 1,
|
|
dTagValue: "d",
|
|
});
|
|
assert.equal(event.tags.find((t) => t[0] === "k")?.[1], "application");
|
|
});
|
|
|
|
test("parseSubmissionEvent structures a submission", () => {
|
|
const event = buildSubmissionEvent({
|
|
pubkey: EPHEMERAL,
|
|
created_at: 4242,
|
|
dTagValue: "sub-7",
|
|
submissionType: "bug",
|
|
applicantNpub: APPLICANT,
|
|
recipientPubkey: ADMIN,
|
|
content: "the zap button is off-center",
|
|
});
|
|
event.id = "event-id-9";
|
|
|
|
const parsed = parseSubmissionEvent(event);
|
|
assert.equal(parsed.id, "sub-7");
|
|
assert.equal(parsed.eventId, "event-id-9");
|
|
assert.equal(parsed.type, "bug");
|
|
assert.equal(parsed.applicant, APPLICANT);
|
|
assert.equal(parsed.recipient, ADMIN);
|
|
assert.equal(parsed.content, "the zap button is off-center");
|
|
assert.equal(parsed.createdAt, 4242);
|
|
assert.equal(parsed.pubkey, EPHEMERAL);
|
|
});
|
|
|
|
test("parseSubmissionEvent normalizes an unknown type to 'other'", () => {
|
|
const parsed = parseSubmissionEvent({
|
|
kind: SUBMISSION_KIND,
|
|
pubkey: EPHEMERAL,
|
|
created_at: 1,
|
|
tags: [["d", "x"], ["k", "totally-made-up"]],
|
|
});
|
|
assert.equal(parsed.type, "other");
|
|
});
|
|
|
|
test("parseSubmissionEvent rejects the wrong kind and a missing d", () => {
|
|
assert.equal(parseSubmissionEvent({ kind: 1, tags: [["d", "x"]] }), null);
|
|
assert.equal(
|
|
parseSubmissionEvent({ kind: SUBMISSION_KIND, tags: [["k", "application"]] }),
|
|
null,
|
|
);
|
|
assert.equal(parseSubmissionEvent(null), null);
|
|
});
|
|
|
|
test("build → parse round-trips every known type", () => {
|
|
for (const type of Object.values(SUBMISSION_TYPES)) {
|
|
const event = buildSubmission({
|
|
pubkey: EPHEMERAL,
|
|
id: `rt-${type}`,
|
|
type,
|
|
applicantNpub: APPLICANT,
|
|
recipientPubkey: ADMIN,
|
|
content: `body for ${type}`,
|
|
created_at: 5,
|
|
});
|
|
const parsed = parseSubmissionEvent({ ...event, id: `signed-${type}` });
|
|
assert.equal(parsed.type, type);
|
|
assert.equal(parsed.content, `body for ${type}`);
|
|
assert.equal(parsed.applicant, APPLICANT);
|
|
}
|
|
});
|
|
|
|
test("generateSubmissionId produces distinct, tag-safe ids", () => {
|
|
const a = generateSubmissionId();
|
|
const b = generateSubmissionId();
|
|
assert.notEqual(a, b);
|
|
assert.match(a, /^sub-[0-9a-z]+-[0-9a-z]+$/);
|
|
});
|