Files
stack/packages/bus/tests/model-pending-fixture.mjs

56 lines
8.3 KiB
JavaScript

import { readTemplate } from '../../organization/src/definitions.mjs';
import { ownerConsole } from './hire-fixtures.mjs';
// #654 isolated real runtime/writer/controller with owned HTTP request gates.
import fs from 'node:fs';
import http from 'node:http';
import assert from 'node:assert/strict';
import { createHash, randomBytes } from 'node:crypto';
import { join } from 'node:path';
import { tmpdir } from 'node:os';
import { startBroker } from '../src/runtime.mjs';
import { busOrganization } from '../src/organization-context.mjs';
import { loadBusiness } from '../../organization/src/organization-config.mjs';
import { resolveInstance } from '../../organization/src/resolve.mjs';
import { businessDoc, rolesCopy, systemFor, writeJson } from '../../organization/tests/helpers.mjs';
import { sessionsFile, SESSIONS_VERSION } from '../../seat/src/session.mjs';
const hash = value => createHash('sha256').update(value).digest('hex');
const deferred = () => {let resolve;const promise=new Promise(r=>{resolve=r;});return {promise,resolve};};
export async function bounded(promise,message,ms=5000){let timer;try{return await Promise.race([promise,new Promise((_,reject)=>{timer=setTimeout(()=>reject(Error(message)),ms);})]);}finally{clearTimeout(timer);}}
export async function pendingFixture(t,{twoBusinesses=false}={}){
const root=fs.mkdtempSync(join(tmpdir(),'p654-')),closers=[],active=new Set(),gates=[],history=new Set(['fx654-manage']),receipts=[];
let server,bus,stderr;const state={keys:[],calls:[],failPut:false,nextGate:null};
const valueFree=(entries=receipts)=>{const text=JSON.stringify(entries);for(const value of history)if(text.includes(value))throw Error('fixture credential leaked');};
t.after(async()=>{const failures=[];for(const g of gates)g.release.resolve();try{await bounded(Promise.allSettled([...active]),'owned operations did not settle');}catch{failures.push('owned-operation-close');}for(const close of [...closers].reverse()){try{await close();}catch{failures.push('owned-resource-close');}}try{server?.closeAllConnections();if(server?.listening)await new Promise((resolve,reject)=>server.close(e=>e?reject(e):resolve()));}catch{failures.push('owned-server-close');}try{valueFree();}catch{failures.push('value-free-receipts');}if(stderr)process.stderr.write=stderr;try{fs.rmSync(root,{recursive:true,force:true});}catch{failures.push('owned-root-close');}if(failures.length)throw new AggregateError(failures.map(code=>Error(code)),'owned pending fixture cleanup failed');});
const configDir=join(root,'config'),dataRoot=join(root,'data'),rolesDir=rolesCopy(root),system=systemFor(root);fs.mkdirSync(dataRoot,{mode:0o700});
const doc=businessDoc(root,'demo');doc.plugins={...doc.plugins,harness:['pi']};
// Existing row331 scratch aide pattern only; repository role authority unchanged.
fs.writeFileSync(join(rolesDir,'aide.md'),'# Aide\n\n## Authority\n');writeJson(join(rolesDir,'aide.json'),{roleVersion:2,name:'aide',title:'Aide',contract:'aide.md',tools:['read'],network:'api-only',authority:{withinRole:['message.send'],crossRole:[]},credentials:[]});
const ids=twoBusinesses?['demo','other']:['demo'],files={},businesses={};for(const name of ids){const document={...structuredClone(doc),id:name};files[name]=writeJson(join(configDir,'businesses',name+'.json'),document);const business=loadBusiness(name,{dir:configDir,rolesDir}),resolved={};for(const instance of Object.keys(business.roles))resolved[instance]=resolveInstance({system,business,instance});businesses[name]=busOrganization(business,resolved);}
fs.mkdirSync(join(configDir,'secrets'),{recursive:true,mode:0o700});fs.writeFileSync(join(configDir,'secrets','proxy-management.key'),'fx654-manage',{mode:0o600});fs.mkdirSync(join(dataRoot,'bus-host'),{recursive:true,mode:0o700});writeJson(sessionsFile(dataRoot),{sessionsVersion:SESSIONS_VERSION,sessions:[]});
const sentinel=`msk-${hash(configDir).slice(0,12)}-sentinel-${randomBytes(32).toString('base64url')}`;state.keys.push(sentinel);history.add(sentinel);
server=http.createServer(async(req,res)=>{try{
const supplied=req.headers.authorization?.slice(7);if(supplied)history.add(supplied);for(const key of state.keys)history.add(key);
const management=req.url==='/v8/management/config/access/api-keys';state.calls.push(management?req.method:'PROBE');let attempted;
if(management&&req.method==='PUT'){const chunks=[];for await(const chunk of req)chunks.push(chunk);attempted=JSON.parse(Buffer.concat(chunks));for(const key of attempted)history.add(key);}
// Faults/gates are ONLY the owned fake HTTP transport boundary. Existing
// ModelKeys, pending Map, writer locks and publication remain untouched.
const g=state.nextGate;if(g&&management&&req.method===g.method){state.nextGate=null;g.entered.resolve();await g.release.promise;}
if(res.destroyed)return;
if(management){if(supplied!=='fx654-manage'){res.writeHead(401);res.end('{}');return;}if(req.method==='GET'){res.end(JSON.stringify(state.keys));return;}if(state.failPut){res.writeHead(503);res.end('{}');return;}state.keys=attempted;res.end('{"status":"ok"}');return;}
res.writeHead(state.keys.includes(supplied)?200:401);res.end('{}');
}catch{if(!res.destroyed){res.writeHead(500);res.end('{}');}}});await new Promise((resolve,reject)=>{server.once('error',reject);server.listen(0,'127.0.0.1',resolve);});assert.notEqual(server.address().port,8317);
stderr=process.stderr.write;process.stderr.write=function(chunk,...args){if(String(chunk).startsWith('model-proxy: ')){receipts.push(String(chunk));const callback=args.find(a=>typeof a==='function');callback?.();return true;}return stderr.call(this,chunk,...args);};
bus=await startBroker({dataRoot,businesses,organization:{dir:configDir,rolesDir,system},proxy:{port:server.address().port,binary:'/fixture-unused'}});closers.push(()=>bus.close());
const chief={};for(const name of ids){chief[name]=bus.broker.bindLaunch({business:name,role:'chief',run:'pending-'+name,harness:'pi'});bus.broker.request(chief[name],{verb:'role.claim'});}
const track=promise=>{active.add(promise);promise.then(()=>active.delete(promise),()=>active.delete(promise));return promise;};
const holdNext=(method='GET')=>{assert.equal(state.nextGate,null,'only one owned HTTP hold armed');const g={method,entered:deferred(),release:deferred()};gates.push(g);state.nextGate=g;return {entered:()=>bounded(g.entered.promise,'owned lifecycle HTTP boundary not observed'),release:()=>g.release.resolve()};};
const listed=(business,name)=>JSON.parse(fs.readFileSync(files[business],'utf8')).launch.instances.includes(name);
const snapshot=business=>{const journal=join(configDir,'businesses','.history',business,'changes.jsonl');return {membership:hash(fs.readFileSync(files[business])),publicationJournal:fs.existsSync(journal)?hash(fs.readFileSync(journal)):null,modelStatus:hash(JSON.stringify(bus.credentials.status())),remote:hash(JSON.stringify(state.keys)),calls:state.calls.length};};
const hire=(business,name,definition='aide',cap=chief[business])=>track(Promise.resolve().then(()=>(async()=>{const d=await bus.broker.request(cap,{verb:'hire.raise',args:{op:'hire',level:'org',name,role:{template:definition,sha256:readTemplate(rolesDir,definition).sha256},route:{harness:'pi',model:'claude-opus-5-5'},reason:'fixture pending hire'}});return bus.broker.request(ownerConsole(bus.broker,business),{verb:'hire.resolve.console',args:{id:d.id,digest:d.hire.digest,choice:'yes'}});})()));
const busy=async promise=>{const result=await bounded(promise.then(()=>({code:'unexpected-success'}),e=>({code:e.code??'unexpected-error'})),'same-business refusal not observed');assert.equal(result.code,'organization-busy','second same-business lifecycle call refuses exact pending code');};
return {root,configDir,dataRoot,rolesDir,bus,state,receipts,chief,holdNext,listed,snapshot,hire,busy,valueFree,
healthy:async(business,name)=>{const value=await bus.modelPreflight({business,role:name});const record=bus.credentials.modelRecord(business+'/'+name);assert.equal(value.key===record.key,true);assert.equal(state.keys.includes(record.key),true,'published member retains confirmed model credential');},
wrongActor:()=>{const cap=bus.broker.bindLaunch({business:'demo',role:'coder',run:'pending-coder',harness:'pi'});bus.broker.request(cap,{verb:'role.claim'});return cap;},
assertHistoryGuard:()=>{try{valueFree([{canary:sentinel}]);}catch(e){if(e.message==='fixture credential leaked')return;}throw Error('fixture history guard failed');}};
}