const { describe, test, expect, beforeEach, afterEach } = require('@jest/globals'); const ElicitationBroker = require('../../core/elicitation-broker'); const BMADMessageQueue = require('../../core/message-queue'); describe('ElicitationBroker', () => { let broker; let queue; let tempDir; beforeEach(async () => { tempDir = await global.testUtils.createTempDir(); queue = new BMADMessageQueue({ basePath: tempDir }); await queue.initialize(); broker = new ElicitationBroker(queue, { basePath: tempDir }); }); afterEach(async () => { await global.testUtils.cleanupTempDir(tempDir); }); describe('Session Management', () => { test('should create an elicitation session', async () => { const session = await broker.createSession('pm', { user_request: 'Create user story', project: 'test-project' }); expect(session.id).toMatch(/^elicit-\d+-[a-f0-9]+$/); expect(session.agent).toBe('pm'); expect(session.status).toBe('active'); expect(session.context.user_request).toBe('Create user story'); expect(session.context.elicitationHistory).toEqual([]); }); test('should load an existing session', async () => { const session = await broker.createSession('architect', { task: 'Design system' }); const loaded = await broker.loadSession(session.id); expect(loaded.id).toBe(session.id); expect(loaded.agent).toBe('architect'); expect(loaded.context.task).toBe('Design system'); }); test('should throw error for non-existent session', async () => { await expect(broker.loadSession('non-existent')).rejects.toThrow('Session non-existent not found'); }); }); describe('Question and Response Tracking', () => { test('should add questions to session', async () => { const session = await broker.createSession('pm', {}); const question1 = await broker.addQuestion(session.id, 'What is the primary use case?'); const question2 = await broker.addQuestion(session.id, 'Who are the target users?', { priority: 'high' }); expect(question1.id).toBe('q1'); expect(question1.type).toBe('question'); expect(question1.text).toBe('What is the primary use case?'); expect(question2.id).toBe('q2'); expect(question2.metadata.priority).toBe('high'); const updated = await broker.loadSession(session.id); expect(updated.context.elicitationHistory.length).toBe(2); }); test('should add responses to session', async () => { const session = await broker.createSession('qa', {}); await broker.addQuestion(session.id, 'What testing framework?'); const response = await broker.addResponse(session.id, 'Jest with AI judge tests', 'q1'); expect(response.id).toBe('r1'); expect(response.type).toBe('response'); expect(response.text).toBe('Jest with AI judge tests'); expect(response.questionId).toBe('q1'); const updated = await broker.loadSession(session.id); expect(updated.context.elicitationHistory.length).toBe(2); }); }); describe('Phase Management', () => { test('should update session phase', async () => { const session = await broker.createSession('architect', {}); await broker.updatePhase(session.id, 'requirements_gathering'); await broker.updatePhase(session.id, 'design_phase'); const updated = await broker.loadSession(session.id); expect(updated.currentPhase).toBe('design_phase'); const phaseChanges = updated.context.elicitationHistory.filter(h => h.type === 'phase_change'); expect(phaseChanges.length).toBe(2); }); }); describe('Session Completion', () => { test('should complete session with result', async () => { const session = await broker.createSession('dev', {}); await broker.addQuestion(session.id, 'Programming language?'); await broker.addResponse(session.id, 'TypeScript'); const completed = await broker.completeSession(session.id, { implementation_plan: 'Use TypeScript with Node.js' }); expect(completed.status).toBe('completed'); expect(completed.completedAt).toBeDefined(); expect(completed.result).toEqual({ implementation_plan: 'Use TypeScript with Node.js' }); expect(completed.metadata.duration).toBeGreaterThan(0); }); }); describe('Active Sessions', () => { test('should list active elicitation sessions', async () => { // Create multiple sessions const session1 = await broker.createSession('pm', { task: 'Task 1' }); const session2 = await broker.createSession('architect', { task: 'Task 2' }); await broker.completeSession(session1.id); const activeSessions = await broker.getActiveElicitations(); expect(activeSessions.length).toBe(1); expect(activeSessions[0].id).toBe(session2.id); expect(activeSessions[0].agent).toBe('architect'); }); }); describe('Elicitation Formatting', () => { test('should format elicitation prompt correctly', async () => { const session = await broker.createSession('ux-expert', {}); // Test with no history first const emptyPrompt = await broker.formatElicitationPrompt(session, 'First question?'); expect(emptyPrompt).toContain('BMAD ux-expert - Elicitation'); expect(emptyPrompt).toContain('Current Question:'); expect(emptyPrompt).toContain('First question?'); expect(emptyPrompt).not.toContain('Previous Context:'); // Now add history and test again await broker.addQuestion(session.id, 'What is the target demographic?'); await broker.addResponse(session.id, 'Young professionals 25-35'); await broker.addQuestion(session.id, 'What design style preference?'); const prompt = await broker.formatElicitationPrompt(session, 'Modern or classic design?'); // Debug: log the prompt to see what's happening // console.log('Generated prompt:', prompt); // Reload session to ensure we have latest data const reloadedSession = await broker.loadSession(session.id); expect(reloadedSession.context.elicitationHistory.length).toBeGreaterThan(0); const promptWithHistory = await broker.formatElicitationPrompt(reloadedSession, 'Modern or classic design?'); expect(promptWithHistory).toContain('BMAD ux-expert - Elicitation'); expect(promptWithHistory).toContain('Previous Context:'); expect(promptWithHistory).toContain('What is the target demographic?'); expect(promptWithHistory).toContain('Young professionals 25-35'); expect(promptWithHistory).toContain('Current Question:'); expect(promptWithHistory).toContain('Modern or classic design?'); }); }); describe('Session Summary', () => { test('should generate session summary', async () => { const session = await broker.createSession('sm', {}); await broker.addQuestion(session.id, 'Sprint length?'); await broker.addResponse(session.id, '2 weeks'); await broker.addQuestion(session.id, 'Team size?'); await broker.addResponse(session.id, '5 developers'); const summary = await broker.getSessionSummary(session.id); expect(summary.id).toBe(session.id); expect(summary.agent).toBe('sm'); expect(summary.status).toBe('active'); expect(summary.questionsAsked).toBe(2); expect(summary.responsesReceived).toBe(2); expect(summary.duration).toBeGreaterThan(0); }); }); describe('Message Processing', () => { test('should process elicitation messages', async () => { const createMessage = { data: { action: 'create', data: { agent: 'po', context: { project: 'Test' } } } }; const session = await broker.processElicitationMessage(createMessage); expect(session.agent).toBe('po'); const questionMessage = { data: { sessionId: session.id, action: 'question', data: { question: 'What are the key features?', metadata: { phase: 'discovery' } } } }; const question = await broker.processElicitationMessage(questionMessage); expect(question.text).toBe('What are the key features?'); expect(question.metadata.phase).toBe('discovery'); }); test('should handle unknown action', async () => { const message = { data: { action: 'unknown-action', data: {} } }; await expect(broker.processElicitationMessage(message)).rejects.toThrow('Unknown elicitation action: unknown-action'); }); }); });