UNPKG

@iservu-inc/adf-cli

Version:

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

323 lines (255 loc) 9.88 kB
const fs = require('fs-extra'); const path = require('path'); const os = require('os'); const KnowledgeGraph = require('../lib/analysis/knowledge-graph'); describe('KnowledgeGraph', () => { let tempDir; let kg; beforeEach(async () => { tempDir = await fs.mkdtemp(path.join(os.tmpdir(), 'kg-test-')); kg = new KnowledgeGraph(tempDir); }); afterEach(async () => { await fs.remove(tempDir); }); describe('add', () => { it('should add new information', () => { const extractedInfo = [ { type: 'tech_stack', content: 'React and Node.js', confidence: 90, source: 'tech-question' } ]; kg.add(extractedInfo); expect(kg.has('tech_stack', 70)).toBe(true); expect(kg.getConfidence('tech_stack')).toBe(90); }); it('should update existing information with higher confidence', () => { const info1 = [ { type: 'tech_stack', content: 'React for frontend development', confidence: 70, source: 'q1' } ]; const info2 = [ { type: 'tech_stack', content: 'React for the frontend', confidence: 90, source: 'q2' } ]; kg.add(info1); kg.add(info2); expect(kg.getConfidence('tech_stack')).toBe(90); const items = kg.get('tech_stack'); // Should have merged or both sources tracked const allSources = items.flatMap(i => i.sources); expect(allSources).toContain('q1'); expect(allSources).toContain('q2'); }); it('should not replace existing information with lower confidence', () => { const info1 = [ { type: 'tech_stack', content: 'React and Node', confidence: 90, source: 'q1' } ]; const info2 = [ { type: 'tech_stack', content: 'React', confidence: 70, source: 'q2' } ]; kg.add(info1); kg.add(info2); expect(kg.getConfidence('tech_stack')).toBe(90); const items = kg.get('tech_stack'); expect(items[0].content).toContain('Node'); }); it('should add dissimilar information as separate items', () => { const info1 = [ { type: 'features', content: 'User authentication', confidence: 85, source: 'q1' } ]; const info2 = [ { type: 'features', content: 'Data export to CSV', confidence: 80, source: 'q2' } ]; kg.add(info1); kg.add(info2); const items = kg.get('features'); expect(items).toHaveLength(2); }); it('should merge similar information', () => { const info1 = [ { type: 'tech_stack', content: 'React for frontend', confidence: 85, source: 'q1' } ]; const info2 = [ { type: 'tech_stack', content: 'React for the frontend', confidence: 80, source: 'q2' } ]; kg.add(info1); kg.add(info2); const items = kg.get('tech_stack'); // Should merge because very similar expect(items.length).toBeLessThanOrEqual(2); }); }); describe('has', () => { it('should return true if information exists with sufficient confidence', () => { const info = [ { type: 'project_goal', content: 'Build a CRM', confidence: 85, source: 'q1' } ]; kg.add(info); expect(kg.has('project_goal', 80)).toBe(true); expect(kg.has('project_goal', 90)).toBe(false); }); it('should return false for missing information types', () => { expect(kg.has('tech_stack')).toBe(false); }); it('should use default minConfidence of 70', () => { const info = [ { type: 'platform', content: 'Web', confidence: 75, source: 'q1' } ]; kg.add(info); expect(kg.has('platform')).toBe(true); }); }); describe('get', () => { it('should return all items of a type', () => { const info = [ { type: 'features', content: 'Auth', confidence: 85, source: 'q1' }, { type: 'features', content: 'Export', confidence: 80, source: 'q2' } ]; kg.add(info); const items = kg.get('features'); expect(items).toHaveLength(2); }); it('should return empty array for non-existent type', () => { const items = kg.get('nonexistent'); expect(items).toEqual([]); }); }); describe('getConfidence', () => { it('should return highest confidence for a type', () => { const info = [ { type: 'tech_stack', content: 'React', confidence: 80, source: 'q1' }, { type: 'tech_stack', content: 'Node', confidence: 90, source: 'q2' } ]; kg.add(info); expect(kg.getConfidence('tech_stack')).toBe(90); }); it('should return 0 for non-existent type', () => { expect(kg.getConfidence('nonexistent')).toBe(0); }); }); describe('getSummary', () => { it('should return summary of all knowledge', () => { const info = [ { type: 'tech_stack', content: 'React', confidence: 90, source: 'q1' }, { type: 'architecture', content: 'SPA', confidence: 85, source: 'q2' }, { type: 'platform', content: 'Web', confidence: 80, source: 'q3' } ]; kg.add(info); const summary = kg.getSummary(); expect(summary.tech_stack.maxConfidence).toBe(90); expect(summary.architecture.maxConfidence).toBe(85); expect(summary.platform.maxConfidence).toBe(80); expect(summary.tech_stack.sources).toContain('q1'); }); }); describe('getDisplaySummary', () => { it('should return displayable summary with icons', () => { const info = [ { type: 'tech_stack', content: 'React', confidence: 90, source: 'q1' }, { type: 'project_goal', content: 'Build CRM', confidence: 95, source: 'q2' } ]; kg.add(info); const display = kg.getDisplaySummary(); expect(display.length).toBe(2); expect(display[0].confidence).toBe(95); // Sorted by confidence expect(display[0].icon).toBe('🎯'); expect(display[1].icon).toBe('🔧'); }); it('should filter out low confidence items', () => { const info = [ { type: 'tech_stack', content: 'React', confidence: 90, source: 'q1' }, { type: 'timeline', content: 'Soon', confidence: 50, source: 'q2' } ]; kg.add(info); const display = kg.getDisplaySummary(); // Should only include tech_stack (90% > 60%) expect(display).toHaveLength(1); expect(display[0].type).toBe('tech stack'); }); }); describe('calculateSimilarity', () => { it('should calculate high similarity for similar texts', () => { const similarity = kg.calculateSimilarity( 'React for frontend development', 'React for the frontend' ); expect(similarity).toBeGreaterThan(0.5); }); it('should calculate low similarity for different texts', () => { const similarity = kg.calculateSimilarity( 'React for frontend', 'PostgreSQL database' ); expect(similarity).toBeLessThan(0.5); }); it('should return 1 for identical texts', () => { const similarity = kg.calculateSimilarity( 'same text', 'same text' ); expect(similarity).toBe(1); }); }); describe('save and load', () => { it('should save knowledge graph to disk', async () => { const info = [ { type: 'tech_stack', content: 'React', confidence: 90, source: 'q1' } ]; kg.add(info); await kg.save(); const filePath = path.join(tempDir, '_knowledge_graph.json'); expect(await fs.pathExists(filePath)).toBe(true); const data = await fs.readJson(filePath); expect(data.knowledge).toBeDefined(); expect(data.knowledge.length).toBeGreaterThan(0); }); it('should load knowledge graph from disk', async () => { const info = [ { type: 'tech_stack', content: 'React', confidence: 90, source: 'q1' }, { type: 'platform', content: 'Web', confidence: 85, source: 'q2' } ]; kg.add(info); await kg.save(); // Create new instance and load const kg2 = new KnowledgeGraph(tempDir); const loaded = await kg2.load(); expect(loaded).toBe(true); expect(kg2.has('tech_stack')).toBe(true); expect(kg2.has('platform')).toBe(true); expect(kg2.getConfidence('tech_stack')).toBe(90); }); it('should return false when loading from non-existent file', async () => { const loaded = await kg.load(); expect(loaded).toBe(false); }); }); describe('getStats', () => { it('should return statistics about knowledge graph', () => { const info = [ { type: 'tech_stack', content: 'React', confidence: 90, source: 'q1' }, { type: 'tech_stack', content: 'Node', confidence: 85, source: 'q2' }, { type: 'platform', content: 'Web', confidence: 75, source: 'q3' }, { type: 'architecture', content: 'SPA', confidence: 80, source: 'q4' } ]; kg.add(info); const stats = kg.getStats(); expect(stats.totalItems).toBe(4); expect(stats.highConfidenceItems).toBe(3); // 90, 85, 80 are >= 80 expect(stats.types).toBe(3); // tech_stack, platform, architecture expect(stats.typeList).toContain('tech_stack'); }); }); describe('clear', () => { it('should clear all knowledge', () => { const info = [ { type: 'tech_stack', content: 'React', confidence: 90, source: 'q1' } ]; kg.add(info); expect(kg.has('tech_stack')).toBe(true); kg.clear(); expect(kg.has('tech_stack')).toBe(false); }); }); });