diff --git a/src/test/e2e/architect-workflow.test.ts b/src/test/e2e/architect-workflow.test.ts new file mode 100644 index 0000000..943a8b3 --- /dev/null +++ b/src/test/e2e/architect-workflow.test.ts @@ -0,0 +1,102 @@ +/** + * E2E Tests for Architect Workflow + * + * Tests the complete architect workflow from discussion through phase creation: + * - Discuss mode: Gather context, answer questions, capture decisions + * - Breakdown mode: Decompose initiative into phases + * - Full workflow: Discuss -> Breakdown -> Phase persistence + * + * Uses TestHarness from src/test/ for full system wiring. + */ + +import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest'; +import { createTestHarness, type TestHarness } from '../index.js'; +import type { AgentStoppedEvent } from '../../events/types.js'; + +describe('Architect Workflow E2E', () => { + let harness: TestHarness; + + beforeEach(() => { + harness = createTestHarness(); + }); + + afterEach(() => { + harness.cleanup(); + vi.useRealTimers(); + }); + + describe('discuss mode', () => { + it('should spawn architect in discuss mode and complete with decisions', async () => { + vi.useFakeTimers(); + + // Create initiative + const initiative = await harness.createInitiative('Auth System', 'User authentication'); + + // Set up discuss completion scenario + harness.setArchitectDiscussComplete('auth-discuss', [ + { topic: 'Auth Method', decision: 'JWT', reason: 'Stateless, scalable' }, + { topic: 'Token Storage', decision: 'httpOnly cookie', reason: 'XSS protection' }, + ], 'Auth approach decided'); + + // Spawn architect in discuss mode + const agent = await harness.caller.spawnArchitectDiscuss({ + name: 'auth-discuss', + initiativeId: initiative.id, + }); + + expect(agent.mode).toBe('discuss'); + + // Wait for completion + await harness.advanceTimers(); + + // Verify agent stopped with context_complete + const events = harness.getEmittedEvents('agent:stopped') as AgentStoppedEvent[]; + expect(events).toHaveLength(1); + expect(events[0].payload.reason).toBe('context_complete'); + }); + + it('should pause on questions and resume with answers', async () => { + vi.useFakeTimers(); + + const initiative = await harness.createInitiative('Auth System'); + + // First, agent asks questions + harness.setArchitectDiscussQuestions('auth-discuss', [ + { id: 'q1', question: 'JWT or Session?', options: [{ label: 'JWT' }, { label: 'Session' }] }, + { id: 'q2', question: 'OAuth providers?' }, + ]); + + const agent = await harness.caller.spawnArchitectDiscuss({ + name: 'auth-discuss', + initiativeId: initiative.id, + }); + + await harness.advanceTimers(); + + // Agent should be waiting + const waitingAgent = await harness.caller.getAgent({ name: 'auth-discuss' }); + expect(waitingAgent?.status).toBe('waiting_for_input'); + + // Get pending questions + const pending = await harness.mockAgentManager.getPendingQuestions(agent.id); + expect(pending?.questions).toHaveLength(2); + + // Now set up completion scenario for after resume + harness.setArchitectDiscussComplete('auth-discuss', [ + { topic: 'Auth', decision: 'JWT', reason: 'User chose' }, + ], 'Complete'); + + // Resume with answers + await harness.caller.resumeAgent({ + name: 'auth-discuss', + answers: { q1: 'JWT', q2: 'Google, GitHub' }, + }); + + await harness.advanceTimers(); + + // Should complete + const finalAgent = await harness.caller.getAgent({ name: 'auth-discuss' }); + expect(finalAgent?.status).toBe('idle'); + }); + }); +});