zebra-spaces: wireZebraMachines orchestrator + integration tests (83 green)

Step five — composition layer. wireZebraMachines() returns a coherent
room:
  - one CallFSM
  - one SubscribeFSM
  - three PublishFSMs (mic / screen / camera)
  - lazy Map of RemoteTileFSMs created on first tileFor(kind, pubHex)
  - tileLeft(pubHex) fans LEFT to every tile keyed by that publisher

Observers wire transitions between machines but the orchestrator
itself stays pure — no WebRTC, no DOM, no fetch. The page's runtime
layers its OWN observers on top to drive real side effects, and the
test extracts the orchestrator directly.

Cascades modelled:
- CallFSM joined (from anything except reconnecting) ── starts the sub
- CallFSM reconnecting → joined does NOT re-START (sub stayed alive)
- CallFSM leaving / booted ── stops sub AND every live publish
- RemoteTileFSMs lazy: tileFor returns the same instance per key
- tileLeft sends LEFT to every kind for that pubHex

+ 11 integration tests + 1 full end-to-end scenario walking through
host publishes mic+screen / listener joins late / listener sees the
screen / host unshares / mute+prune cycle removes the tile / listener
leaves and sub stops.

Total: 83 tests passing. The pure-FSM layer + orchestrator are now
ready to be wired into the imperative call sites in the live runtime.
That's the next step — gradually replace the firefighting code paths
(sfuPublishCamera, sfuSubscribe, role transitions) by feeding events
into these machines from the existing handlers, then observing
state changes to invoke the side effects. Tests catch regressions
on the pure layer while the QA loop catches what touches the wire.
This commit is contained in:
Russell Ballestrini 2026-06-02 11:10:16 -04:00
parent a29baeee7e
commit d9a743a680
No known key found for this signature in database
2 changed files with 233 additions and 10 deletions

View file

@ -29,20 +29,21 @@ function extract(re){
return src.slice(m.index, j);
}
const createFSMSrc = extract(/function createFSM\(/);
const publishSpecSrc = extract(/const publishSpec = /);
const subscribeSpecSrc = extract(/const subscribeSpec = /);
const remoteTileSpecSrc = extract(/const remoteTileSpec = /);
const callSpecSrc = extract(/const callSpec = /);
const createFSMSrc = extract(/function createFSM\(/);
const publishSpecSrc = extract(/const publishSpec = /);
const subscribeSpecSrc = extract(/const subscribeSpec = /);
const remoteTileSpecSrc = extract(/const remoteTileSpec = /);
const callSpecSrc = extract(/const callSpec = /);
const wireMachinesSrc = extract(/function wireZebraMachines\(/);
/* Function-constructor scope so `const` declarations are visible at the
* harness's `return` they would NOT leak through a bare `eval()`. */
const harness = new Function(
createFSMSrc + '\n' + publishSpecSrc + '\n' + subscribeSpecSrc + '\n' +
remoteTileSpecSrc + '\n' + callSpecSrc +
'\nreturn { createFSM, publishSpec, subscribeSpec, remoteTileSpec, callSpec };'
remoteTileSpecSrc + '\n' + callSpecSrc + '\n' + wireMachinesSrc +
'\nreturn { createFSM, publishSpec, subscribeSpec, remoteTileSpec, callSpec, wireZebraMachines };'
);
const { createFSM, publishSpec, subscribeSpec, remoteTileSpec, callSpec } = harness();
const { createFSM, publishSpec, subscribeSpec, remoteTileSpec, callSpec, wireZebraMachines } = harness();
let pass = 0, fail = 0;
function test(name, fn){
@ -698,5 +699,156 @@ test('leaving + every other event is a no-op', () => {
eq(m.state, 'leaving');
});
console.log('\n=== integration: wireZebraMachines ===\n');
console.log('CallFSM joined → SubscribeFSM auto-starts:');
test('idle: nothing is running', () => {
const w = wireZebraMachines();
eq(w.call.state, 'idle');
eq(w.sub.state, 'off');
eq(w.pubs.mic.state, 'off');
});
test('ENTER → WELCOME: sub goes off → connecting', () => {
const w = wireZebraMachines();
w.call.send('ENTER', { code: 'r', handle: 'h' });
w.call.send('WELCOME', { uuid: 'u', role: 'host' });
eq(w.call.state, 'joined');
eq(w.sub.state, 'connecting');
});
test('coming back from reconnecting does NOT re-START sub', () => {
const w = wireZebraMachines();
w.call.send('ENTER', { code: 'r', handle: 'h' });
w.call.send('WELCOME', { uuid: 'u', role: 'host' });
w.sub.send('CONNECTED', { pc: { id: 'pc' } }); /* sub is now subscribed */
w.call.send('WS_DROPPED');
w.call.send('WELCOME', { role: 'host' }); /* reconnect */
/* sub should still be subscribed — we did NOT send START again */
eq(w.sub.state, 'subscribed');
});
console.log('CallFSM leaving / booted → all live publishes get STOP:');
test('LEAVE while publishing mic → mic transitions to stopping', () => {
const w = wireZebraMachines();
w.call.send('ENTER', { code: 'r', handle: 'h' });
w.call.send('WELCOME', { uuid: 'u', role: 'host' });
w.pubs.mic.send('START');
w.pubs.mic.send('ACQUIRED', { stream: {} });
w.pubs.mic.send('NEGOTIATED', { pc: {}, peerID: 'p' });
eq(w.pubs.mic.state, 'live');
w.call.send('LEAVE');
eq(w.pubs.mic.state, 'stopping');
});
test('LEAVE while screen is mid-acquire → screen cancels into off', () => {
const w = wireZebraMachines();
w.call.send('ENTER', { code: 'r', handle: 'h' });
w.call.send('WELCOME', { uuid: 'u', role: 'host' });
w.pubs.screen.send('START');
eq(w.pubs.screen.state, 'acquiring');
w.call.send('LEAVE');
eq(w.pubs.screen.state, 'off');
});
test('BOOTED stops every live publish at once', () => {
const w = wireZebraMachines();
w.call.send('ENTER', { code: 'r', handle: 'h' });
w.call.send('WELCOME', { uuid: 'u', role: 'host' });
for (const k of ['mic', 'screen', 'camera']){
w.pubs[k].send('START');
w.pubs[k].send('ACQUIRED', { stream: {} });
w.pubs[k].send('NEGOTIATED', { pc: {}, peerID: k });
}
w.call.send('BOOTED', { by: 'host' });
eq(w.pubs.mic.state, 'stopping');
eq(w.pubs.screen.state, 'stopping');
eq(w.pubs.camera.state, 'stopping');
});
test('BOOTED stops sub too', () => {
const w = wireZebraMachines();
w.call.send('ENTER', { code: 'r', handle: 'h' });
w.call.send('WELCOME', { uuid: 'u', role: 'host' });
w.sub.send('CONNECTED', { pc: {} });
w.call.send('BOOTED', { by: 'host' });
eq(w.sub.state, 'stopping');
});
console.log('RemoteTileFSMs — tileFor lazy create, tileLeft fans LEFT:');
test('tileFor returns same FSM for repeat calls (same kind+pubHex)', () => {
const w = wireZebraMachines();
const a = w.tileFor('camera', 'pub1');
const b = w.tileFor('camera', 'pub1');
truthy(a === b, 'tileFor is idempotent per key');
});
test('tileFor creates separate FSMs per kind', () => {
const w = wireZebraMachines();
const cam = w.tileFor('camera', 'pub1');
const scr = w.tileFor('screen', 'pub1');
truthy(cam !== scr, 'camera ≠ screen for same pubHex');
});
test('tileLeft sends LEFT to every tile keyed by that pubHex (every kind)', () => {
const w = wireZebraMachines();
const cam = w.tileFor('camera', 'pubA');
const scr = w.tileFor('screen', 'pubA');
const other = w.tileFor('camera', 'pubB');
cam.send('TRACK_ARRIVED', { stream: {} });
scr.send('TRACK_ARRIVED', { stream: {} });
other.send('TRACK_ARRIVED', { stream: {} });
w.tileLeft('pubA');
eq(cam.state, 'removed');
eq(scr.state, 'removed');
eq(other.state, 'receiving'); /* unrelated publisher untouched */
});
console.log('end-to-end scenario: host joins, publishes mic + screen, listener sees them, screen unshares:');
test('full round trip across the wire', () => {
const host = wireZebraMachines();
const listener = wireZebraMachines();
host.call.send('ENTER', { code: 'room-a', handle: 'host' });
host.call.send('WELCOME', { uuid: 'host-uuid', role: 'host' });
/* host's sub starts connecting (then would CONNECT via signal) */
host.sub.send('CONNECTED', { pc: {} });
/* host publishes mic */
host.pubs.mic.send('START');
host.pubs.mic.send('ACQUIRED', { stream: { id: 'mic-stream' } });
host.pubs.mic.send('NEGOTIATED', { pc: {}, peerID: 'mic-peer' });
/* host publishes screen */
host.pubs.screen.send('START');
host.pubs.screen.send('ACQUIRED', { stream: { id: 'scr-stream' } });
host.pubs.screen.send('NEGOTIATED', { pc: {}, peerID: 'scr-peer' });
/* listener joins late */
listener.call.send('ENTER', { code: 'room-a', handle: 'listener' });
listener.call.send('WELCOME', { uuid: 'l-uuid', role: 'listener' });
/* their sub connects + receives host's screen track */
listener.sub.send('CONNECTED', { pc: {} });
const screenTile = listener.tileFor('screen', 'host-pub-hex');
screenTile.send('TRACK_ARRIVED', { stream: { id: 'scr-on-listener' } });
eq(screenTile.state, 'receiving');
/* host stops sharing the screen */
host.pubs.screen.send('STOP');
host.pubs.screen.send('DONE');
eq(host.pubs.screen.state, 'off');
/* on the listener side the SFU stops the transceiver track mutes
* debounce expires PRUNE removed */
screenTile.send('MUTED');
screenTile.send('PRUNE');
eq(screenTile.state, 'removed');
/* listener leaves */
listener.call.send('LEAVE');
eq(listener.sub.state, 'stopping');
});
console.log('\n' + pass + ' passed, ' + fail + ' failed');
process.exit(fail === 0 ? 0 : 1);