import test from 'node:test'; import assert from 'node:assert/strict'; import {CaptureBridge, captureRequested} from '../src/capture-bridge.ts'; const ready = () => new Response(JSON.stringify({mode: 'readme-capture', ready: true, token: 'a'.repeat(64)})); test('ordinary previews and remote pages never initialize a capture bridge', async () => { const bridge = new CaptureBridge(); let called = 0; for (const href of ['http://127.0.0.1:1431/', 'https://example.com/?capture=1', 'file:///tmp/demo?capture=1', 'http://127.0.0.1:1431/?capture=0']) { assert.equal(captureRequested(href), false); assert.equal(await bridge.initialize(href, async () => { called++; return ready(); }), false); } assert.equal(called, 0); assert.equal(bridge.active, false); }); test('ready identity and nonce are required before any command dispatch', async () => { for (const info of [{mode:'other',ready:true,token:'a'.repeat(64)}, {mode:'readme-capture',ready:false,token:'a'.repeat(64)}, {mode:'readme-capture',ready:true,token:'short'}]) { const bridge = new CaptureBridge(); await assert.rejects(bridge.initialize('http://127.0.0.1:1431/?capture=1', async () => new Response(JSON.stringify(info)))); assert.equal(bridge.active, false); await assert.rejects(bridge.invoke('stats', {}, async () => assert.fail('Must not dispatch'))); } }); test('read-only commands use authenticated same-origin requests and mutation is rejected', async () => { const bridge = new CaptureBridge(); await bridge.initialize('http://127.0.0.1:1431/?capture=1', async () => ready()); let request; const result = await bridge.invoke('search_measured', {query:'real query'}, async (url, options) => { request = {url, options}; return new Response(JSON.stringify({results:[],metrics:{total_ms:1}})); }); assert.equal(result.metrics.total_ms, 1); assert.equal(request.url, 'http://127.0.0.1:1431/capture-api/search_measured'); assert.equal(request.options.headers['X-Searchi-Capture-Token'], 'a'.repeat(64)); assert.equal(request.options.redirect, 'error'); assert.equal(request.options.credentials, 'omit'); for (const command of ['open_result','index_folder','set_cpu_threads','../../other']) { await assert.rejects(bridge.invoke(command, {}, async () => assert.fail('Mutation must not dispatch')), /read-only/); } }); test('a failed reinitialization cannot retain earlier capture authorization', async () => { const bridge = new CaptureBridge(); await bridge.initialize('http://127.0.0.1:1431/?capture=1', async () => ready()); await assert.rejects(bridge.initialize('http://127.0.0.1:1431/?capture=1', async () => new Response('{}'))); assert.equal(bridge.active, false); await assert.rejects(bridge.invoke('stats', {}, async () => assert.fail('No stale authorization'))); }); test('media resolver rejects file paths, remote URLs and traversal; failed measured payload survives', async () => { const bridge = new CaptureBridge(); await bridge.initialize('http://127.0.0.1:1431/?capture=1', async () => ready()); assert.equal(bridge.mediaURL('/capture-media/cat'), 'http://127.0.0.1:1431/capture-media/cat'); for (const path of ['C:/secret.jpg','https://example.com/cat','//example.com/cat','/capture-media/../secret','/capture-media/%2e%2e','/capture-media/cat?token=other']) assert.throws(() => bridge.mediaURL(path)); await assert.rejects(bridge.invoke('hybrid_search_measured', {}, async () => new Response(JSON.stringify({message:'busy',metrics:{total_ms:4}}), {status:503})), error => error.message === 'busy' && error.metrics.total_ms === 4); });