@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
JavaScript
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);
});
});
});