mirror of
https://github.com/PR0M3TH3AN/bitvid.git
synced 2026-10-27 07:52:24 +00:00
375 lines
11 KiB
JavaScript
375 lines
11 KiB
JavaScript
import {
|
|
getNostrEventSchema,
|
|
validateEventStructure,
|
|
buildVideoPostEvent,
|
|
buildHttpAuthEvent,
|
|
buildReportEvent,
|
|
buildGiftWrapEvent,
|
|
buildSealEvent,
|
|
buildChatMessageEvent,
|
|
buildVideoMirrorEvent,
|
|
buildRepostEvent,
|
|
buildShareEvent,
|
|
buildRelayListEvent,
|
|
buildDmRelayListEvent,
|
|
buildProfileMetadataEvent,
|
|
buildMuteListEvent,
|
|
buildDeletionEvent,
|
|
buildLegacyDirectMessageEvent,
|
|
buildDmAttachmentEvent,
|
|
buildDmReadReceiptEvent,
|
|
buildDmTypingIndicatorEvent,
|
|
buildViewEvent,
|
|
buildZapRequestEvent,
|
|
buildReactionEvent,
|
|
buildCommentEvent,
|
|
buildWatchHistoryEvent,
|
|
buildSubscriptionListEvent,
|
|
buildBlockListEvent,
|
|
buildHashtagPreferenceEvent,
|
|
buildAdminListEvent,
|
|
NOTE_TYPES,
|
|
} from "../../js/nostrEventSchemas.js";
|
|
import { buildNip71VideoEvent } from "../../js/nostr/nip71.js";
|
|
import fs from "fs";
|
|
import { execSync } from "child_process";
|
|
import path from "path";
|
|
|
|
// Parse CLI args
|
|
const args = process.argv.slice(2);
|
|
const getArg = (key) => {
|
|
const arg = args.find(a => a.startsWith(`--${key}=`));
|
|
return arg ? arg.split('=')[1] : null;
|
|
};
|
|
const hasArg = (key) => args.includes(`--${key}`);
|
|
|
|
const now = new Date();
|
|
const yyyymmdd = now.toISOString().slice(0, 10).replace(/-/g, "");
|
|
const defaultOut = `artifacts/validate-events-${yyyymmdd}.json`;
|
|
|
|
const OUT_FILE = getArg("out") || defaultOut;
|
|
const ONLY_BUILDER = getArg("only");
|
|
const DRY_RUN = hasArg("dry-run");
|
|
|
|
// Valid 32-byte hex pubkey for testing
|
|
const TEST_PUBKEY = "0000000000000000000000000000000000000000000000000000000000000001";
|
|
const TEST_EVENT_ID = "0000000000000000000000000000000000000000000000000000000000000002";
|
|
|
|
function findCallSites() {
|
|
try {
|
|
// Grep for build*Event calls in js/ directory, excluding node_modules and dist
|
|
// We look for the pattern build[A-Z]...Event
|
|
const cmd = `grep -rn "build[A-Z][a-zA-Z]*Event" js/ --include="*.js" --exclude-dir=node_modules`;
|
|
const output = execSync(cmd, { encoding: 'utf-8' });
|
|
return output.split('\n').filter(Boolean).map(line => {
|
|
// line format: file:line:content
|
|
const parts = line.split(':');
|
|
if (parts.length < 3) return null;
|
|
return {
|
|
file: parts[0],
|
|
line: parts[1],
|
|
content: parts.slice(2).join(':').trim()
|
|
};
|
|
}).filter(Boolean);
|
|
} catch (e) {
|
|
// grep returns exit code 1 if no matches found, which execSync treats as error
|
|
if (e.status === 1) return [];
|
|
console.warn("Could not find call sites via grep:", e.message);
|
|
return [];
|
|
}
|
|
}
|
|
|
|
function runValidation() {
|
|
const results = [];
|
|
const failures = [];
|
|
|
|
const runTest = (name, builder, input, expectedType) => {
|
|
if (ONLY_BUILDER && !name.includes(ONLY_BUILDER)) return;
|
|
|
|
try {
|
|
const event = builder(input);
|
|
const validation = validateEventStructure(expectedType, event);
|
|
|
|
const result = {
|
|
builder: name,
|
|
input: input,
|
|
event: event,
|
|
validation: {
|
|
status: validation.valid ? "PASS" : "FAIL",
|
|
failures: validation.errors.map(msg => ({ message: msg }))
|
|
}
|
|
};
|
|
|
|
results.push(result);
|
|
|
|
if (!validation.valid) {
|
|
failures.push(result);
|
|
console.error(`❌ ${name} FAILED:`);
|
|
validation.errors.forEach(e => console.error(` - ${e}`));
|
|
} else {
|
|
console.log(`✅ ${name} PASSED`);
|
|
}
|
|
} catch (e) {
|
|
console.error(`❌ ${name} CRASHED:`, e);
|
|
failures.push({
|
|
builder: name,
|
|
input: input,
|
|
error: e.message,
|
|
validation: { status: "CRASH", failures: [{ message: e.message }] }
|
|
});
|
|
}
|
|
};
|
|
|
|
console.log(`Starting event schema validation... Output: ${OUT_FILE}`);
|
|
|
|
// 1. Video Post
|
|
runTest("buildVideoPostEvent", buildVideoPostEvent, {
|
|
pubkey: TEST_PUBKEY,
|
|
created_at: 1234567890,
|
|
dTagValue: "video-id",
|
|
content: {
|
|
version: 3,
|
|
title: "My Video",
|
|
videoRootId: "video-id",
|
|
url: "https://example.com/video.mp4"
|
|
}
|
|
}, NOTE_TYPES.VIDEO_POST);
|
|
|
|
// 2. HTTP Auth
|
|
runTest("buildHttpAuthEvent", buildHttpAuthEvent, {
|
|
pubkey: TEST_PUBKEY,
|
|
created_at: 1234567890,
|
|
url: "https://example.com/login",
|
|
method: "GET"
|
|
}, NOTE_TYPES.HTTP_AUTH);
|
|
|
|
// 3. Report
|
|
runTest("buildReportEvent", buildReportEvent, {
|
|
pubkey: TEST_PUBKEY,
|
|
created_at: 1234567890,
|
|
eventId: TEST_EVENT_ID,
|
|
reportType: "nudity"
|
|
}, NOTE_TYPES.REPORT);
|
|
|
|
// 4. Gift Wrap
|
|
runTest("buildGiftWrapEvent", buildGiftWrapEvent, {
|
|
pubkey: TEST_PUBKEY,
|
|
created_at: 1234567890,
|
|
recipientPubkey: TEST_PUBKEY,
|
|
ciphertext: "encrypted-content"
|
|
}, NOTE_TYPES.GIFT_WRAP);
|
|
|
|
// 5. Seal
|
|
runTest("buildSealEvent", buildSealEvent, {
|
|
pubkey: TEST_PUBKEY,
|
|
created_at: 1234567890,
|
|
ciphertext: "encrypted-rumor"
|
|
}, NOTE_TYPES.SEAL);
|
|
|
|
// 6. Chat Message
|
|
runTest("buildChatMessageEvent", buildChatMessageEvent, {
|
|
pubkey: TEST_PUBKEY,
|
|
created_at: 1234567890,
|
|
recipientPubkey: TEST_PUBKEY,
|
|
content: "Hello world"
|
|
}, NOTE_TYPES.CHAT_MESSAGE);
|
|
|
|
// 7. Video Mirror
|
|
runTest("buildVideoMirrorEvent", buildVideoMirrorEvent, {
|
|
pubkey: TEST_PUBKEY,
|
|
created_at: 1234567890,
|
|
content: "https://example.com/video.mp4"
|
|
}, NOTE_TYPES.VIDEO_MIRROR);
|
|
|
|
// 8. Repost
|
|
runTest("buildRepostEvent", buildRepostEvent, {
|
|
pubkey: TEST_PUBKEY,
|
|
created_at: 1234567890,
|
|
eventId: TEST_EVENT_ID,
|
|
eventRelay: "wss://relay.example.com"
|
|
}, NOTE_TYPES.REPOST);
|
|
|
|
// 9. Share
|
|
runTest("buildShareEvent", buildShareEvent, {
|
|
pubkey: TEST_PUBKEY,
|
|
created_at: 1234567890,
|
|
content: "Check this out!",
|
|
video: { id: "video-id", pubkey: TEST_PUBKEY }
|
|
}, NOTE_TYPES.SHARE);
|
|
|
|
// 10. Relay List
|
|
runTest("buildRelayListEvent", buildRelayListEvent, {
|
|
pubkey: TEST_PUBKEY,
|
|
created_at: 1234567890,
|
|
relays: ["wss://relay1.com", { url: "wss://relay2.com", mode: "read" }]
|
|
}, NOTE_TYPES.RELAY_LIST);
|
|
|
|
// 11. DM Relay List
|
|
runTest("buildDmRelayListEvent", buildDmRelayListEvent, {
|
|
pubkey: TEST_PUBKEY,
|
|
created_at: 1234567890,
|
|
relays: ["wss://dm.relay.com"]
|
|
}, NOTE_TYPES.DM_RELAY_LIST);
|
|
|
|
// 12. Profile Metadata
|
|
runTest("buildProfileMetadataEvent", buildProfileMetadataEvent, {
|
|
pubkey: TEST_PUBKEY,
|
|
created_at: 1234567890,
|
|
metadata: { name: "Alice", about: "Bob" }
|
|
}, NOTE_TYPES.PROFILE_METADATA);
|
|
|
|
// 13. Mute List
|
|
runTest("buildMuteListEvent", buildMuteListEvent, {
|
|
pubkey: TEST_PUBKEY,
|
|
created_at: 1234567890,
|
|
pTags: [TEST_PUBKEY]
|
|
}, NOTE_TYPES.MUTE_LIST);
|
|
|
|
// 14. Deletion
|
|
runTest("buildDeletionEvent", buildDeletionEvent, {
|
|
pubkey: TEST_PUBKEY,
|
|
created_at: 1234567890,
|
|
eventIds: [TEST_EVENT_ID],
|
|
reason: "spam"
|
|
}, NOTE_TYPES.DELETION);
|
|
|
|
// 15. Legacy DM
|
|
runTest("buildLegacyDirectMessageEvent", buildLegacyDirectMessageEvent, {
|
|
pubkey: TEST_PUBKEY,
|
|
created_at: 1234567890,
|
|
recipientPubkey: TEST_PUBKEY,
|
|
ciphertext: "secret message"
|
|
}, NOTE_TYPES.LEGACY_DM);
|
|
|
|
// 16. DM Attachment
|
|
runTest("buildDmAttachmentEvent", buildDmAttachmentEvent, {
|
|
pubkey: TEST_PUBKEY,
|
|
created_at: 1234567890,
|
|
recipientPubkey: TEST_PUBKEY,
|
|
attachment: { x: "hash", url: "https://example.com/file.jpg" }
|
|
}, NOTE_TYPES.DM_ATTACHMENT);
|
|
|
|
// 17. DM Read Receipt
|
|
runTest("buildDmReadReceiptEvent", buildDmReadReceiptEvent, {
|
|
pubkey: TEST_PUBKEY,
|
|
created_at: 1234567890,
|
|
recipientPubkey: TEST_PUBKEY,
|
|
eventId: TEST_EVENT_ID
|
|
}, NOTE_TYPES.DM_READ_RECEIPT);
|
|
|
|
// 18. DM Typing Indicator
|
|
runTest("buildDmTypingIndicatorEvent", buildDmTypingIndicatorEvent, {
|
|
pubkey: TEST_PUBKEY,
|
|
created_at: 1234567890,
|
|
recipientPubkey: TEST_PUBKEY,
|
|
eventId: TEST_EVENT_ID
|
|
}, NOTE_TYPES.DM_TYPING);
|
|
|
|
// 19. View Event
|
|
runTest("buildViewEvent", buildViewEvent, {
|
|
pubkey: TEST_PUBKEY,
|
|
created_at: 1234567890,
|
|
pointerValue: "video-id",
|
|
dedupeTag: "random-dedupe"
|
|
}, NOTE_TYPES.VIEW_EVENT);
|
|
|
|
// 20. Zap Request
|
|
runTest("buildZapRequestEvent", buildZapRequestEvent, {
|
|
pubkey: TEST_PUBKEY,
|
|
created_at: 1234567890,
|
|
recipientPubkey: TEST_PUBKEY,
|
|
amountSats: 100,
|
|
lnurl: "lnurl1..."
|
|
}, NOTE_TYPES.ZAP_REQUEST);
|
|
|
|
// 21. Reaction
|
|
runTest("buildReactionEvent", buildReactionEvent, {
|
|
pubkey: TEST_PUBKEY,
|
|
created_at: 1234567890,
|
|
targetPointer: { type: "e", value: TEST_EVENT_ID },
|
|
content: "+"
|
|
}, NOTE_TYPES.VIDEO_REACTION);
|
|
|
|
// 22. Comment
|
|
runTest("buildCommentEvent", buildCommentEvent, {
|
|
pubkey: TEST_PUBKEY,
|
|
created_at: 1234567890,
|
|
videoEventId: TEST_EVENT_ID,
|
|
content: "Nice video!"
|
|
}, NOTE_TYPES.VIDEO_COMMENT);
|
|
|
|
// 23. Watch History
|
|
// Testing implicit monthIdentifier via default
|
|
runTest("buildWatchHistoryEvent (Default Month)", buildWatchHistoryEvent, {
|
|
pubkey: TEST_PUBKEY,
|
|
created_at: 1234567890,
|
|
content: { "video-id": 1234567890 }
|
|
}, NOTE_TYPES.WATCH_HISTORY);
|
|
|
|
// 24. Subscription List
|
|
runTest("buildSubscriptionListEvent", buildSubscriptionListEvent, {
|
|
pubkey: TEST_PUBKEY,
|
|
created_at: 1234567890,
|
|
content: [["p", TEST_PUBKEY]]
|
|
}, NOTE_TYPES.SUBSCRIPTION_LIST);
|
|
|
|
// 25. Block List
|
|
runTest("buildBlockListEvent", buildBlockListEvent, {
|
|
pubkey: TEST_PUBKEY,
|
|
created_at: 1234567890,
|
|
content: [["p", TEST_PUBKEY]]
|
|
}, NOTE_TYPES.USER_BLOCK_LIST);
|
|
|
|
// 26. Hashtag Preference
|
|
runTest("buildHashtagPreferenceEvent", buildHashtagPreferenceEvent, {
|
|
pubkey: TEST_PUBKEY,
|
|
created_at: 1234567890,
|
|
content: { version: 1, interests: ["nostr"], disinterests: [] }
|
|
}, NOTE_TYPES.HASHTAG_PREFERENCES);
|
|
|
|
// 27. Admin List (Moderation)
|
|
runTest("buildAdminListEvent (Moderation)", (input) => buildAdminListEvent("moderation", input), {
|
|
pubkey: TEST_PUBKEY,
|
|
created_at: 1234567890,
|
|
hexPubkeys: [TEST_PUBKEY]
|
|
}, NOTE_TYPES.ADMIN_MODERATION_LIST);
|
|
|
|
// 28. NIP-71 Video
|
|
runTest("buildNip71VideoEvent", buildNip71VideoEvent, {
|
|
pubkey: TEST_PUBKEY,
|
|
created_at: 1234567890,
|
|
title: "NIP-71 Test",
|
|
content: "Summary",
|
|
metadata: {
|
|
duration: 120,
|
|
alt: "Alt text"
|
|
}
|
|
}, NOTE_TYPES.NIP71_VIDEO);
|
|
|
|
// Find call sites
|
|
const callSites = findCallSites();
|
|
console.log(`Found ${callSites.length} runtime call sites.`);
|
|
|
|
const finalReport = {
|
|
results,
|
|
runtime_call_sites: callSites
|
|
};
|
|
|
|
if (!DRY_RUN) {
|
|
fs.mkdirSync(path.dirname(OUT_FILE), { recursive: true });
|
|
fs.writeFileSync(OUT_FILE, JSON.stringify(finalReport, null, 2));
|
|
console.log(`\nReport written to ${OUT_FILE}`);
|
|
} else {
|
|
console.log("\nDry run: Skipping report write.");
|
|
}
|
|
|
|
if (failures.length > 0) {
|
|
console.error(`\n⚠️ Found ${failures.length} validation failures.`);
|
|
process.exit(1);
|
|
} else {
|
|
console.log("\n✨ All validations passed!");
|
|
}
|
|
}
|
|
|
|
runValidation();
|