Files
bitvid/tests/submission-service.test.mjs
T
thePR0M3TH3ANandClaude Opus 4.8 a18230eb70 submissions(#23): foundation — kind-30083 schema, builder, service + tests
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]>
2026-07-06 20:17:47 -04:00

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]+$/);
});