* Fake worker speaking the same {type:'ready'|'result'} protocol as the real * one. `behavior` decides per call whether to return a result, crash (exit≠0), * hang (never reply — exercises the backstop), or wait on a promise (lets a test * hold a call in-flight to observe concurrency). Emits 'read
| 26 | * the pool has wired its listeners first. |
| 27 | */ |
| 28 | class FakeWorker implements PoolWorker { |
| 29 | private msgCb?: (m: unknown) => void; |
| 30 | private exitCb?: (code: number) => void; |
| 31 | alive = true; |
| 32 | constructor(private behavior: (m: CallMsg) => Action, readyOk = true) { |
| 33 | setTimeout(() => { if (this.alive) this.msgCb?.({ type: 'ready', ok: readyOk }); }, 0); |
| 34 | } |
| 35 | on(event: string, cb: (...args: any[]) => void): void { |
| 36 | if (event === 'message') this.msgCb = cb; |
| 37 | else if (event === 'exit') this.exitCb = cb; |
| 38 | // 'error' unused by the fakes |
| 39 | } |
| 40 | private reply(id: number, result: ToolResult): void { |
| 41 | if (this.alive) this.msgCb?.({ type: 'result', id, result }); |
| 42 | } |
| 43 | postMessage(msg: unknown): void { |
| 44 | const m = msg as CallMsg; |
| 45 | if (!m || m.type !== 'call') return; |
| 46 | const action = this.behavior(m); |
| 47 | if ('crash' in action) { |
| 48 | this.alive = false; |
| 49 | setTimeout(() => this.exitCb?.(13), 0); // simulate a crash exit |
| 50 | return; |
| 51 | } |
| 52 | if ('hang' in action) return; // never reply |
| 53 | if ('wait' in action) { void action.wait.then((r) => this.reply(m.id, r)); return; } |
| 54 | setTimeout(() => this.reply(m.id, action.result), 0); |
| 55 | } |
| 56 | terminate(): Promise<number> { this.alive = false; return Promise.resolve(0); } |
| 57 | } |
| 58 | |
| 59 | const ok = (text: string): ToolResult => ({ content: [{ type: 'text', text }] }); |
| 60 |
nothing calls this directly
no outgoing calls
no test coverage detected