import { mkdirSync, mkdtempSync } from 'node:fs'; import { tmpdir } from 'node:os'; import { join } from 'node:path'; import { Command } from 'commander'; import { afterEach, describe, expect, it } from 'vitest'; import { exitStatusFor, registerWatchCommand, resolveAgentWatchTool } from './watch.js'; // The dispatch command execs a real process with inherited stdio; the spec // covers the pure resolution and exit-mapping surfaces plus the absent-tool // path (which exits without spawning). Live pass-through is exercised by the // fleet smoke test against the real brain tool. describe('resolveAgentWatchTool', () => { const saved = process.env['MOSAIC_BRAIN_HOME']; afterEach(() => { if (saved === undefined) delete process.env['MOSAIC_BRAIN_HOME']; else process.env['MOSAIC_BRAIN_HOME'] = saved; }); it('honors MOSAIC_BRAIN_HOME over the canonical brain', () => { const tmp = mkdtempSync(join(tmpdir(), 'watch-resolve-')); process.env['MOSAIC_BRAIN_HOME'] = tmp; expect(resolveAgentWatchTool('/nonexistent/mosaic-home')).toBe( join(tmp, 'tools', 'agent-watch', 'agent-watch.sh'), ); }); it('resolves inside the brain tools tree', () => { const tmp = mkdtempSync(join(tmpdir(), 'watch-resolve-')); process.env['MOSAIC_BRAIN_HOME'] = tmp; const tool = resolveAgentWatchTool(tmp); expect(tool.endsWith(join('tools', 'agent-watch', 'agent-watch.sh'))).toBe(true); }); }); describe('exitStatusFor', () => { it('maps absent tool to 127', () => { expect(exitStatusFor({ status: 0 }, false)).toBe(127); }); it('passes the tool exit status through', () => { expect(exitStatusFor({ status: 2 }, true)).toBe(2); expect(exitStatusFor({ status: 78 }, true)).toBe(78); }); it('maps signal death / null status to 125', () => { expect(exitStatusFor({ status: null }, true)).toBe(125); }); }); describe('registerWatchCommand absent-tool path', () => { const saved = process.env['MOSAIC_BRAIN_HOME']; afterEach(() => { if (saved === undefined) delete process.env['MOSAIC_BRAIN_HOME']; else process.env['MOSAIC_BRAIN_HOME'] = saved; process.exitCode = undefined; }); it('sets exitCode 127 with the resolved path when the tool is missing', async () => { const tmp = mkdtempSync(join(tmpdir(), 'watch-missing-')); // The suite directory exists but the tool file does not. mkdirSync(join(tmp, 'tools', 'agent-watch'), { recursive: true }); process.env['MOSAIC_BRAIN_HOME'] = tmp; const program = new Command(); registerWatchCommand(program); await program.parseAsync(['watch', 'list'], { from: 'user' }); expect(process.exitCode).toBe(127); }); });