import { mkdir, mkdtemp, rm } from 'node:fs/promises'; import { homedir, tmpdir } from 'node:os'; import { join } from 'node:path'; import { afterEach, beforeEach, describe, expect, it } from 'vitest'; import { brainHomeIsActive, fleetAgentEnvDir, fleetProfilesDir, fleetRolesLocalDir, fleetStateDir, resolveBrainHome, type BrainHomeOptions, } from './brain-home.js'; describe('fleet brain-home resolution', (): void => { let cleanup: string | undefined; const savedBrainEnv = process.env['MOSAIC_BRAIN_HOME']; beforeEach((): void => { delete process.env['MOSAIC_BRAIN_HOME']; }); afterEach(async (): Promise => { if (savedBrainEnv === undefined) { delete process.env['MOSAIC_BRAIN_HOME']; } else { process.env['MOSAIC_BRAIN_HOME'] = savedBrainEnv; } if (cleanup !== undefined) { await rm(cleanup, { recursive: true, force: true }); cleanup = undefined; } }); async function makeTmp(): Promise { const root = await mkdtemp(join(tmpdir(), 'mosaic-brain-home-')); cleanup = root; return root; } it('MOSAIC_BRAIN_HOME env wins over every other signal', (): void => { process.env['MOSAIC_BRAIN_HOME'] = '/explicit/brain'; expect(resolveBrainHome('/any/mosaic-home')).toBe('/explicit/brain'); expect(fleetAgentEnvDir('/any/mosaic-home')).toBe('/explicit/brain/fleet/agents'); expect(brainHomeIsActive('/any/mosaic-home')).toBe(true); }); it('injected envBrainHome wins identically (test seam)', (): void => { const opts: BrainHomeOptions = { envBrainHome: '/injected/brain' }; expect(resolveBrainHome('/any/mosaic-home', opts)).toBe('/injected/brain'); expect(fleetAgentEnvDir('/any/mosaic-home', opts)).toBe('/injected/brain/fleet/agents'); }); it('a non-default mosaicHome never adopts the canonical brain (hermetic legacy)', (): void => { const mosaicHome = '/tmp/not-the-default-config-home'; expect(resolveBrainHome(mosaicHome)).toBe(mosaicHome); expect(brainHomeIsActive(mosaicHome)).toBe(false); expect(fleetAgentEnvDir(mosaicHome)).toBe(join(mosaicHome, 'fleet', 'agents')); }); it('the default config home adopts the brain when it carries fleet/agents', async (): Promise => { const root = await makeTmp(); const brain = join(root, 'brain'); await mkdir(join(brain, 'fleet', 'agents'), { recursive: true }); const configHome = join(root, 'config', 'mosaic'); const opts: BrainHomeOptions = { homes: { brain, configDefault: configHome } }; expect(resolveBrainHome(configHome, opts)).toBe(brain); expect(fleetAgentEnvDir(configHome, opts)).toBe(join(brain, 'fleet', 'agents')); expect(fleetRolesLocalDir(configHome, opts)).toBe(join(brain, 'fleet', 'roles.local')); expect(fleetProfilesDir(configHome, opts)).toBe(join(brain, 'fleet', 'profiles')); expect(fleetStateDir(configHome, opts)).toBe(join(brain, 'fleet')); expect(brainHomeIsActive(configHome, opts)).toBe(true); }); it('the default config home stays legacy when no brain exists', async (): Promise => { const root = await makeTmp(); const configHome = join(root, 'config', 'mosaic'); const opts: BrainHomeOptions = { homes: { brain: join(root, 'brain'), configDefault: configHome }, }; expect(resolveBrainHome(configHome, opts)).toBe(configHome); expect(brainHomeIsActive(configHome, opts)).toBe(false); }); it('an empty MOSAIC_BRAIN_HOME is ignored, not treated as set', (): void => { process.env['MOSAIC_BRAIN_HOME'] = ' '; expect(resolveBrainHome('/tmp/legacy-home')).toBe('/tmp/legacy-home'); }); it('adoption requires fleet/agents specifically, not any brain content', async (): Promise => { const root = await makeTmp(); const brain = join(root, 'brain'); await mkdir(join(brain, 'fleet'), { recursive: true }); // fleet without agents const configHome = join(root, 'config', 'mosaic'); const opts: BrainHomeOptions = { homes: { brain, configDefault: configHome } }; expect(resolveBrainHome(configHome, opts)).toBe(configHome); }); it('real-home control: a host brain is adopted only through the default home', (): void => { // Control on the un-injected path: this host carries ~/.mosaic/fleet/agents, // so the default config home resolves to the brain or legacy — both valid // canonical endpoints — while a non-default home never adopts. const defaultHome = join(homedir(), '.config', 'mosaic'); const resolved = resolveBrainHome(defaultHome); expect([defaultHome, join(homedir(), '.mosaic')]).toContain(resolved); expect(resolveBrainHome(join(homedir(), 'elsewhere', 'mosaic'))).toBe( join(homedir(), 'elsewhere', 'mosaic'), ); }); });