mirror of
https://github.com/PR0M3TH3AN/bitvid.git
synced 2026-11-05 20:32:27 +00:00
Merge branch 'unstable' into agents/daily/perf-agent-2026-02-15-3720994187998550610
This commit is contained in:
287 files changed
+243764
-7024
No files matched your search
@@ -0,0 +1,35 @@
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
|
||||
const criticalFiles = [
|
||||
'js/services/authService.js',
|
||||
'js/relayManager.js',
|
||||
'js/nostr/dmDecryptWorker.js',
|
||||
'js/nostr/dmDecryptWorkerClient.js',
|
||||
'js/nostr/watchHistory.js',
|
||||
'js/userBlocks.js',
|
||||
'js/ui/ambientBackground.js',
|
||||
'torrent/app.js'
|
||||
];
|
||||
|
||||
try {
|
||||
const summary = JSON.parse(fs.readFileSync('test-audit/coverage/coverage-summary.json', 'utf8'));
|
||||
const report = {};
|
||||
|
||||
for (const file of criticalFiles) {
|
||||
// Find the file in the coverage summary keys (might be absolute paths)
|
||||
const key = Object.keys(summary).find(k => k.endsWith(file));
|
||||
if (key) {
|
||||
report[file] = summary[key].lines.pct;
|
||||
} else {
|
||||
report[file] = 0; // Not covered
|
||||
}
|
||||
}
|
||||
|
||||
console.log("Critical Coverage Report:");
|
||||
console.log(JSON.stringify(report, null, 2));
|
||||
|
||||
fs.writeFileSync('test-audit/coverage-gaps.json', JSON.stringify(report, null, 2));
|
||||
} catch (e) {
|
||||
console.error("Error processing coverage summary:", e);
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
#!/bin/bash
|
||||
|
||||
OUTPUT="test-audit/suspicious-tests.txt"
|
||||
echo "Suspicious Tests Report" > "$OUTPUT"
|
||||
echo "=======================" >> "$OUTPUT"
|
||||
|
||||
echo "" >> "$OUTPUT"
|
||||
echo "## Tests with zero assertions (heuristic)" >> "$OUTPUT"
|
||||
# Find test files
|
||||
find tests -name "*.test.mjs" -o -name "*.test.js" | while read f; do
|
||||
if ! grep -qE "assert\.|expect\(|t\.is" "$f"; then
|
||||
echo " - $f" >> "$OUTPUT"
|
||||
fi
|
||||
done
|
||||
|
||||
echo "" >> "$OUTPUT"
|
||||
echo "## Tests using .only or .skip" >> "$OUTPUT"
|
||||
grep -rE "\.(only|skip)\(" tests >> "$OUTPUT"
|
||||
|
||||
echo "" >> "$OUTPUT"
|
||||
echo "## Tests using setTimeout/sleep" >> "$OUTPUT"
|
||||
grep -rE "setTimeout\(|sleep\(|await delay\(" tests >> "$OUTPUT"
|
||||
|
||||
echo "" >> "$OUTPUT"
|
||||
echo "## Tests using console logs" >> "$OUTPUT"
|
||||
grep -rE "console\.(log|warn|error)" tests >> "$OUTPUT"
|
||||
|
||||
echo "" >> "$OUTPUT"
|
||||
echo "## Tests using real network (fetch/axios/WebSocket)" >> "$OUTPUT"
|
||||
grep -rE "fetch\(|axios\.|new WebSocket" tests >> "$OUTPUT"
|
||||
@@ -38,8 +38,8 @@ def run():
|
||||
page.on("requestfailed", on_request_failed)
|
||||
|
||||
try:
|
||||
log("INFO", "Navigating to http://localhost:8000...")
|
||||
page.goto("http://localhost:8000")
|
||||
log("INFO", "Navigating to http://localhost:3000...")
|
||||
page.goto("http://localhost:3000")
|
||||
|
||||
# Wait for load state to ensure initial resources are loaded
|
||||
try:
|
||||
|
||||
@@ -0,0 +1,97 @@
|
||||
import {
|
||||
randomInt,
|
||||
randomString,
|
||||
runFuzzer,
|
||||
} from "./fuzz-utils.mjs";
|
||||
import { validateEventStructure, NOTE_TYPES } from "../../js/nostrEventSchemas.js";
|
||||
|
||||
function generateAggressiveJSON(depth = 0) {
|
||||
if (depth > 50) return "deep"; // Stop before stack overflow unless we want to test that limit
|
||||
|
||||
const choice = randomInt(0, 10);
|
||||
|
||||
if (choice < 2) {
|
||||
// Nested Object
|
||||
const obj = {};
|
||||
const key = Math.random() < 0.1 ? "__proto__" : (Math.random() < 0.1 ? "constructor" : randomString(5));
|
||||
obj[key] = generateAggressiveJSON(depth + 1);
|
||||
return obj;
|
||||
}
|
||||
|
||||
if (choice < 4) {
|
||||
// Nested Array
|
||||
return [generateAggressiveJSON(depth + 1)];
|
||||
}
|
||||
|
||||
if (choice < 5) {
|
||||
// Huge String
|
||||
return randomString(10000);
|
||||
}
|
||||
|
||||
if (choice < 6) {
|
||||
// Unicode Chaos
|
||||
return "\uD800\uDC00" + randomString(100, true) + "\uFFFF";
|
||||
}
|
||||
|
||||
if (choice < 7) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (choice < 8) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return 12345;
|
||||
}
|
||||
|
||||
function genAggressiveEvent() {
|
||||
const types = Object.values(NOTE_TYPES);
|
||||
const type = types[randomInt(0, types.length - 1)];
|
||||
|
||||
// Malformed structure
|
||||
if (Math.random() < 0.1) return [type, null];
|
||||
if (Math.random() < 0.1) return [type, []];
|
||||
if (Math.random() < 0.1) return [type, "string"];
|
||||
|
||||
const event = {
|
||||
kind: randomInt(0, 50000),
|
||||
tags: [],
|
||||
content: ""
|
||||
};
|
||||
|
||||
// Malformed tags
|
||||
if (Math.random() < 0.5) {
|
||||
const numTags = randomInt(0, 100);
|
||||
for(let i=0; i<numTags; i++) {
|
||||
if (Math.random() < 0.1) event.tags.push(null);
|
||||
else if (Math.random() < 0.1) event.tags.push("string");
|
||||
else event.tags.push([randomString(5), randomString(20)]);
|
||||
}
|
||||
}
|
||||
|
||||
// Aggressive Content
|
||||
try {
|
||||
const aggressive = generateAggressiveJSON();
|
||||
event.content = JSON.stringify(aggressive);
|
||||
} catch (e) {
|
||||
event.content = "{}";
|
||||
}
|
||||
|
||||
// Sometimes unparsable JSON
|
||||
if (Math.random() < 0.2) {
|
||||
event.content = "{ unparsable: true, " + randomString(50);
|
||||
}
|
||||
|
||||
return [type, event];
|
||||
}
|
||||
|
||||
async function main() {
|
||||
await runFuzzer(
|
||||
"validateEventStructure-Aggressive",
|
||||
validateEventStructure,
|
||||
genAggressiveEvent,
|
||||
5000 // 5000 iterations
|
||||
);
|
||||
}
|
||||
|
||||
main().catch(console.error);
|
||||
+64
-62
@@ -4,10 +4,11 @@ import fs from 'fs';
|
||||
import path from 'path';
|
||||
import { spawn } from 'child_process';
|
||||
import { fileURLToPath } from 'url';
|
||||
import { buildVideoPostEvent, buildViewEvent } from '../../js/nostrEventSchemas.js';
|
||||
|
||||
// --- Configuration ---
|
||||
let RELAY_URL = process.env.RELAY_URL;
|
||||
const CLIENTS = parseInt(process.env.CLIENTS || '50', 10);
|
||||
const CLIENTS = parseInt(process.env.CLIENTS || '1000', 10);
|
||||
const DURATION_SEC = parseInt(process.env.DURATION_SEC || '600', 10);
|
||||
const RATE_EPS = parseFloat(process.env.RATE_EPS || '10');
|
||||
const MIX_VIDEO_RATIO = parseFloat(process.env.MIX || '0.5'); // 0.0 to 1.0
|
||||
@@ -50,6 +51,7 @@ const metrics = {
|
||||
errors: 0,
|
||||
latencies: [], // ms
|
||||
connectedClients: 0,
|
||||
resources: [], // { time, rss, heapTotal, heapUsed, external, cpuUser, cpuSystem }
|
||||
};
|
||||
|
||||
// --- Helpers ---
|
||||
@@ -62,47 +64,7 @@ function generateKeypair() {
|
||||
return { sk, pk };
|
||||
}
|
||||
|
||||
// Minimal Event Builders
|
||||
// NOTE: We duplicate these schemas here to avoid importing the main application codebase (js/nostrEventSchemas.js)
|
||||
// which has heavy dependencies on browser-specific APIs (window, DOM) and complex config/logger modules.
|
||||
// This keeps the load test harness lightweight, standalone, and safe to run in Node.js CI environments.
|
||||
function buildVideoPostEvent(pubkey, dTagValue) {
|
||||
const videoRootId = dTagValue;
|
||||
const content = JSON.stringify({
|
||||
version: 3,
|
||||
title: `Load Test Video ${dTagValue}`,
|
||||
videoRootId: videoRootId,
|
||||
url: `https://example.com/videos/${dTagValue}.mp4`,
|
||||
magnet: `magnet:?xt=urn:btih:${dTagValue}&dn=video`,
|
||||
mode: 'dev',
|
||||
description: 'Generated by load-test-agent'
|
||||
});
|
||||
|
||||
return {
|
||||
kind: 30078,
|
||||
created_at: Math.floor(Date.now() / 1000),
|
||||
tags: [
|
||||
['d', dTagValue],
|
||||
['t', 'video'],
|
||||
],
|
||||
content,
|
||||
pubkey,
|
||||
};
|
||||
}
|
||||
|
||||
function buildViewEvent(pubkey, dTagValue) {
|
||||
return {
|
||||
kind: 30079, // WATCH_HISTORY_KIND
|
||||
created_at: Math.floor(Date.now() / 1000),
|
||||
tags: [
|
||||
['d', dTagValue],
|
||||
['t', 'view'],
|
||||
['s', `nostr:${dTagValue}`]
|
||||
],
|
||||
content: 'View event',
|
||||
pubkey,
|
||||
};
|
||||
}
|
||||
// Event builders are imported from js/nostrEventSchemas.js
|
||||
|
||||
// --- Client Class ---
|
||||
class LoadClient {
|
||||
@@ -184,34 +146,60 @@ class LoadClient {
|
||||
const dTag = `load-${this.id}-${Date.now()}-${Math.random().toString(36).slice(2)}`;
|
||||
|
||||
let eventTemplate;
|
||||
const now = Math.floor(Date.now() / 1000);
|
||||
if (isVideo) {
|
||||
eventTemplate = buildVideoPostEvent(this.keys.pk, dTag);
|
||||
eventTemplate = buildVideoPostEvent({
|
||||
pubkey: this.keys.pk,
|
||||
created_at: now,
|
||||
dTagValue: dTag,
|
||||
content: {
|
||||
version: 3,
|
||||
title: `Load Test Video ${dTag}`,
|
||||
videoRootId: dTag,
|
||||
url: `https://example.com/videos/${dTag}.mp4`,
|
||||
magnet: `magnet:?xt=urn:btih:${dTag}&dn=video`,
|
||||
mode: 'dev',
|
||||
description: 'Generated by load-test-agent'
|
||||
}
|
||||
});
|
||||
} else {
|
||||
eventTemplate = buildViewEvent(this.keys.pk, dTag);
|
||||
eventTemplate = buildViewEvent({
|
||||
pubkey: this.keys.pk,
|
||||
created_at: now,
|
||||
dedupeTag: dTag,
|
||||
pointerTag: ['s', `nostr:${dTag}`],
|
||||
content: 'View event'
|
||||
});
|
||||
}
|
||||
|
||||
// Sign
|
||||
const skBytes = Uint8Array.from(Buffer.from(this.keys.sk, 'hex'));
|
||||
const signedEvent = finalizeEvent(eventTemplate, skBytes);
|
||||
|
||||
if (DRY_RUN) {
|
||||
metrics.sent++;
|
||||
metrics.accepted++; // Simulate acceptance
|
||||
metrics.latencies.push(Math.random() * 50); // Simulate latency
|
||||
return;
|
||||
}
|
||||
|
||||
// Send
|
||||
const msg = JSON.stringify(['EVENT', signedEvent]);
|
||||
this.pendingPublishes.set(signedEvent.id, Date.now());
|
||||
|
||||
try {
|
||||
this.ws.send(msg);
|
||||
metrics.sent++;
|
||||
const signedEvent = finalizeEvent(eventTemplate, skBytes);
|
||||
if (DRY_RUN) {
|
||||
metrics.sent++;
|
||||
metrics.accepted++; // Simulate acceptance
|
||||
metrics.latencies.push(Math.random() * 50); // Simulate latency
|
||||
return;
|
||||
}
|
||||
|
||||
// Send
|
||||
const msg = JSON.stringify(['EVENT', signedEvent]);
|
||||
this.pendingPublishes.set(signedEvent.id, Date.now());
|
||||
|
||||
try {
|
||||
this.ws.send(msg);
|
||||
metrics.sent++;
|
||||
} catch (err) {
|
||||
metrics.errors++;
|
||||
this.pendingPublishes.delete(signedEvent.id);
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('Failed to finalize event:', err);
|
||||
console.error('Event template:', JSON.stringify(eventTemplate, null, 2));
|
||||
metrics.errors++;
|
||||
this.pendingPublishes.delete(signedEvent.id);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
close() {
|
||||
@@ -314,7 +302,20 @@ Load Test Configuration:
|
||||
const progressTimer = setInterval(() => {
|
||||
const elapsed = (Date.now() - startTime) / 1000;
|
||||
const sentRate = metrics.sent / elapsed;
|
||||
console.log(`[${elapsed.toFixed(1)}s] Sent: ${metrics.sent} (${sentRate.toFixed(1)}/s) | Accepted: ${metrics.accepted} | Rejected: ${metrics.rejected} | Errors: ${metrics.errors}`);
|
||||
const mem = process.memoryUsage();
|
||||
const cpu = process.cpuUsage();
|
||||
|
||||
metrics.resources.push({
|
||||
time: elapsed,
|
||||
rss: mem.rss,
|
||||
heapTotal: mem.heapTotal,
|
||||
heapUsed: mem.heapUsed,
|
||||
external: mem.external,
|
||||
cpuUser: cpu.user,
|
||||
cpuSystem: cpu.system
|
||||
});
|
||||
|
||||
console.log(`[${elapsed.toFixed(1)}s] Sent: ${metrics.sent} (${sentRate.toFixed(1)}/s) | Accepted: ${metrics.accepted} | Rejected: ${metrics.rejected} | Errors: ${metrics.errors} | RSS: ${(mem.rss / 1024 / 1024).toFixed(1)}MB`);
|
||||
}, 5000);
|
||||
|
||||
// 3. Finish & Report
|
||||
@@ -358,7 +359,8 @@ Load Test Configuration:
|
||||
p50,
|
||||
p95,
|
||||
p99
|
||||
}
|
||||
},
|
||||
resources: metrics.resources
|
||||
},
|
||||
timestamp: new Date().toISOString()
|
||||
};
|
||||
|
||||
+407
-304
@@ -1,319 +1,422 @@
|
||||
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 fs from 'fs';
|
||||
import path from 'path';
|
||||
import { fileURLToPath } from 'url';
|
||||
import * as schemas from '../../js/nostrEventSchemas.js';
|
||||
import { buildNip71VideoEvent } from '../../js/nostr/nip71.js';
|
||||
|
||||
const REPORT_FILE = "artifacts/validate-events.json";
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = path.dirname(__filename);
|
||||
|
||||
// Valid 32-byte hex pubkey for testing
|
||||
const TEST_PUBKEY = "0000000000000000000000000000000000000000000000000000000000000001";
|
||||
const TEST_EVENT_ID = "0000000000000000000000000000000000000000000000000000000000000002";
|
||||
// Mock window for compatibility
|
||||
if (typeof window === 'undefined') {
|
||||
global.window = {};
|
||||
}
|
||||
|
||||
function runValidation() {
|
||||
const results = [];
|
||||
const failures = [];
|
||||
const results = [];
|
||||
|
||||
const runTest = (name, builder, input, expectedType) => {
|
||||
try {
|
||||
const event = builder(input);
|
||||
const validation = validateEventStructure(expectedType, event);
|
||||
const builders = {
|
||||
...schemas,
|
||||
buildNip71VideoEvent
|
||||
};
|
||||
|
||||
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 }] }
|
||||
});
|
||||
function runTest(builderName, testName, args, expectedValid = true) {
|
||||
try {
|
||||
const builder = builders[builderName];
|
||||
if (typeof builder !== 'function') {
|
||||
throw new Error(`Builder ${builderName} not found`);
|
||||
}
|
||||
};
|
||||
|
||||
console.log("Starting event schema validation...");
|
||||
|
||||
// 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"
|
||||
// Handle arguments: if args is an array, apply it. If it's an object, assume single argument (params).
|
||||
// Exception: buildAdminListEvent takes (listKey, params).
|
||||
let event;
|
||||
if (Array.isArray(args)) {
|
||||
event = builder(...args);
|
||||
} else {
|
||||
event = builder(args);
|
||||
}
|
||||
}, 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"
|
||||
if (!event) {
|
||||
throw new Error(`Builder ${builderName} returned null/undefined`);
|
||||
}
|
||||
}, NOTE_TYPES.NIP71_VIDEO);
|
||||
|
||||
// Write Report
|
||||
fs.writeFileSync(REPORT_FILE, JSON.stringify(results, null, 2));
|
||||
console.log(`\nReport written to ${REPORT_FILE}`);
|
||||
let type;
|
||||
const builderToType = {
|
||||
buildVideoPostEvent: schemas.NOTE_TYPES.VIDEO_POST,
|
||||
buildVideoMirrorEvent: schemas.NOTE_TYPES.VIDEO_MIRROR,
|
||||
buildRepostEvent: schemas.NOTE_TYPES.REPOST,
|
||||
buildShareEvent: schemas.NOTE_TYPES.SHARE,
|
||||
buildRelayListEvent: schemas.NOTE_TYPES.RELAY_LIST,
|
||||
buildDmRelayListEvent: schemas.NOTE_TYPES.DM_RELAY_LIST,
|
||||
buildProfileMetadataEvent: schemas.NOTE_TYPES.PROFILE_METADATA,
|
||||
buildMuteListEvent: schemas.NOTE_TYPES.MUTE_LIST,
|
||||
buildDeletionEvent: schemas.NOTE_TYPES.DELETION,
|
||||
buildLegacyDirectMessageEvent: schemas.NOTE_TYPES.LEGACY_DM,
|
||||
buildDmAttachmentEvent: schemas.NOTE_TYPES.DM_ATTACHMENT,
|
||||
buildDmReadReceiptEvent: schemas.NOTE_TYPES.DM_READ_RECEIPT,
|
||||
buildDmTypingIndicatorEvent: schemas.NOTE_TYPES.DM_TYPING,
|
||||
buildViewEvent: schemas.NOTE_TYPES.VIEW_EVENT,
|
||||
buildZapRequestEvent: schemas.NOTE_TYPES.ZAP_REQUEST,
|
||||
buildReactionEvent: schemas.NOTE_TYPES.VIDEO_REACTION,
|
||||
buildCommentEvent: schemas.NOTE_TYPES.VIDEO_COMMENT,
|
||||
buildWatchHistoryEvent: schemas.NOTE_TYPES.WATCH_HISTORY,
|
||||
buildSubscriptionListEvent: schemas.NOTE_TYPES.SUBSCRIPTION_LIST,
|
||||
buildBlockListEvent: schemas.NOTE_TYPES.USER_BLOCK_LIST,
|
||||
buildHashtagPreferenceEvent: schemas.NOTE_TYPES.HASHTAG_PREFERENCES,
|
||||
buildAdminListEvent: schemas.NOTE_TYPES.ADMIN_MODERATION_LIST, // Default
|
||||
buildGiftWrapEvent: schemas.NOTE_TYPES.GIFT_WRAP,
|
||||
buildSealEvent: schemas.NOTE_TYPES.SEAL,
|
||||
buildChatMessageEvent: schemas.NOTE_TYPES.CHAT_MESSAGE,
|
||||
buildHttpAuthEvent: schemas.NOTE_TYPES.HTTP_AUTH,
|
||||
buildReportEvent: schemas.NOTE_TYPES.REPORT,
|
||||
buildNip71VideoEvent: schemas.NOTE_TYPES.NIP71_VIDEO // Default
|
||||
};
|
||||
|
||||
if (failures.length > 0) {
|
||||
console.error(`\n⚠️ Found ${failures.length} validation failures.`);
|
||||
process.exit(1);
|
||||
} else {
|
||||
console.log("\n✨ All validations passed!");
|
||||
type = builderToType[builderName];
|
||||
|
||||
// Special cases for type resolution
|
||||
if (builderName === 'buildRepostEvent' && event.kind === 16) {
|
||||
type = schemas.NOTE_TYPES.GENERIC_REPOST;
|
||||
}
|
||||
if (builderName === 'buildAdminListEvent') {
|
||||
const listKey = Array.isArray(args) ? args[0] : args; // assuming first arg is listKey
|
||||
if (listKey === 'moderation' || listKey === 'editors') type = schemas.NOTE_TYPES.ADMIN_MODERATION_LIST;
|
||||
else if (listKey === 'whitelist') type = schemas.NOTE_TYPES.ADMIN_WHITELIST;
|
||||
else if (listKey === 'blacklist') type = schemas.NOTE_TYPES.ADMIN_BLACKLIST;
|
||||
}
|
||||
if (builderName === 'buildNip71VideoEvent') {
|
||||
if (event.kind === 22) type = schemas.NOTE_TYPES.NIP71_SHORT_VIDEO;
|
||||
else type = schemas.NOTE_TYPES.NIP71_VIDEO;
|
||||
}
|
||||
|
||||
const validation = schemas.validateEventStructure(type, event);
|
||||
|
||||
results.push({
|
||||
builder: builderName,
|
||||
testName,
|
||||
inputs: args,
|
||||
event,
|
||||
valid: validation.valid === expectedValid,
|
||||
validationResult: validation,
|
||||
expectedValid
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
results.push({
|
||||
builder: builderName,
|
||||
testName,
|
||||
inputs: args,
|
||||
error: error.message,
|
||||
stack: error.stack,
|
||||
valid: false
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
runValidation();
|
||||
// --- Test Cases ---
|
||||
|
||||
const PUBKEY = '0000000000000000000000000000000000000000000000000000000000000001';
|
||||
const CREATED_AT = 1700000000;
|
||||
|
||||
// Video Post
|
||||
runTest('buildVideoPostEvent', 'Basic Video Post', {
|
||||
pubkey: PUBKEY,
|
||||
created_at: CREATED_AT,
|
||||
dTagValue: 'video-1',
|
||||
content: {
|
||||
version: 3,
|
||||
title: 'My Video',
|
||||
videoRootId: 'video-1',
|
||||
url: 'https://example.com/video.mp4'
|
||||
}
|
||||
});
|
||||
|
||||
runTest('buildVideoPostEvent', 'Video Post with Magnet', {
|
||||
pubkey: PUBKEY,
|
||||
created_at: CREATED_AT,
|
||||
dTagValue: 'video-2',
|
||||
content: {
|
||||
version: 3,
|
||||
title: 'My Magnet Video',
|
||||
videoRootId: 'video-2',
|
||||
magnet: 'magnet:?xt=urn:btih:1234567890abcdef1234567890abcdef12345678&dn=video'
|
||||
}
|
||||
});
|
||||
|
||||
// Video Mirror
|
||||
runTest('buildVideoMirrorEvent', 'Video Mirror', {
|
||||
pubkey: PUBKEY,
|
||||
created_at: CREATED_AT,
|
||||
tags: [['url', 'https://example.com/mirror.mp4'], ['m', 'video/mp4']],
|
||||
content: 'Alt text'
|
||||
});
|
||||
|
||||
// Repost
|
||||
runTest('buildRepostEvent', 'Repost Event', {
|
||||
pubkey: PUBKEY,
|
||||
created_at: CREATED_AT,
|
||||
eventId: '1111111111111111111111111111111111111111111111111111111111111111',
|
||||
eventRelay: 'wss://relay.example.com'
|
||||
});
|
||||
|
||||
runTest('buildRepostEvent', 'Generic Repost (Kind 16)', {
|
||||
pubkey: PUBKEY,
|
||||
created_at: CREATED_AT,
|
||||
targetKind: 30078,
|
||||
serializedEvent: JSON.stringify({ kind: 30078, tags: [], content: '{}' })
|
||||
});
|
||||
|
||||
// Share
|
||||
runTest('buildShareEvent', 'Share Event', {
|
||||
pubkey: PUBKEY,
|
||||
created_at: CREATED_AT,
|
||||
content: 'Check this out!',
|
||||
video: { id: '2222222222222222222222222222222222222222222222222222222222222222', pubkey: PUBKEY },
|
||||
relays: ['wss://relay.example.com']
|
||||
});
|
||||
|
||||
// Relay List
|
||||
runTest('buildRelayListEvent', 'Relay List', {
|
||||
pubkey: PUBKEY,
|
||||
created_at: CREATED_AT,
|
||||
relays: [
|
||||
{ url: 'wss://relay1.com', read: true, write: true },
|
||||
{ url: 'wss://relay2.com', read: true, write: false }
|
||||
]
|
||||
});
|
||||
|
||||
// DM Relay List
|
||||
runTest('buildDmRelayListEvent', 'DM Relay List', {
|
||||
pubkey: PUBKEY,
|
||||
created_at: CREATED_AT,
|
||||
relays: ['wss://relay.dm.com']
|
||||
});
|
||||
|
||||
// Profile Metadata
|
||||
runTest('buildProfileMetadataEvent', 'Profile Metadata', {
|
||||
pubkey: PUBKEY,
|
||||
created_at: CREATED_AT,
|
||||
metadata: { name: 'Alice', about: 'Hello' }
|
||||
});
|
||||
|
||||
// Mute List
|
||||
runTest('buildMuteListEvent', 'Mute List', {
|
||||
pubkey: PUBKEY,
|
||||
created_at: CREATED_AT,
|
||||
pTags: [PUBKEY],
|
||||
content: '{ "blockedPubkeys": [] }',
|
||||
encrypted: true,
|
||||
encryptionTag: 'nip44_v2'
|
||||
});
|
||||
|
||||
// Deletion
|
||||
runTest('buildDeletionEvent', 'Deletion', {
|
||||
pubkey: PUBKEY,
|
||||
created_at: CREATED_AT,
|
||||
eventIds: ['3333333333333333333333333333333333333333333333333333333333333333'],
|
||||
reason: 'Mistake'
|
||||
});
|
||||
|
||||
// Legacy DM
|
||||
runTest('buildLegacyDirectMessageEvent', 'Legacy DM', {
|
||||
pubkey: PUBKEY,
|
||||
created_at: CREATED_AT,
|
||||
recipientPubkey: PUBKEY,
|
||||
ciphertext: 'encrypted_content'
|
||||
});
|
||||
|
||||
// DM Attachment
|
||||
runTest('buildDmAttachmentEvent', 'DM Attachment', {
|
||||
pubkey: PUBKEY,
|
||||
created_at: CREATED_AT,
|
||||
recipientPubkey: PUBKEY,
|
||||
attachment: { url: 'https://example.com/file.jpg', x: 'hash', type: 'image/jpeg' }
|
||||
});
|
||||
|
||||
// DM Read Receipt
|
||||
runTest('buildDmReadReceiptEvent', 'DM Read Receipt', {
|
||||
pubkey: PUBKEY,
|
||||
created_at: CREATED_AT,
|
||||
recipientPubkey: PUBKEY,
|
||||
eventId: '4444444444444444444444444444444444444444444444444444444444444444'
|
||||
});
|
||||
|
||||
// DM Typing
|
||||
runTest('buildDmTypingIndicatorEvent', 'DM Typing', {
|
||||
pubkey: PUBKEY,
|
||||
created_at: CREATED_AT,
|
||||
recipientPubkey: PUBKEY,
|
||||
expiresAt: CREATED_AT + 60
|
||||
});
|
||||
|
||||
// View Event
|
||||
runTest('buildViewEvent', 'View Event', {
|
||||
pubkey: PUBKEY,
|
||||
created_at: CREATED_AT,
|
||||
pointerValue: 'video-1',
|
||||
dedupeTag: 'session-1',
|
||||
includeSessionTag: true
|
||||
});
|
||||
|
||||
// Zap Request
|
||||
runTest('buildZapRequestEvent', 'Zap Request', {
|
||||
pubkey: PUBKEY,
|
||||
created_at: CREATED_AT,
|
||||
recipientPubkey: PUBKEY,
|
||||
amountSats: 100,
|
||||
relays: ['wss://relay.zap.com']
|
||||
});
|
||||
|
||||
// Reaction
|
||||
runTest('buildReactionEvent', 'Reaction', {
|
||||
pubkey: PUBKEY,
|
||||
created_at: CREATED_AT,
|
||||
targetPointer: { type: 'e', value: '5555555555555555555555555555555555555555555555555555555555555555' },
|
||||
content: '+'
|
||||
});
|
||||
|
||||
// Comment
|
||||
runTest('buildCommentEvent', 'Comment', {
|
||||
pubkey: PUBKEY,
|
||||
created_at: CREATED_AT,
|
||||
content: 'Nice video!',
|
||||
videoEventId: '6666666666666666666666666666666666666666666666666666666666666666',
|
||||
rootKind: '30078'
|
||||
});
|
||||
|
||||
// Watch History
|
||||
runTest('buildWatchHistoryEvent', 'Watch History', {
|
||||
pubkey: PUBKEY,
|
||||
created_at: CREATED_AT,
|
||||
monthIdentifier: '2023-11',
|
||||
content: { version: 2, month: '2023-11', items: [] }
|
||||
});
|
||||
|
||||
// Subscription List
|
||||
runTest('buildSubscriptionListEvent', 'Subscription List', {
|
||||
pubkey: PUBKEY,
|
||||
created_at: CREATED_AT,
|
||||
content: 'encrypted_subscriptions',
|
||||
encryption: 'nip44_v2'
|
||||
});
|
||||
|
||||
// Block List
|
||||
runTest('buildBlockListEvent', 'Block List', {
|
||||
pubkey: PUBKEY,
|
||||
created_at: CREATED_AT,
|
||||
content: 'encrypted_blocks',
|
||||
encryption: 'nip44_v2'
|
||||
});
|
||||
|
||||
// Hashtag Preferences
|
||||
runTest('buildHashtagPreferenceEvent', 'Hashtag Preferences', {
|
||||
pubkey: PUBKEY,
|
||||
created_at: CREATED_AT,
|
||||
content: 'encrypted_preferences'
|
||||
});
|
||||
|
||||
// Admin Lists
|
||||
runTest('buildAdminListEvent', 'Admin Moderation List', ['moderation', {
|
||||
pubkey: PUBKEY,
|
||||
created_at: CREATED_AT,
|
||||
hexPubkeys: [PUBKEY]
|
||||
}]);
|
||||
|
||||
runTest('buildAdminListEvent', 'Admin Whitelist', ['whitelist', {
|
||||
pubkey: PUBKEY,
|
||||
created_at: CREATED_AT,
|
||||
hexPubkeys: [PUBKEY]
|
||||
}]);
|
||||
|
||||
// Gift Wrap
|
||||
runTest('buildGiftWrapEvent', 'Gift Wrap', {
|
||||
pubkey: PUBKEY,
|
||||
created_at: CREATED_AT,
|
||||
recipientPubkey: PUBKEY,
|
||||
ciphertext: 'sealed_message'
|
||||
});
|
||||
|
||||
// Seal
|
||||
runTest('buildSealEvent', 'Seal', {
|
||||
pubkey: PUBKEY,
|
||||
created_at: CREATED_AT,
|
||||
ciphertext: 'rumor_message'
|
||||
});
|
||||
|
||||
// Chat Message
|
||||
runTest('buildChatMessageEvent', 'Chat Message', {
|
||||
pubkey: PUBKEY,
|
||||
created_at: CREATED_AT,
|
||||
recipientPubkey: PUBKEY,
|
||||
content: 'Hello'
|
||||
});
|
||||
|
||||
// HTTP Auth
|
||||
runTest('buildHttpAuthEvent', 'HTTP Auth', {
|
||||
pubkey: PUBKEY,
|
||||
created_at: CREATED_AT,
|
||||
url: 'https://example.com/auth',
|
||||
method: 'GET'
|
||||
});
|
||||
|
||||
// Report
|
||||
runTest('buildReportEvent', 'Report', {
|
||||
pubkey: PUBKEY,
|
||||
created_at: CREATED_AT,
|
||||
eventId: '7777777777777777777777777777777777777777777777777777777777777777',
|
||||
reportType: 'spam'
|
||||
});
|
||||
|
||||
// NIP-71 Video
|
||||
runTest('buildNip71VideoEvent', 'NIP-71 Video', {
|
||||
metadata: {
|
||||
title: 'My NIP-71 Video',
|
||||
summary: 'Summary',
|
||||
kind: 21,
|
||||
publishedAt: 1700000000
|
||||
},
|
||||
pubkey: PUBKEY,
|
||||
title: 'My NIP-71 Video',
|
||||
created_at: CREATED_AT
|
||||
});
|
||||
|
||||
// --- CLI Handling ---
|
||||
|
||||
const args = process.argv.slice(2);
|
||||
let outFile = 'artifacts/validate-events-latest.json';
|
||||
const dryRun = args.includes('--dry-run');
|
||||
|
||||
args.forEach(arg => {
|
||||
if (arg.startsWith('--out=')) {
|
||||
outFile = arg.split('=')[1];
|
||||
}
|
||||
});
|
||||
|
||||
// Summary
|
||||
const total = results.length;
|
||||
const passed = results.filter(r => r.valid).length;
|
||||
const failed = total - passed;
|
||||
|
||||
console.log(`\nValidation Summary: ${passed}/${total} passed`);
|
||||
|
||||
if (failed > 0) {
|
||||
console.log(`⚠️ ${failed} failures found:`);
|
||||
results.filter(r => !r.valid).forEach(r => {
|
||||
console.log(`\n[FAIL] ${r.builder}: ${r.testName}`);
|
||||
if (r.error) {
|
||||
console.log(` Error: ${r.error}`);
|
||||
} else if (r.validationResult && r.validationResult.errors) {
|
||||
r.validationResult.errors.forEach(e => console.log(` Schema Error: ${e}`));
|
||||
}
|
||||
});
|
||||
} else {
|
||||
console.log('✓ All builders validated successfully.');
|
||||
}
|
||||
|
||||
if (!dryRun) {
|
||||
const dir = path.dirname(outFile);
|
||||
if (!fs.existsSync(dir)) {
|
||||
fs.mkdirSync(dir, { recursive: true });
|
||||
}
|
||||
fs.writeFileSync(outFile, JSON.stringify(results, null, 2));
|
||||
console.log(`\nReport written to ${outFile}`);
|
||||
}
|
||||
@@ -26,7 +26,7 @@ const GROWTH_MARGIN = 50; // lines — allowlisted files may grow by this much f
|
||||
// These files are known to be oversized and are decomposition targets.
|
||||
// They must NOT grow beyond (recorded + GROWTH_MARGIN).
|
||||
const GRANDFATHERED = {
|
||||
"js/ui/profileModalController.js": 8130,
|
||||
"js/ui/profileModalController.js": 5884,
|
||||
"js/ui/components/VideoModal.js": 6045,
|
||||
"js/app.js": 5602,
|
||||
"js/channelProfile.js": 5529,
|
||||
|
||||
+21
-5
@@ -56,7 +56,9 @@ for (const glob of IGNORED_GLOBS) {
|
||||
|
||||
// We rely on 'git' being available and 'git grep' supporting -P.
|
||||
// Typically available in most CI/dev environments.
|
||||
const result = spawnSync('git', gitArgs, { encoding: 'utf8' });
|
||||
// Increase maxBuffer to 10MB to avoid ENOBUFS on large outputs.
|
||||
console.log("Running git with args:", gitArgs.join(" "));
|
||||
const result = spawnSync('git', gitArgs, { encoding: 'utf8', maxBuffer: 1024 * 1024 * 10 });
|
||||
|
||||
if (result.error) {
|
||||
console.error('Failed to execute git grep:', result.error.message);
|
||||
@@ -69,10 +71,24 @@ if (result.error) {
|
||||
// 2+ on errors.
|
||||
|
||||
if (result.status === 0 && result.stdout.trim()) {
|
||||
console.error('Hex colors detected outside tokens or SVG assets:');
|
||||
console.error(result.stdout.trimEnd());
|
||||
console.error('\nAllowed exceptions: tokens (css/tokens.css) and vector logos (*.svg).');
|
||||
process.exit(1);
|
||||
const lines = result.stdout.trim().split('\n');
|
||||
|
||||
// Robust filtering: If git grep exclusion fails (e.g. in some CI environments),
|
||||
// we filter out known ignored files here.
|
||||
const filteredLines = lines.filter(line => {
|
||||
// Output format is filename:line:content...
|
||||
// We just check if the line starts with a known ignored file.
|
||||
if (line.startsWith('js/webtorrent.min.js')) return false;
|
||||
if (line.startsWith('perf/hits-')) return false;
|
||||
return true;
|
||||
});
|
||||
|
||||
if (filteredLines.length > 0) {
|
||||
console.error('Hex colors detected outside tokens or SVG assets:');
|
||||
console.error(filteredLines.join('\n'));
|
||||
console.error('\nAllowed exceptions: tokens (css/tokens.css) and vector logos (*.svg).');
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
if (result.status && result.status !== 1) {
|
||||
|
||||
@@ -24,10 +24,9 @@ const SOURCE_DIR = join(ROOT, "js");
|
||||
const BASELINE = {
|
||||
"js/app.js": 1,
|
||||
"js/app/feedCoordinator.js": 1,
|
||||
"js/docsView.js": 8,
|
||||
"js/docsView.js": 1,
|
||||
"js/exploreView.js": 1,
|
||||
"js/forYouView.js": 1,
|
||||
"js/historyView.js": 4,
|
||||
"js/index.js": 1,
|
||||
"js/kidsView.js": 1,
|
||||
"js/ui/components/DeleteModal.js": 3,
|
||||
@@ -48,7 +47,7 @@ const BASELINE = {
|
||||
"js/ui/loginModalController.js": 3,
|
||||
"js/ui/moreMenuController.js": 1,
|
||||
"js/ui/profileModalController.js": 1,
|
||||
"js/ui/subscriptionHistoryController.js": 7,
|
||||
"js/ui/subscriptionHistoryController.js": 1,
|
||||
"js/ui/videoListViewController.js": 1,
|
||||
"js/ui/views/VideoListView.js": 3,
|
||||
"js/utils/qrcode.js": 2,
|
||||
|
||||
Reference in new issue
Block a user