mirror of
https://github.com/PR0M3TH3AN/bitvid.git
synced 2026-11-01 02:12:26 +00:00
feat(ai): Implement scripts/agent/interop-test.mjs
Co-authored-by: PR0M3TH3AN <[email protected]>
This commit is contained in:
1 parent
7c33ecf6e4
commit
3c1227e5be
1 file changed
+127
-31
+127
-31
@@ -1,20 +1,46 @@
|
||||
import './setup-test-env.js';
|
||||
import { indexedDB, IDBKeyRange } from "fake-indexeddb";
|
||||
import { WebSocket } from 'ws';
|
||||
import * as NostrTools from 'nostr-tools';
|
||||
import { webcrypto } from 'node:crypto';
|
||||
|
||||
// --- Polyfills ---
|
||||
if (!global.indexedDB) {
|
||||
global.indexedDB = indexedDB;
|
||||
}
|
||||
if (!global.IDBKeyRange) {
|
||||
global.IDBKeyRange = IDBKeyRange;
|
||||
}
|
||||
if (!global.NostrTools) {
|
||||
global.NostrTools = NostrTools;
|
||||
}
|
||||
// Ensure crypto is available globally for NostrClient (it might be used in some paths)
|
||||
if (!global.crypto) {
|
||||
global.crypto = webcrypto;
|
||||
}
|
||||
|
||||
// --- Imports from Codebase ---
|
||||
import { NostrClient } from '../../js/nostr/client.js';
|
||||
import {
|
||||
buildVideoPostEvent,
|
||||
buildViewEvent
|
||||
} from '../../js/nostrEventSchemas.js';
|
||||
import * as NostrTools from 'nostr-tools';
|
||||
import { decryptDM } from '../../js/dmDecryptor.js';
|
||||
import { bytesToHex } from '../../vendor/crypto-helpers.bundle.min.js';
|
||||
|
||||
// Setup global NostrTools for toolkit.js fallback
|
||||
global.NostrTools = NostrTools;
|
||||
// --- Configuration ---
|
||||
const TEST_RELAYS = ["wss://relay.damus.io", "wss://nos.lol"];
|
||||
const TIMEOUT_MS = 15000;
|
||||
|
||||
async function waitForEvent(pool, relays, id, timeoutMs = 10000) {
|
||||
const start = Date.now();
|
||||
while (Date.now() - start < timeoutMs) {
|
||||
const event = await pool.get(relays, { ids: [id] });
|
||||
if (event) return event;
|
||||
try {
|
||||
const event = await pool.get(relays, { ids: [id] });
|
||||
if (event) return event;
|
||||
} catch (e) {
|
||||
// ignore network errors during polling
|
||||
}
|
||||
await new Promise(r => setTimeout(r, 1000));
|
||||
}
|
||||
return null;
|
||||
@@ -22,29 +48,34 @@ async function waitForEvent(pool, relays, id, timeoutMs = 10000) {
|
||||
|
||||
async function run() {
|
||||
console.log("Starting Interop Test...");
|
||||
console.log(`Relays: ${TEST_RELAYS.join(', ')}`);
|
||||
|
||||
// 1. Setup Client
|
||||
const client = new NostrClient();
|
||||
|
||||
// Test relays - using reputable public relays
|
||||
const TEST_RELAYS = ["wss://relay.damus.io", "wss://nos.lol"];
|
||||
client.relays = [...TEST_RELAYS];
|
||||
|
||||
console.log(`Using relays: ${TEST_RELAYS.join(', ')}`);
|
||||
client.readRelays = [...TEST_RELAYS];
|
||||
client.writeRelays = [...TEST_RELAYS];
|
||||
|
||||
try {
|
||||
// 2. Init (connects to relays)
|
||||
await client.init();
|
||||
console.log("Client initialized.");
|
||||
|
||||
// 3. Generate Ephemeral Keys
|
||||
// 3. Generate Ephemeral Keys (Sender)
|
||||
console.log("Generating ephemeral keys...");
|
||||
const sk = NostrTools.generateSecretKey();
|
||||
const skHex = bytesToHex(sk);
|
||||
const pk = NostrTools.getPublicKey(sk);
|
||||
console.log(`Ephemeral Pubkey: ${pk}`);
|
||||
console.log(`Ephemeral Sender Pubkey: ${pk}`);
|
||||
|
||||
// 4. Register Signer
|
||||
await client.registerPrivateKeySigner({ privateKey: skHex, pubkey: pk });
|
||||
// Manually set pubkey as registerPrivateKeySigner doesn't always set client.pubkey depending on implementation details in some versions,
|
||||
// though the code read showed it sets sessionActor. client.pubkey is usually set on login.
|
||||
// The client.registerPrivateKeySigner implementation sets sessionActor but assumes the user calls init or similar to set client.pubkey if full login is desired?
|
||||
// Looking at NostrClient.js, registerPrivateKeySigner does NOT set this.pubkey. It sets sessionActor.
|
||||
// So we manually set it to simulate "logged in" state for high-level methods.
|
||||
client.pubkey = pk;
|
||||
console.log("Signer registered.");
|
||||
|
||||
// 5. Test A: Video Post
|
||||
@@ -62,9 +93,10 @@ async function run() {
|
||||
url: "https://example.com/video.mp4",
|
||||
mimeType: "video/mp4",
|
||||
mode: "live",
|
||||
deleted: false
|
||||
deleted: false,
|
||||
magnet: `magnet:?xt=urn:btih:${Math.random().toString(16).slice(2)}`, // Add magnet as per schema requirements usually
|
||||
},
|
||||
// 's' tag is required by schema (storage pointer)
|
||||
// 's' tag is recommended for storage pointer
|
||||
additionalTags: [['s', `url:https://example.com/video.mp4`]]
|
||||
});
|
||||
|
||||
@@ -78,8 +110,8 @@ async function run() {
|
||||
console.log(`Published Video Event ID: ${publishedEvent.id}`);
|
||||
|
||||
// Verify
|
||||
console.log("Verifying Video Post...");
|
||||
const rawRelayEvent = await waitForEvent(client.pool, TEST_RELAYS, publishedEvent.id);
|
||||
console.log("Verifying Video Post (fetch by ID)...");
|
||||
const rawRelayEvent = await waitForEvent(client.pool, TEST_RELAYS, publishedEvent.id, TIMEOUT_MS);
|
||||
|
||||
if (!rawRelayEvent) throw new Error("Failed to fetch published video event from relays");
|
||||
if (rawRelayEvent.id !== publishedEvent.id) throw new Error("Fetched event ID mismatch");
|
||||
@@ -96,35 +128,99 @@ async function run() {
|
||||
});
|
||||
|
||||
const viewPublishResult = await client.signAndPublishEvent(viewEvent);
|
||||
console.log(`Published View Event ID: ${viewPublishResult.signedEvent.id}`);
|
||||
const viewEventId = viewPublishResult.signedEvent.id;
|
||||
console.log(`Published View Event ID: ${viewEventId}`);
|
||||
|
||||
// Ensure it is visible to subscriber clients (simulate).
|
||||
const rawViewEvent = await waitForEvent(client.pool, TEST_RELAYS, viewPublishResult.signedEvent.id);
|
||||
const rawViewEvent = await waitForEvent(client.pool, TEST_RELAYS, viewEventId, TIMEOUT_MS);
|
||||
if (!rawViewEvent) throw new Error("Failed to fetch published view event");
|
||||
console.log("View Event Verified.");
|
||||
|
||||
// 7. Test C: Direct Message
|
||||
console.log("\n[Test C] Testing Direct Message...");
|
||||
const msg = `Test DM ${Date.now()}`;
|
||||
const npub = NostrTools.nip19.npubEncode(pk);
|
||||
|
||||
console.log(`Sending DM to self (${npub})...`);
|
||||
const dmResult = await client.sendDirectMessage(npub, msg);
|
||||
// Generate Recipient
|
||||
const recipientSk = NostrTools.generateSecretKey();
|
||||
const recipientSkHex = bytesToHex(recipientSk);
|
||||
const recipientPk = NostrTools.getPublicKey(recipientSk);
|
||||
const recipientNpub = NostrTools.nip19.npubEncode(recipientPk);
|
||||
console.log(`Recipient Pubkey: ${recipientPk}`);
|
||||
|
||||
const msg = `Test DM ${Date.now()}`;
|
||||
console.log(`Sending DM to recipient (${recipientNpub})...`);
|
||||
|
||||
// We send using the client (Sender)
|
||||
const dmResult = await client.sendDirectMessage(recipientNpub, msg);
|
||||
|
||||
if (!dmResult.ok) throw new Error(`DM Send failed: ${dmResult.error}`);
|
||||
console.log("DM Sent.");
|
||||
|
||||
// Verify decryption using the appropriate decryptor helper
|
||||
console.log("Verifying DM (waiting 2s)...");
|
||||
await new Promise(resolve => setTimeout(resolve, 2000));
|
||||
const dms = await client.listDirectMessages(pk, { limit: 10 });
|
||||
const found = dms.find(d => d.plaintext === msg);
|
||||
// Fetch the raw DM event from relays (Recipient perspective)
|
||||
console.log("Fetching DM event from relays...");
|
||||
|
||||
if (!found) {
|
||||
console.log("Fetched DMs:", dms.map(d => d.plaintext));
|
||||
throw new Error("Failed to find sent DM");
|
||||
// Filter for DMs to recipient
|
||||
const filter = {
|
||||
kinds: [4, 1059], // 4 (legacy) or 1059 (nip17)
|
||||
'#p': [recipientPk],
|
||||
limit: 1,
|
||||
since: Math.floor(Date.now() / 1000) - 60 // last 60 seconds
|
||||
};
|
||||
|
||||
// Wait for propagation
|
||||
await new Promise(r => setTimeout(r, 2000));
|
||||
|
||||
let events = [];
|
||||
const fetchStart = Date.now();
|
||||
while (Date.now() - fetchStart < TIMEOUT_MS) {
|
||||
events = await client.pool.list(TEST_RELAYS, [filter]);
|
||||
if (events.length > 0) break;
|
||||
await new Promise(r => setTimeout(r, 1000));
|
||||
}
|
||||
console.log("DM Verified.");
|
||||
|
||||
if (events.length === 0) {
|
||||
throw new Error("Failed to find sent DM on relays.");
|
||||
}
|
||||
|
||||
const dmEvent = events[0];
|
||||
console.log(`Found DM Event (Kind ${dmEvent.kind}, ID: ${dmEvent.id})`);
|
||||
|
||||
// Decrypt using decryptDM helper and Recipient Keys
|
||||
console.log("Attempting decryption using decryptDM helper...");
|
||||
|
||||
const decryptors = [];
|
||||
// NIP-04 Decryptor
|
||||
decryptors.push({
|
||||
scheme: 'nip04',
|
||||
decrypt: async (remotePubkey, ciphertext) => {
|
||||
return await NostrTools.nip04.decrypt(recipientSkHex, remotePubkey, ciphertext);
|
||||
}
|
||||
});
|
||||
// NIP-44 Decryptor
|
||||
decryptors.push({
|
||||
scheme: 'nip44',
|
||||
supportsGiftWrap: true,
|
||||
decrypt: (remotePubkey, ciphertext) => {
|
||||
const conversationKey = NostrTools.nip44.v2.utils.getConversationKey(recipientSk, remotePubkey);
|
||||
return NostrTools.nip44.v2.decrypt(ciphertext, conversationKey);
|
||||
}
|
||||
});
|
||||
|
||||
const context = {
|
||||
actorPubkey: recipientPk,
|
||||
decryptors
|
||||
};
|
||||
|
||||
const decryptResult = await decryptDM(dmEvent, context);
|
||||
|
||||
if (!decryptResult.ok) {
|
||||
console.error("Decryption errors:", decryptResult.errors);
|
||||
throw new Error("DM decryption failed");
|
||||
}
|
||||
|
||||
if (decryptResult.plaintext !== msg) {
|
||||
throw new Error(`DM plaintext mismatch. Expected "${msg}", got "${decryptResult.plaintext}"`);
|
||||
}
|
||||
console.log("DM Verified (Decryption successful).");
|
||||
|
||||
console.log("\nAll Interop Tests Passed!");
|
||||
process.exit(0);
|
||||
|
||||
Reference in new issue
Block a user