Files
buzz/beehive/test/owner-ipc.test.ts
2026-09-21 17:22:16 -04:00

51 lines
3.5 KiB
TypeScript

import { once } from 'node:events';
import { test } from 'node:test';
import assert from 'node:assert/strict';
import { mkdtempSync, writeFileSync, rmSync, existsSync } from 'node:fs';
import { tmpdir } from 'node:os';
import { join } from 'node:path';
import { nip19 } from 'nostr-tools';
import { localOwner } from '../src/owner-client.ts';
const sleep = (ms: number) => new Promise(resolve => setTimeout(resolve, ms));
test('real private IPC binds public snapshots to session, cancel, signout and restart', async () => {
const home = mkdtempSync(join(tmpdir(), 'owner-tui-fixture-'));
writeFileSync(join(home, 'desktop-mode'), 'available');
const open = () => localOwner({ helper: new URL('./owner-tui-fixture-child.ts', import.meta.url), environment: { PATH: process.env.PATH, BEEHIVE_HOME: home } });
let client = open(); const snapshots: unknown[] = []; client.subscribe(s => snapshots.push(s));
try {
assert.equal(await client.request({ action: 'probe' }), true); assert.equal(client.snapshot().desktop, 'available');
const nsec = nip19.nsecEncode(Buffer.from('1'.repeat(64), 'hex'));
client.secret('append', nsec);
assert.equal(await client.request({ action: 'signin-nsec', relay: 'wss://relay.example' }), true);
assert.equal(client.snapshot().signedIn, true);
assert.doesNotMatch(JSON.stringify(snapshots), new RegExp(nsec));
await client.request({ action: 'signout' }); assert.equal(client.snapshot().signedIn, false);
writeFileSync(join(home, 'desktop-mode'), 'delayed');
const pending = client.request({ action: 'signin-desktop' }); await sleep(80); client.cancel();
assert.equal(await pending, false); assert.equal(client.snapshot().signedIn, false);
client.dispose(); client = open();
await client.request({ action: 'probe' }); assert.equal(client.snapshot().signedIn, false);
assert.equal(existsSync(join(home, '.beehive/owner/controller.json')), true);
} finally { client.dispose(); rmSync(home, { recursive: true, force: true }); }
});
test('owner child disconnect drains earlier operations after a later signout resolves', async () => {
const { fork } = await import('node:child_process');
const { fileURLToPath } = await import('node:url');
const home=mkdtempSync(join(tmpdir(),'owner-disconnect-'));
const loader=join(home,'isolate.mjs'), marker=join(home,'drained');
writeFileSync(loader,`import {registerHooks} from 'node:module';
registerHooks({load(url,ctx,next){if(!url.endsWith('/src/owner-service.ts'))return next(url,ctx);return {format:'module',shortCircuit:true,source:${JSON.stringify(`import {writeFileSync} from 'node:fs';
export class OwnerService {
subscribe() {} secret() {} cancel() {} dispose() {}
async request(r) { if(r.action==='host-reset') { process.send({started:true});await new Promise(resolve=>setTimeout(resolve,200));writeFileSync(${JSON.stringify(marker)},'drained'); } return true; }
}`)}};}});`);
const child=fork(fileURLToPath(new URL('../src/owner-child.ts',import.meta.url)),[],{execPath:process.execPath,execArgv:['--import',loader],silent:true,env:{HOME:home,PATH:'/usr/bin:/bin'}});
try {
const started=once(child,'message');child.send({type:'request',id:1,request:{action:'host-reset'}});await started;
const signedout=once(child,'message');child.send({type:'request',id:2,request:{action:'signout'}});assert.equal((await signedout)[0].id,2);
const exited=once(child,'exit');child.disconnect();await exited;
assert.equal(existsSync(marker),true,'earlier cleanup completed before process exit');
} finally {child.kill();rmSync(home,{recursive:true,force:true});}
});