mirror of
https://github.com/block/buzz.git
synced 2026-10-29 13:31:44 +00:00
109 lines
9.5 KiB
TypeScript
109 lines
9.5 KiB
TypeScript
import { test } from 'node:test';
|
|
import assert from 'node:assert/strict';
|
|
import {mkdtempSync,realpathSync,readFileSync,rmSync} from 'node:fs';
|
|
import {spawn} from 'node:child_process';
|
|
import {writePrivate} from '../src/storage.ts';
|
|
import {tmpdir} from 'node:os';
|
|
import {join,resolve} from 'node:path';
|
|
import {setTimeout as delay} from 'node:timers/promises';
|
|
import {host,provision} from '../src/host.ts';
|
|
import {relay} from '../src/relay.ts';
|
|
import {connect} from '../src/client.ts';
|
|
import {newKey,publicKey,message,type Message} from '../src/protocol.ts';
|
|
import {createGenesis} from '../src/assignment.ts';
|
|
import {profileRevision} from '../src/profiles.ts';
|
|
test('original reviewer actual TUI profiled Move materializes one truthful named revision across reselect, Restart and reopen', { timeout: 30000 }, async () => {
|
|
const dir=realpathSync(mkdtempSync(join(tmpdir(),'bh-review-named-'))), owner=newKey(), key=newKey(), agent=publicKey(key);
|
|
const root=createGenesis(publicKey(owner),agent,'s');
|
|
for(const name of ['s','t']) provision(join(dir,name),{host:name,ownerSecret:owner,agentSecret:key,runner:realpathSync(process.execPath),args:[resolve('test/runner.ts')],workspace:dir,mode:'fixture'},root);
|
|
const server=await relay(0,publicKey(owner),join(dir,'relay.json')); const address=server.address(); assert.ok(address&&typeof address!=='string');
|
|
const url=`ws://127.0.0.1:${address.port}`, seen:Message[]=[];
|
|
let s=await host(join(dir,'s'),url), t=await host(join(dir,'t'),url); const c=connect(url,owner,m=>seen.push(m)); await c.ready;
|
|
const state=(h:string)=>JSON.parse(readFileSync(join(dir,h,'journal.json'),'utf8'));
|
|
async function until(f:()=>boolean){for(let i=0;i<500;i++){if(f())return;await delay(20);}throw Error('probe deadline');}
|
|
async function send(type:Message['type'],h:string,body:any={},revision=state(h).revision){const m=message(type,h,agent,revision,body);c.send(m);await until(()=>seen.some(r=>r.type==='receipt'&&r.body.operation===m.id));return {m,r:seen.find(r=>r.type==='receipt'&&r.body.operation===m.id)!};}
|
|
try{
|
|
const p={name:'Source',parent:null,instructions:'Reviewer source behavior',revision:profileRevision('Source',null,'Reviewer source behavior')}; c.send(message('profile','profiles','profiles',0,p)); await delay(100);
|
|
assert.match(String((await send('save','s',{...state('s').selected,profile:p.revision,behavior:p})).r.body.result),/saved/);
|
|
assert.match(String((await send('save','t',{configurationAction:'create',name:'Destination'})).r.body.result),/saved/);
|
|
const before=state('t').configurations.Destination;
|
|
assert.equal((await send('start','s')).r.body.result,'accepted');
|
|
const formerSourceActual=state('s').actual;
|
|
// Reordered local setup JSON is the same validated prepared input, not repair.
|
|
const targetSetup=join(dir,'t','setup.json');
|
|
writePrivate(targetSetup,Object.fromEntries(Object.entries(JSON.parse(readFileSync(targetSetup,'utf8'))).reverse()));
|
|
writePrivate(join(dir,'identity.json'),{secret:owner});
|
|
const child=spawn(process.execPath,['src/cli.ts','tui',join(dir,'identity.json'),url],{stdio:['pipe','pipe','pipe']});
|
|
let transcript='';child.stdout.on('data',x=>transcript+=x);child.stderr.on('data',x=>transcript+=x);
|
|
const exited=new Promise(resolve=>child.once('exit',resolve));const timer=setTimeout(()=>child.kill('SIGTERM'),15000);
|
|
try { let cursor=0;for(const [prompt,answer] of [['beehive> ','select s'],['beehive> ','move'],['Destination number: ','1'],['Confirm [yes/no]: ','yes'],['beehive> ','quit']]){await until(()=>transcript.indexOf(prompt!,cursor)>=0);cursor=transcript.indexOf(prompt!,cursor)+prompt!.length;if(answer==='quit')await until(()=>state('t').phase==='running');await delay(150);child.stdin.write(answer+'\n');}assert.equal(await exited,0); } finally {clearTimeout(timer);if(child.exitCode===null)child.kill('SIGTERM');console.log('Original reviewer actual TUI transcript:\n'+transcript);}
|
|
assert.doesNotMatch(transcript,/preflight failed/);
|
|
await until(()=>state('t').phase==='running');
|
|
const move=seen.find(m=>m.type==='move'&&m.host==='s')!;
|
|
const grant=seen.find(m=>m.type==='grant')!;
|
|
assert.equal(state('s').assignment.assignedHost,'t'); assert.equal(state('s').phase,'stopped');
|
|
assert.deepEqual(state('s').runs[formerSourceActual.run],formerSourceActual);
|
|
assert.deepEqual(state('t').selected,state('t').actual.selection);
|
|
assert.deepEqual(state('t').configurations.Destination,state('t').selected);
|
|
assert.equal(state('t').selected.behavior.instructions,p.instructions);
|
|
assert.equal(state('t').selected.configuration.revision,before.configuration.revision+1);
|
|
assert.deepEqual(state('t').preparations[move.id].candidate,before);
|
|
assert.equal(before.profile,'default'); assert.equal(before.configuration.revision,1);
|
|
assert.equal(state('t').assignment.chain.at(-1).materialization,'named-v1');
|
|
assert.deepEqual(state('t').assignment.chain.at(-1).selection,state('t').selected);
|
|
const movedBytes=readFileSync(join(dir,'t','journal.json'));
|
|
c.send(move); c.send(grant); await delay(100);
|
|
assert.deepEqual(readFileSync(join(dir,'t','journal.json')),movedBytes);
|
|
const actual=state('t').actual;
|
|
const selected=await send('save','t',{configurationAction:'select',name:'Destination'});
|
|
assert.equal(state('t').selected.profile,p.revision);assert.deepEqual(state('t').selected.configuration,actual.selection.configuration);assert.deepEqual(state('t').actual,actual);
|
|
const bytes=readFileSync(join(dir,'t','journal.json')); c.send(selected.m);await delay(100);assert.deepEqual(readFileSync(join(dir,'t','journal.json')),bytes);
|
|
assert.equal((await send('save','t',{configurationAction:'rename',name:'Destination',newName:'Later'},selected.m.revision)).r.body.result,'revision-conflict');
|
|
assert.equal(String((await send('save','t',{...state('t').selected,runner:'/bin/sh'})).r.body.result).includes('Unexpected'),true);
|
|
await send('restart','t');assert.equal(state('t').actual.selection.profile,p.revision);assert.deepEqual(state('t').actual.selection.configuration,actual.selection.configuration);assert.equal(state('t').runs[actual.run].selection.profile,p.revision);
|
|
const restarted=state('t').actual;
|
|
assert.deepEqual(restarted.selection,actual.selection);
|
|
assert.deepEqual(state('t').runs[actual.run],actual);
|
|
await s.close(); await t.close(); s=await host(join(dir,'s'),url); t=await host(join(dir,'t'),url);
|
|
assert.equal(state('s').assignment.assignedHost,'t'); assert.equal(state('s').phase,'stopped');
|
|
assert.deepEqual(state('s').runs[formerSourceActual.run],formerSourceActual);
|
|
assert.deepEqual(state('t').runs[actual.run],actual);
|
|
assert.deepEqual(state('t').runs[restarted.run],restarted);
|
|
const reopenedBytes=readFileSync(join(dir,'t','journal.json')); c.send(grant); await delay(100);
|
|
assert.deepEqual(readFileSync(join(dir,'t','journal.json')),reopenedBytes);
|
|
await send('save','t',{configurationAction:'select',name:'Destination'});
|
|
assert.equal((await send('restart','t')).r.body.result,'accepted');
|
|
assert.deepEqual(state('t').actual.selection,actual.selection);
|
|
console.log('PASS: original TUI Move, duplicate grant, reselect/Restart and both host reopens retain the same effective named snapshot.');
|
|
|
|
const oldRun=state('t').actual;
|
|
await send('save','t',{configurationAction:'select',name:'default'});
|
|
assert.match(String((await send('save','t',{configurationAction:'remove',name:'Destination'})).r.body.result),/saved/);
|
|
assert.deepEqual(state('t').actual,oldRun);assert.deepEqual(state('t').runs[oldRun.run],oldRun);
|
|
const setupBefore=readFileSync(join(dir,'t','setup.json'));
|
|
for(const body of [
|
|
{...state('t').selected,setup:'unknown'}, {...state('t').selected,env:{TOKEN:'not-a-secret'}},
|
|
{...state('t').selected,agent:'wrong'}, {...state('t').selected,workspace:'/not/allowed'},
|
|
{...state('t').selected,configuration:{name:'default',revision:0}},
|
|
{configurationAction:'select',name:'missing'}, {configurationAction:'create',name:'__proto__'},
|
|
{configurationAction:'remove',name:'default'}, {configurationAction:'rename',name:'default',newName:'bad/name'},
|
|
]) {
|
|
const before=state('t'); const r=(await send('save','t',body)).r;
|
|
assert.doesNotMatch(String(r.body.result),/saved/);
|
|
assert.equal(state('t').revision,before.revision);assert.deepEqual(state('t').selected,before.selected);assert.deepEqual(state('t').actual,before.actual);
|
|
}
|
|
assert.deepEqual(readFileSync(join(dir,'t','setup.json')),setupBefore);
|
|
console.log('PASS: removal of inactive but actual/history-referenced configuration preserves run snapshots; 9 malformed/unknown/injection/path/name/selected-removal Saves rejected without revision/selection/actual or setup changes.');
|
|
// Same real relay/host path with recursively reordered validated public fields.
|
|
const reorder=(v:any):any=>Array.isArray(v)?v.map(reorder):v!==null&&typeof v==='object'?Object.fromEntries(Object.entries(v).reverse().map(([k,x])=>[k,reorder(x)])):v;
|
|
const {behavior:_oldBehavior,...local}=state('s').selected;
|
|
const reverse=await send('move','t',{target:'s',targetRevision:state('s').revision,selection:reorder({...local,profile:'default'})});
|
|
assert.equal(reverse.r.body.result,'accepted'); await until(()=>state('s').phase==='running');
|
|
assert.deepEqual(state('s').configurations.default,state('s').actual.selection);
|
|
assert.equal(state('s').actual.selection.profile,'default');
|
|
assert.equal(state('t').phase,'stopped');
|
|
console.log('PASS: recursively reordered wire Move succeeds; target-local named reference and default behavior remain truthful.');
|
|
}finally{await s.close();await t.close();c.close();for(const ws of server.clients)ws.terminate();await new Promise<void>(r=>server.close(()=>r()));rmSync(dir,{recursive:true,force:true});}
|
|
|
|
});
|