import { promises as fs } from 'node:fs'; import os from 'node:os'; import path from 'node:path'; import { describe, expect, it } from 'vitest'; import { createMission, loadMission, missionFilePath } from '../src/mission.js'; describe('mission lifecycle', () => { it('creates and loads mission state files', async () => { const projectDir = await fs.mkdtemp(path.join(os.tmpdir(), 'coord-mission-')); try { const mission = await createMission({ name: 'Wave 3 Mission', projectPath: projectDir, milestones: ['Phase One', 'Phase Two'], qualityGates: 'pnpm lint && pnpm typecheck && pnpm test', description: 'Wave 3 implementation', }); expect(mission.id).toMatch(/^wave-3-mission-\d{8}$/); expect(mission.status).toBe('active'); expect(mission.milestones).toHaveLength(2); await expect(fs.stat(missionFilePath(projectDir, mission))).resolves.toBeDefined(); await expect(fs.stat(path.join(projectDir, 'docs/TASKS.md'))).resolves.toBeDefined(); await expect( fs.stat(path.join(projectDir, '.mosaic/orchestrator/mission.json')), ).resolves.toBeDefined(); const loaded = await loadMission(projectDir); expect(loaded.id).toBe(mission.id); expect(loaded.name).toBe('Wave 3 Mission'); expect(loaded.qualityGates).toBe('pnpm lint && pnpm typecheck && pnpm test'); } finally { await fs.rm(projectDir, { recursive: true, force: true }); } }); it('rejects inactive missions on load', async () => { const projectDir = await fs.mkdtemp(path.join(os.tmpdir(), 'coord-mission-inactive-')); try { const mission = await createMission({ name: 'Inactive Mission', projectPath: projectDir, }); const missionPath = missionFilePath(projectDir, mission); const payload = JSON.parse(await fs.readFile(missionPath, 'utf8')) as { status: string; }; payload.status = 'inactive'; await fs.writeFile(missionPath, `${JSON.stringify(payload, null, 2)}\n`, 'utf8'); await expect(loadMission(projectDir)).rejects.toThrow( 'Mission exists but is inactive', ); } finally { await fs.rm(projectDir, { recursive: true, force: true }); } }); });