UNPKG

@iservu-inc/adf-cli

Version:

CLI tool for AgentDevFramework - AI-assisted development framework with multi-provider AI support

206 lines (164 loc) 8.42 kB
const fs = require('fs-extra'); const path = require('path'); const ProgressTracker = require('../lib/frameworks/progress-tracker'); const TEST_SESSION_PATH = path.join(__dirname, 'test-session'); describe('ProgressTracker', () => { beforeEach(async () => { // Clean up test session directory await fs.remove(TEST_SESSION_PATH); await fs.ensureDir(TEST_SESSION_PATH); }); afterEach(async () => { // Clean up after tests await fs.remove(TEST_SESSION_PATH); }); describe('initialize', () => { it('should create new progress file for new session', async () => { const tracker = new ProgressTracker(TEST_SESSION_PATH, 5, 'rapid'); const isResumable = await tracker.initialize(); expect(isResumable).toBe(false); expect(await fs.pathExists(path.join(TEST_SESSION_PATH, '_progress.json'))).toBe(true); expect(await fs.pathExists(path.join(TEST_SESSION_PATH, '_progress-log.md'))).toBe(true); }); it('should load existing progress for resumable session', async () => { // Create initial session const tracker1 = new ProgressTracker(TEST_SESSION_PATH, 5, 'rapid'); await tracker1.initialize(); await tracker1.startBlock(1, 'Test Block'); // Resume session const tracker2 = new ProgressTracker(TEST_SESSION_PATH, 5, 'rapid'); const isResumable = await tracker2.initialize(); expect(isResumable).toBe(true); expect(tracker2.getProgress().currentBlock).toBe(1); }); }); describe('answerQuestion', () => { it('should save answer with quality metrics', async () => { const tracker = new ProgressTracker(TEST_SESSION_PATH, 5, 'rapid'); await tracker.initialize(); const qualityMetrics = { wordCount: 50, qualityScore: 85, isComprehensive: true, hasKeywords: { matched: ['react', 'web'], count: 2 }, hasRequiredElements: { detected: ['platform'], count: 1 } }; await tracker.answerQuestion('q1', 'What are you building?', 'A React web app', qualityMetrics); const progress = tracker.getProgress(); expect(progress.answers['q1']).toBeDefined(); expect(progress.answers['q1'].text).toBe('A React web app'); expect(progress.answers['q1'].quality.qualityScore).toBe(85); expect(progress.totalWordCount).toBe(50); expect(progress.comprehensiveAnswers).toBe(1); }); it('should calculate average answer quality correctly', async () => { const tracker = new ProgressTracker(TEST_SESSION_PATH, 5, 'rapid'); await tracker.initialize(); await tracker.answerQuestion('q1', 'Q1', 'Answer 1', { qualityScore: 80, wordCount: 20, isComprehensive: true }); await tracker.answerQuestion('q2', 'Q2', 'Answer 2', { qualityScore: 90, wordCount: 30, isComprehensive: true }); await tracker.answerQuestion('q3', 'Q3', 'Answer 3', { qualityScore: 70, wordCount: 25, isComprehensive: true }); const progress = tracker.getProgress(); expect(progress.averageAnswerQuality).toBe(80); // (80+90+70)/3 = 80 }); it('should calculate information richness based on quality and completion', async () => { const tracker = new ProgressTracker(TEST_SESSION_PATH, 5, 'rapid'); await tracker.initialize(); // Complete 2 of 5 blocks (40% completion) await tracker.completeBlock(1, 'Block 1', 3); await tracker.completeBlock(2, 'Block 2', 2); // Add high-quality answers (90% quality) await tracker.answerQuestion('q1', 'Q1', 'A1', { qualityScore: 90, wordCount: 50, isComprehensive: true }); await tracker.answerQuestion('q2', 'Q2', 'A2', { qualityScore: 90, wordCount: 50, isComprehensive: true }); const progress = tracker.getProgress(); // informationRichness = (0.4 * completionFactor) + (0.6 * qualityFactor) // = (0.4 * 0.4) + (0.6 * 0.9) = 0.16 + 0.54 = 0.70 = 70% expect(progress.informationRichness).toBeCloseTo(70, 0); }); }); describe('saveWithBackup', () => { it('should create triple-redundant saves', async () => { const tracker = new ProgressTracker(TEST_SESSION_PATH, 5, 'rapid'); await tracker.initialize(); await tracker.answerQuestion('q1', 'Q1', 'Answer', { qualityScore: 75, wordCount: 20, isComprehensive: true }); // Check all three save locations exist expect(await fs.pathExists(path.join(TEST_SESSION_PATH, '_progress.json'))).toBe(true); expect(await fs.pathExists(path.join(TEST_SESSION_PATH, '_progress.backup.json'))).toBe(true); expect(await fs.pathExists(path.join(TEST_SESSION_PATH, '_progress-log.md'))).toBe(true); // Verify main and backup have same content const main = await fs.readJson(path.join(TEST_SESSION_PATH, '_progress.json')); const backup = await fs.readJson(path.join(TEST_SESSION_PATH, '_progress.backup.json')); expect(main).toEqual(backup); }); it('should create emergency save on error', async () => { const tracker = new ProgressTracker(TEST_SESSION_PATH, 5, 'rapid'); await tracker.initialize(); // Make main file read-only to simulate save error const mainFile = path.join(TEST_SESSION_PATH, '_progress.json'); await fs.chmod(mainFile, 0o444); try { await tracker.answerQuestion('q1', 'Q1', 'Answer', { qualityScore: 75, wordCount: 20, isComprehensive: true }); } catch (error) { // May throw, but should still create emergency file } // Check for emergency file const files = await fs.readdir(TEST_SESSION_PATH); const emergencyFiles = files.filter(f => f.startsWith('_emergency-')); // Restore permissions for cleanup await fs.chmod(mainFile, 0o666); // Emergency file should exist if save failed expect(emergencyFiles.length).toBeGreaterThan(0); }); }); describe('block tracking', () => { it('should track block start, complete, and skip', async () => { const tracker = new ProgressTracker(TEST_SESSION_PATH, 5, 'rapid'); await tracker.initialize(); await tracker.startBlock(1, 'Block 1'); await tracker.completeBlock(1, 'Block 1', 3); await tracker.skipBlock(2, 'Block 2'); const progress = tracker.getProgress(); expect(progress.currentBlock).toBe(1); expect(progress.completedBlocks.length).toBe(1); expect(progress.completedBlocks[0].number).toBe(1); expect(progress.completedBlocks[0].questionsAnswered).toBe(3); expect(progress.skippedBlocks.length).toBe(1); expect(progress.skippedBlocks[0].number).toBe(2); }); }); describe('complete', () => { it('should mark session as completed', async () => { const tracker = new ProgressTracker(TEST_SESSION_PATH, 5, 'rapid'); await tracker.initialize(); await tracker.complete(); const progress = tracker.getProgress(); expect(progress.status).toBe('completed'); expect(progress.completedAt).toBeDefined(); expect(progress.canResume).toBe(false); }); }); describe('canResume', () => { it('should return true for in-progress sessions', async () => { const tracker = new ProgressTracker(TEST_SESSION_PATH, 5, 'rapid'); await tracker.initialize(); expect(tracker.canResume()).toBe(true); }); it('should return false for completed sessions', async () => { const tracker = new ProgressTracker(TEST_SESSION_PATH, 5, 'rapid'); await tracker.initialize(); await tracker.complete(); expect(tracker.canResume()).toBe(false); }); }); describe('getResumeInfo', () => { it('should return resume information', async () => { const tracker = new ProgressTracker(TEST_SESSION_PATH, 5, 'rapid'); await tracker.initialize(); await tracker.completeBlock(1, 'Block 1', 3); // This adds 3 to totalQuestionsAnswered await tracker.answerQuestion('q1', 'Q1', 'A1', { qualityScore: 80, wordCount: 20, isComprehensive: true }); const info = tracker.getResumeInfo(); expect(info.completedBlocks).toBe(1); expect(info.totalBlocks).toBe(5); expect(info.totalQuestionsAnswered).toBe(3); // completeBlock added 3 }); }); });