Files
safetynet/tests/broker-service-supervisor.test.mjs

63 lines
4.8 KiB
JavaScript

import test from 'node:test';
import assert from 'node:assert/strict';
import path from 'node:path';
import {spawn} from 'node:child_process';
import {createRequire} from 'node:module';
import {fileURLToPath} from 'node:url';
import {defaultPolicy} from '../packages/core/policy.mjs';
const {createServiceBrokerSupervisor}=createRequire(import.meta.url)('../apps/browser/broker-service-supervisor.cjs');
const {createFixtureDiagnostics}=createRequire(import.meta.url)('./fixtures/broker-native-diagnostics.cjs');
const {createSyntheticAudit}=createRequire(import.meta.url)('./fixtures/broker-durable-audit.cjs');
const root=path.resolve(fileURLToPath(new URL('../',import.meta.url)));
function setup(extra={}){
let launched,quarantines=0;const events=[];
const supervisor=createServiceBrokerSupervisor({installationRoot:root,verifyInstallation:()=>true,verifyPackage:()=>true,persistAudit:event=>{events.push(event);return {persisted:true,eventId:event.eventId};},acceptCertificate:()=>true,onQuarantine:()=>quarantines++,deadlineMs:5000,shutdownMs:5000,
launch:(executable,args,options)=>{launched={executable,args,options};return spawn(process.execPath,[fileURLToPath(new URL('./fixtures/broker-service-stdio-child.cjs',import.meta.url))],options);},...extra});
return {supervisor,events,get launched(){return launched;},get quarantines(){return quarantines;}};
}
test('service transport uses fixed native command and real bounded stdio protocol',async()=>{
const f=setup();try{
const ready=await f.supervisor.start({policyVersion:3,port:43871,config:{port:43871}});
assert.equal(ready.port,43871);assert.equal(f.supervisor.status().ready,true);
assert.equal(f.launched.executable,path.join(root,'safetynet-service-host.exe'));assert.deepEqual(f.launched.args,['--client-stdio']);
assert.equal(f.launched.options.shell,false);assert.equal(f.launched.options.windowsHide,true);assert.deepEqual(f.launched.options.stdio,['pipe','pipe','ignore']);
assert.ok(Object.keys(f.launched.options.env).every(key=>['SystemRoot','WINDIR','TEMP','TMP','USERPROFILE','LOCALAPPDATA','APPDATA'].includes(key)));
assert.equal(f.launched.options.env.SAFETYNET_SERVICE_CLIENT_SID,undefined);assert.equal(f.launched.options.env.NODE_OPTIONS,undefined);
}finally{await f.supervisor.close();}
assert.equal(f.quarantines,0);assert.equal(f.supervisor.status().closed,true);
});
test('real stdio audit roundtrip retains event identity and durable receipt',async()=>{
const f=setup();try{
await f.supervisor.start({policyVersion:3,port:43871,config:{port:43871,audit:true}});
const until=Date.now()+3000;while(!f.events.length&&Date.now()<until)await new Promise(resolve=>setTimeout(resolve,10));
assert.equal(f.events.length,1);assert.equal(f.events[0].eventId,'12345678-1234-4234-8234-123456789abc');
assert.equal(f.supervisor.status().ready,true);
}finally{await f.supervisor.close();}
});
test('malformed native stream quarantines and cannot restart',async()=>{
const f=setup();await assert.rejects(f.supervisor.start({policyVersion:3,port:43871,config:{malformed:true}}),/unavailable/);
assert.equal(f.quarantines,1);assert.equal(f.supervisor.status().ready,false);
await assert.rejects(f.supervisor.start({policyVersion:3,port:43871}),/invalid/);await f.supervisor.close();
});
test('unverified installed native client or broker payload never launches',async()=>{
for(const extra of [{verifyInstallation:()=>false},{verifyPackage:()=>false}]){
const f=setup(extra);await assert.rejects(f.supervisor.start({policyVersion:3,port:43871,config:{}}),/unavailable/);assert.equal(f.launched,undefined);await f.supervisor.close();
}
});
test('real service-adapter subprocess exports audit-correlated OTLP timings',async t=>{
const audit=createSyntheticAudit(t),diagnostics=await createFixtureDiagnostics({enabled:true,policy:defaultPolicy(),audit});
const f=setup({persistAudit:diagnostics.persist,traceOperation:diagnostics.stage});let proof;
try{
await diagnostics.run({bytes:0},async()=>{
await f.supervisor.start({policyVersion:3,port:43871,config:{port:43871,audit:true}});
const until=Date.now()+3000;while(!audit.events().length&&Date.now()<until)await new Promise(resolve=>setTimeout(resolve,10));
assert.equal(audit.events().length,1);assert.equal(f.supervisor.status().ready,true);
await f.supervisor.close();
});
}finally{try{await f.supervisor.close();}finally{proof=await diagnostics.finish();}}
assert.ok(proof.spanCount>=2);assert.equal(proof.invalid,0);assert.equal(proof.failedExports,0);assert.equal(proof.lifetimeFailedExports,0);
assert.deepEqual(proof.stages.map(value=>value.name).sort(),['browser.broker.shutdown','browser.broker.start','browser.broker.verify']);
assert.ok(proof.stages.every(value=>value.parentSpanId===proof.parentSpanId&&value.durationMs>0));
t.diagnostic(JSON.stringify({kind:'actual-service-adapter-test-child',...proof}));
});