mirror of
https://github.com/PR0M3TH3AN/bitvid.git
synced 2026-10-27 07:52:24 +00:00
chore(ai): event schema validation tool + fixes
- Created scripts/agent/validate-events.mjs to validate event builders against schemas. - Added schemas and builders for Kind 5 (Deletion) and Kind 4 (Legacy DM) to js/nostrEventSchemas.js. - Updated js/nostr/client.js to use new builders for Kind 5 and Kind 4 events. - Updated js/nostr/client.js to use buildVideoPostEvent for video reverts. - Verified all builders produce valid events.
This commit is contained in:
1 parent
a99a9f3231
commit
0458f354de
3 files changed
+121
-455
No files matched your search
+22
-25
@@ -58,6 +58,8 @@ import {
|
||||
buildRepostEvent,
|
||||
buildWatchHistoryEvent,
|
||||
buildDmAttachmentEvent,
|
||||
buildDeletionEvent,
|
||||
buildLegacyDirectMessageEvent,
|
||||
getNostrEventSchema,
|
||||
NOTE_TYPES,
|
||||
} from "../nostrEventSchemas.js";
|
||||
@@ -4986,13 +4988,12 @@ export class NostrClient {
|
||||
return { ok: false, error: "encryption-failed", details };
|
||||
}
|
||||
|
||||
const event = {
|
||||
kind: 4,
|
||||
created_at: Math.floor(Date.now() / 1000),
|
||||
tags: [["p", targetHex]],
|
||||
content: ciphertext,
|
||||
const event = buildLegacyDirectMessageEvent({
|
||||
pubkey: actorHex,
|
||||
};
|
||||
created_at: Math.floor(Date.now() / 1000),
|
||||
recipientPubkey: targetHex,
|
||||
ciphertext,
|
||||
});
|
||||
|
||||
let signedEvent;
|
||||
try {
|
||||
@@ -5831,21 +5832,12 @@ export class NostrClient {
|
||||
mode: oldContent.mode || "live",
|
||||
};
|
||||
|
||||
const tags = [["t", "video"]];
|
||||
if (stableDTag) {
|
||||
// NIP-78 requires replaceable events to publish a stable "d" tag.
|
||||
// Legacy videos may not carry one, so reuse the prior event id to
|
||||
// guarantee the revert stays addressable.
|
||||
tags.push(["d", stableDTag]);
|
||||
}
|
||||
|
||||
const event = {
|
||||
kind: 30078,
|
||||
const event = buildVideoPostEvent({
|
||||
pubkey,
|
||||
created_at: Math.floor(Date.now() / 1000),
|
||||
tags,
|
||||
content: JSON.stringify(contentObject),
|
||||
};
|
||||
dTagValue: stableDTag,
|
||||
content: contentObject,
|
||||
});
|
||||
|
||||
await this.ensureActiveSignerForPubkey(pubkey);
|
||||
|
||||
@@ -6181,17 +6173,22 @@ export class NostrClient {
|
||||
const chunkSize = 100;
|
||||
for (let index = 0; index < identifierRecords.length; index += chunkSize) {
|
||||
const chunk = identifierRecords.slice(index, index + chunkSize);
|
||||
const deleteTags = chunk.map((record) => [record.type, record.value]);
|
||||
const eventIds = chunk
|
||||
.filter((record) => record.type === "e")
|
||||
.map((record) => record.value);
|
||||
const addresses = chunk
|
||||
.filter((record) => record.type === "a")
|
||||
.map((record) => record.value);
|
||||
|
||||
const deleteEvent = {
|
||||
kind: 5,
|
||||
const deleteEvent = buildDeletionEvent({
|
||||
pubkey,
|
||||
created_at: Math.floor(Date.now() / 1000),
|
||||
tags: deleteTags,
|
||||
content: inferredRoot
|
||||
eventIds,
|
||||
addresses,
|
||||
reason: inferredRoot
|
||||
? `Delete video root ${inferredRoot}`
|
||||
: "Delete published video events",
|
||||
};
|
||||
});
|
||||
|
||||
const signedDelete = await queueSignEvent(signer, deleteEvent);
|
||||
const publishResults = await publishEventToRelays(
|
||||
|
||||
@@ -35,6 +35,8 @@ export const NOTE_TYPES = Object.freeze({
|
||||
ADMIN_WHITELIST: "adminWhitelist",
|
||||
PROFILE_METADATA: "profileMetadata",
|
||||
MUTE_LIST: "muteList",
|
||||
DELETION: "deletion",
|
||||
LEGACY_DM: "legacyDm",
|
||||
});
|
||||
|
||||
export const SUBSCRIPTION_LIST_IDENTIFIER = "subscriptions";
|
||||
@@ -551,6 +553,27 @@ const BASE_SCHEMAS = {
|
||||
description: "Optional content (often encrypted) with public p tags.",
|
||||
},
|
||||
},
|
||||
[NOTE_TYPES.DELETION]: {
|
||||
type: NOTE_TYPES.DELETION,
|
||||
label: "Deletion",
|
||||
kind: 5,
|
||||
appendTags: DEFAULT_APPEND_TAGS,
|
||||
content: {
|
||||
format: "text",
|
||||
description: "Reason for deletion.",
|
||||
},
|
||||
},
|
||||
[NOTE_TYPES.LEGACY_DM]: {
|
||||
type: NOTE_TYPES.LEGACY_DM,
|
||||
label: "Legacy Direct Message",
|
||||
kind: 4,
|
||||
appendTags: DEFAULT_APPEND_TAGS,
|
||||
recipientTagName: "p",
|
||||
content: {
|
||||
format: "text",
|
||||
description: "NIP-04 encrypted ciphertext.",
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
let schemaOverrides = {};
|
||||
@@ -1142,6 +1165,82 @@ export function buildMuteListEvent(params) {
|
||||
};
|
||||
}
|
||||
|
||||
export function buildDeletionEvent(params) {
|
||||
const {
|
||||
pubkey,
|
||||
created_at,
|
||||
eventIds = [],
|
||||
addresses = [],
|
||||
reason = "",
|
||||
additionalTags = [],
|
||||
} = params || {};
|
||||
const schema = getNostrEventSchema(NOTE_TYPES.DELETION);
|
||||
const tags = [];
|
||||
|
||||
if (Array.isArray(eventIds)) {
|
||||
eventIds.forEach((id) => {
|
||||
const normalized = normalizePointerIdentifier(id);
|
||||
if (normalized) {
|
||||
tags.push(["e", normalized]);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (Array.isArray(addresses)) {
|
||||
addresses.forEach((addr) => {
|
||||
const normalized = typeof addr === "string" ? addr.trim() : "";
|
||||
if (normalized) {
|
||||
tags.push(["a", normalized]);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
appendSchemaTags(tags, schema);
|
||||
const sanitizedAdditionalTags = sanitizeAdditionalTags(additionalTags);
|
||||
if (sanitizedAdditionalTags.length) {
|
||||
tags.push(...sanitizedAdditionalTags.map((tag) => tag.slice()));
|
||||
}
|
||||
|
||||
return {
|
||||
kind: schema?.kind ?? 5,
|
||||
pubkey,
|
||||
created_at,
|
||||
tags,
|
||||
content: typeof reason === "string" ? reason : "",
|
||||
};
|
||||
}
|
||||
|
||||
export function buildLegacyDirectMessageEvent(params) {
|
||||
const {
|
||||
pubkey,
|
||||
created_at,
|
||||
recipientPubkey,
|
||||
ciphertext = "",
|
||||
additionalTags = [],
|
||||
} = params || {};
|
||||
const schema = getNostrEventSchema(NOTE_TYPES.LEGACY_DM);
|
||||
const tags = [];
|
||||
|
||||
const recipient = normalizePointerIdentifier(recipientPubkey);
|
||||
if (recipient) {
|
||||
tags.push([schema?.recipientTagName || "p", recipient]);
|
||||
}
|
||||
|
||||
appendSchemaTags(tags, schema);
|
||||
const sanitizedAdditionalTags = sanitizeAdditionalTags(additionalTags);
|
||||
if (sanitizedAdditionalTags.length) {
|
||||
tags.push(...sanitizedAdditionalTags.map((tag) => tag.slice()));
|
||||
}
|
||||
|
||||
return {
|
||||
kind: schema?.kind ?? 4,
|
||||
pubkey,
|
||||
created_at,
|
||||
tags,
|
||||
content: typeof ciphertext === "string" ? ciphertext : "",
|
||||
};
|
||||
}
|
||||
|
||||
export function buildDmAttachmentEvent(params) {
|
||||
const {
|
||||
pubkey,
|
||||
|
||||
@@ -1,430 +0,0 @@
|
||||
import {
|
||||
NOTE_TYPES,
|
||||
getNostrEventSchema,
|
||||
buildVideoPostEvent,
|
||||
buildVideoMirrorEvent,
|
||||
buildRepostEvent,
|
||||
buildRelayListEvent,
|
||||
buildDmRelayListEvent,
|
||||
buildProfileMetadataEvent,
|
||||
buildMuteListEvent,
|
||||
buildDmAttachmentEvent,
|
||||
buildDmReadReceiptEvent,
|
||||
buildDmTypingIndicatorEvent,
|
||||
buildViewEvent,
|
||||
buildZapRequestEvent,
|
||||
buildReactionEvent,
|
||||
buildCommentEvent,
|
||||
buildWatchHistoryEvent,
|
||||
buildSubscriptionListEvent,
|
||||
buildBlockListEvent,
|
||||
buildHashtagPreferenceEvent,
|
||||
buildAdminListEvent,
|
||||
} from "../../js/nostrEventSchemas.js";
|
||||
|
||||
import { buildNip71VideoEvent } from "../../js/nostr/nip71.js";
|
||||
|
||||
// Mock data
|
||||
const MOCK_PUBKEY = "0000000000000000000000000000000000000000000000000000000000000001";
|
||||
const MOCK_EVENT_ID = "0000000000000000000000000000000000000000000000000000000000000002";
|
||||
const MOCK_TIMESTAMP = 1700000000;
|
||||
const MOCK_URL = "https://example.com/video.mp4";
|
||||
const MOCK_THUMBNAIL = "https://example.com/thumb.jpg";
|
||||
const MOCK_MAGNET = "magnet:?xt=urn:btih:example";
|
||||
const MOCK_RELAY = "wss://relay.example.com";
|
||||
const MOCK_D_TAG = "mock-d-tag";
|
||||
|
||||
// Helper to check if a tag exists
|
||||
function hasTag(tags, tagName, tagValue = null) {
|
||||
return tags.some(
|
||||
(tag) =>
|
||||
Array.isArray(tag) &&
|
||||
tag[0] === tagName &&
|
||||
(tagValue === null || tag[1] === tagValue)
|
||||
);
|
||||
}
|
||||
|
||||
// Validation logic
|
||||
function validateEvent(type, event) {
|
||||
const schema = getNostrEventSchema(type);
|
||||
const errors = [];
|
||||
|
||||
if (!schema) {
|
||||
return [`Schema not found for type: ${type}`];
|
||||
}
|
||||
|
||||
// 1. Validate Kind
|
||||
if (event.kind !== schema.kind) {
|
||||
errors.push(`Kind mismatch: expected ${schema.kind}, got ${event.kind}`);
|
||||
}
|
||||
|
||||
// 2. Validate Required Tags
|
||||
// Topic tag
|
||||
if (schema.topicTag) {
|
||||
if (!hasTag(event.tags, schema.topicTag.name, schema.topicTag.value)) {
|
||||
errors.push(
|
||||
`Missing topic tag: ${schema.topicTag.name}=${schema.topicTag.value}`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Identifier tag (d tag)
|
||||
if (schema.identifierTag) {
|
||||
// For lists with fixed identifier values (like subscription list), check the value
|
||||
const expectedValue = schema.identifierTag.value;
|
||||
if (!hasTag(event.tags, schema.identifierTag.name, expectedValue)) {
|
||||
errors.push(
|
||||
`Missing identifier tag: ${schema.identifierTag.name}${
|
||||
expectedValue ? "=" + expectedValue : ""
|
||||
}`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Append tags
|
||||
if (schema.appendTags) {
|
||||
schema.appendTags.forEach((appendTag) => {
|
||||
const tagName = appendTag[0];
|
||||
const tagValue = appendTag[1];
|
||||
if (!hasTag(event.tags, tagName, tagValue)) {
|
||||
errors.push(`Missing append tag: ${tagName}=${tagValue}`);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Specific schema tags
|
||||
if (schema.relayTagName) {
|
||||
// Check if at least one relay tag exists if we added relays in mock data
|
||||
// This is slightly context dependent, but we can check if the builder respects the tag name
|
||||
// Logic: if we expect the builder to use 'r' or 'relay', we can't easily verify generally unless we check specific output
|
||||
// But we can check if any tag with that name exists IF we passed relays.
|
||||
}
|
||||
|
||||
// 3. Validate Content Format
|
||||
if (schema.content) {
|
||||
if (schema.content.format === "json") {
|
||||
try {
|
||||
const parsed = JSON.parse(event.content);
|
||||
if (schema.content.fields) {
|
||||
schema.content.fields.forEach(field => {
|
||||
if (field.required && parsed[field.key] === undefined) {
|
||||
errors.push(`Missing required content field: ${field.key}`);
|
||||
}
|
||||
if (parsed[field.key] !== undefined) {
|
||||
if (field.type === 'number' && typeof parsed[field.key] !== 'number') {
|
||||
errors.push(`Invalid type for content field ${field.key}: expected number, got ${typeof parsed[field.key]}`);
|
||||
}
|
||||
if (field.type === 'string' && typeof parsed[field.key] !== 'string') {
|
||||
errors.push(`Invalid type for content field ${field.key}: expected string, got ${typeof parsed[field.key]}`);
|
||||
}
|
||||
if (field.type === 'boolean' && typeof parsed[field.key] !== 'boolean') {
|
||||
errors.push(`Invalid type for content field ${field.key}: expected boolean, got ${typeof parsed[field.key]}`);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
errors.push("Content is not valid JSON");
|
||||
}
|
||||
} else if (schema.content.format === "empty") {
|
||||
if (event.content !== "") {
|
||||
errors.push("Content should be empty");
|
||||
}
|
||||
} else if (
|
||||
schema.content.format === "text" ||
|
||||
schema.content.format === "encrypted-tag-list" ||
|
||||
schema.content.format === "nip44-json"
|
||||
) {
|
||||
if (typeof event.content !== "string") {
|
||||
errors.push("Content must be a string");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return errors;
|
||||
}
|
||||
|
||||
// Test cases
|
||||
const tests = [
|
||||
{
|
||||
name: "Video Post",
|
||||
type: NOTE_TYPES.VIDEO_POST,
|
||||
builder: buildVideoPostEvent,
|
||||
params: {
|
||||
pubkey: MOCK_PUBKEY,
|
||||
created_at: MOCK_TIMESTAMP,
|
||||
dTagValue: MOCK_D_TAG,
|
||||
content: {
|
||||
version: 3,
|
||||
title: "Test Video",
|
||||
videoRootId: MOCK_EVENT_ID,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Video Mirror",
|
||||
type: NOTE_TYPES.VIDEO_MIRROR,
|
||||
builder: buildVideoMirrorEvent,
|
||||
params: {
|
||||
pubkey: MOCK_PUBKEY,
|
||||
created_at: MOCK_TIMESTAMP,
|
||||
content: "Alt text",
|
||||
tags: [["magnet", MOCK_MAGNET]],
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Repost",
|
||||
type: NOTE_TYPES.REPOST,
|
||||
builder: buildRepostEvent,
|
||||
params: {
|
||||
pubkey: MOCK_PUBKEY,
|
||||
created_at: MOCK_TIMESTAMP,
|
||||
eventId: MOCK_EVENT_ID,
|
||||
eventRelay: MOCK_RELAY,
|
||||
targetEvent: { id: MOCK_EVENT_ID, pubkey: MOCK_PUBKEY, kind: 1, content: "test", tags: [], created_at: MOCK_TIMESTAMP },
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Relay List",
|
||||
type: NOTE_TYPES.RELAY_LIST,
|
||||
builder: buildRelayListEvent,
|
||||
params: {
|
||||
pubkey: MOCK_PUBKEY,
|
||||
created_at: MOCK_TIMESTAMP,
|
||||
relays: [
|
||||
{ url: "wss://r1.com", read: true, write: true },
|
||||
{ url: "wss://r2.com", read: true, write: false },
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "DM Relay List",
|
||||
type: NOTE_TYPES.DM_RELAY_LIST,
|
||||
builder: buildDmRelayListEvent,
|
||||
params: {
|
||||
pubkey: MOCK_PUBKEY,
|
||||
created_at: MOCK_TIMESTAMP,
|
||||
relays: ["wss://dm.relay.com"],
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Profile Metadata",
|
||||
type: NOTE_TYPES.PROFILE_METADATA,
|
||||
builder: buildProfileMetadataEvent,
|
||||
params: {
|
||||
pubkey: MOCK_PUBKEY,
|
||||
created_at: MOCK_TIMESTAMP,
|
||||
metadata: { name: "TestUser", about: "Testing" },
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Mute List",
|
||||
type: NOTE_TYPES.MUTE_LIST,
|
||||
builder: buildMuteListEvent,
|
||||
params: {
|
||||
pubkey: MOCK_PUBKEY,
|
||||
created_at: MOCK_TIMESTAMP,
|
||||
pTags: [MOCK_PUBKEY],
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "DM Attachment",
|
||||
type: NOTE_TYPES.DM_ATTACHMENT,
|
||||
builder: buildDmAttachmentEvent,
|
||||
params: {
|
||||
pubkey: MOCK_PUBKEY,
|
||||
created_at: MOCK_TIMESTAMP,
|
||||
recipientPubkey: MOCK_PUBKEY,
|
||||
attachment: { x: "hash", url: MOCK_URL, type: "image/jpeg" },
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "DM Read Receipt",
|
||||
type: NOTE_TYPES.DM_READ_RECEIPT,
|
||||
builder: buildDmReadReceiptEvent,
|
||||
params: {
|
||||
pubkey: MOCK_PUBKEY,
|
||||
created_at: MOCK_TIMESTAMP,
|
||||
recipientPubkey: MOCK_PUBKEY,
|
||||
eventId: MOCK_EVENT_ID,
|
||||
messageKind: 4,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "DM Typing Indicator",
|
||||
type: NOTE_TYPES.DM_TYPING,
|
||||
builder: buildDmTypingIndicatorEvent,
|
||||
params: {
|
||||
pubkey: MOCK_PUBKEY,
|
||||
created_at: MOCK_TIMESTAMP,
|
||||
recipientPubkey: MOCK_PUBKEY,
|
||||
eventId: MOCK_EVENT_ID,
|
||||
expiresAt: MOCK_TIMESTAMP + 60,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "View Event",
|
||||
type: NOTE_TYPES.VIEW_EVENT,
|
||||
builder: buildViewEvent,
|
||||
params: {
|
||||
pubkey: MOCK_PUBKEY,
|
||||
created_at: MOCK_TIMESTAMP,
|
||||
pointerTag: ["e", MOCK_EVENT_ID],
|
||||
dedupeTag: "unique-view-id",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Zap Request",
|
||||
type: NOTE_TYPES.ZAP_REQUEST,
|
||||
builder: buildZapRequestEvent,
|
||||
params: {
|
||||
pubkey: MOCK_PUBKEY,
|
||||
created_at: MOCK_TIMESTAMP,
|
||||
recipientPubkey: MOCK_PUBKEY,
|
||||
amountSats: 100,
|
||||
relays: [MOCK_RELAY],
|
||||
eventId: MOCK_EVENT_ID,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Reaction",
|
||||
type: NOTE_TYPES.VIDEO_REACTION,
|
||||
builder: buildReactionEvent,
|
||||
params: {
|
||||
pubkey: MOCK_PUBKEY,
|
||||
created_at: MOCK_TIMESTAMP,
|
||||
targetPointer: { type: "e", value: MOCK_EVENT_ID, relay: MOCK_RELAY },
|
||||
content: "+",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Video Comment",
|
||||
type: NOTE_TYPES.VIDEO_COMMENT,
|
||||
builder: buildCommentEvent,
|
||||
params: {
|
||||
pubkey: MOCK_PUBKEY,
|
||||
created_at: MOCK_TIMESTAMP,
|
||||
videoEventId: MOCK_EVENT_ID,
|
||||
videoEventRelay: MOCK_RELAY,
|
||||
content: "Great video!",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Watch History",
|
||||
type: NOTE_TYPES.WATCH_HISTORY,
|
||||
builder: buildWatchHistoryEvent,
|
||||
params: {
|
||||
pubkey: MOCK_PUBKEY,
|
||||
created_at: MOCK_TIMESTAMP,
|
||||
monthIdentifier: "2023-11",
|
||||
pointerTags: [["e", MOCK_EVENT_ID]],
|
||||
content: JSON.stringify({ version: 2, month: "2023-11", items: [] }),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Subscription List",
|
||||
type: NOTE_TYPES.SUBSCRIPTION_LIST,
|
||||
builder: buildSubscriptionListEvent,
|
||||
params: {
|
||||
pubkey: MOCK_PUBKEY,
|
||||
created_at: MOCK_TIMESTAMP,
|
||||
content: "encrypted-content",
|
||||
encryption: "nip44",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Block List",
|
||||
type: NOTE_TYPES.USER_BLOCK_LIST,
|
||||
builder: buildBlockListEvent,
|
||||
params: {
|
||||
pubkey: MOCK_PUBKEY,
|
||||
created_at: MOCK_TIMESTAMP,
|
||||
content: "encrypted-content",
|
||||
encryption: "nip44",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Hashtag Preferences",
|
||||
type: NOTE_TYPES.HASHTAG_PREFERENCES,
|
||||
builder: buildHashtagPreferenceEvent,
|
||||
params: {
|
||||
pubkey: MOCK_PUBKEY,
|
||||
created_at: MOCK_TIMESTAMP,
|
||||
content: "encrypted-content",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Admin Moderation List",
|
||||
type: NOTE_TYPES.ADMIN_MODERATION_LIST,
|
||||
builder: (params) => buildAdminListEvent("moderation", params),
|
||||
params: {
|
||||
pubkey: MOCK_PUBKEY,
|
||||
created_at: MOCK_TIMESTAMP,
|
||||
hexPubkeys: [MOCK_PUBKEY],
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Admin Blacklist",
|
||||
type: NOTE_TYPES.ADMIN_BLACKLIST,
|
||||
builder: (params) => buildAdminListEvent("blacklist", params),
|
||||
params: {
|
||||
pubkey: MOCK_PUBKEY,
|
||||
created_at: MOCK_TIMESTAMP,
|
||||
hexPubkeys: [MOCK_PUBKEY],
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Admin Whitelist",
|
||||
type: NOTE_TYPES.ADMIN_WHITELIST,
|
||||
builder: (params) => buildAdminListEvent("whitelist", params),
|
||||
params: {
|
||||
pubkey: MOCK_PUBKEY,
|
||||
created_at: MOCK_TIMESTAMP,
|
||||
hexPubkeys: [MOCK_PUBKEY],
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "NIP-71 Video",
|
||||
type: NOTE_TYPES.NIP71_VIDEO,
|
||||
builder: buildNip71VideoEvent,
|
||||
params: {
|
||||
metadata: {
|
||||
kind: 21,
|
||||
title: "Test NIP-71 Video",
|
||||
summary: "This is a test summary",
|
||||
},
|
||||
pubkey: MOCK_PUBKEY,
|
||||
title: "Test NIP-71 Video",
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
// Execution
|
||||
console.log("Starting Event Validation...");
|
||||
let failCount = 0;
|
||||
|
||||
tests.forEach((test) => {
|
||||
try {
|
||||
const event = test.builder(test.params);
|
||||
const errors = validateEvent(test.type, event);
|
||||
|
||||
if (errors.length > 0) {
|
||||
console.error(`❌ ${test.name} Failed:`);
|
||||
errors.forEach((err) => console.error(` - ${err}`));
|
||||
failCount++;
|
||||
} else {
|
||||
console.log(`✅ ${test.name} Passed`);
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(`❌ ${test.name} threw exception:`, e);
|
||||
failCount++;
|
||||
}
|
||||
});
|
||||
|
||||
if (failCount > 0) {
|
||||
console.log(`\nValidation failed with ${failCount} errors.`);
|
||||
process.exit(1);
|
||||
} else {
|
||||
console.log("\nAll events validated successfully!");
|
||||
process.exit(0);
|
||||
}
|
||||
Reference in new issue
Block a user