@iservu-inc/adf-cli
Version:
CLI tool for AgentDevFramework - AI-assisted development framework with multi-provider AI support
298 lines (263 loc) • 9.96 kB
JavaScript
const fs = require('fs-extra');
const path = require('path');
const { PatternDetector, detectPatterns, getPatternSummary } = require('../lib/learning/pattern-detector');
describe('Pattern Detector', () => {
const tempDir = path.join(__dirname, 'temp-pattern-test');
beforeEach(async () => {
await fs.ensureDir(tempDir);
});
afterEach(async () => {
await fs.remove(tempDir);
});
describe('PatternDetector', () => {
test('should detect consistent skip patterns', () => {
const skipHistory = {
sessions: [
{
sessionId: 's1',
skips: [
{ questionId: 'q1', text: 'Deploy where?', category: 'deployment', reason: 'manual' }
]
},
{
sessionId: 's2',
skips: [
{ questionId: 'q1', text: 'Deploy where?', category: 'deployment', reason: 'manual' }
]
},
{
sessionId: 's3',
skips: [
{ questionId: 'q1', text: 'Deploy where?', category: 'deployment', reason: 'manual' }
]
}
]
};
const detector = new PatternDetector(skipHistory, {}, { minSessionsForPattern: 3 });
const patterns = detector.detectConsistentSkips();
expect(patterns.length).toBeGreaterThan(0);
expect(patterns[0].questionId).toBe('q1');
expect(patterns[0].confidence).toBe(100); // 3/3 sessions
expect(patterns[0].type).toBe('consistent_skip');
});
test('should not detect patterns with insufficient sessions', () => {
const skipHistory = {
sessions: [
{
sessionId: 's1',
skips: [
{ questionId: 'q1', text: 'Q1', reason: 'manual' }
]
},
{
sessionId: 's2',
skips: [
{ questionId: 'q1', text: 'Q1', reason: 'manual' }
]
}
]
};
const detector = new PatternDetector(skipHistory, {}, { minSessionsForPattern: 3 });
const patterns = detector.detectConsistentSkips();
expect(patterns.length).toBe(0); // Not enough sessions
});
test('should detect category skip patterns', () => {
const skipHistory = {
sessions: [
{
sessionId: 's1',
skips: [
{ questionId: 'q1', category: 'deployment', reason: 'manual' },
{ questionId: 'q2', category: 'deployment', reason: 'manual' },
{ questionId: 'q3', category: 'deployment', reason: 'manual' },
{ questionId: 'q4', category: 'deployment', reason: 'manual' },
{ questionId: 'q5', category: 'deployment', reason: 'manual' }
]
}
]
};
const detector = new PatternDetector(skipHistory, {});
const patterns = detector.detectCategoryPatterns();
expect(patterns.length).toBeGreaterThan(0);
expect(patterns[0].category).toBe('deployment');
expect(patterns[0].type).toBe('category_skip');
expect(patterns[0].confidence).toBe(100); // 5/5 questions skipped
});
test('should detect framework-specific skip patterns', () => {
const skipHistory = {
sessions: [
{
sessionId: 's1',
frameworks: ['Next.js'],
skips: [
{ questionId: 'q_routing', text: 'Routing setup?', category: 'frontend', reason: 'manual' }
]
},
{
sessionId: 's2',
frameworks: ['Next.js'],
skips: [
{ questionId: 'q_routing', text: 'Routing setup?', category: 'frontend', reason: 'manual' }
]
},
{
sessionId: 's3',
frameworks: ['Next.js'],
skips: [
{ questionId: 'q_routing', text: 'Routing setup?', category: 'frontend', reason: 'manual' }
]
}
]
};
const detector = new PatternDetector(skipHistory, {}, { minSessionsForPattern: 3 });
const patterns = detector.detectFrameworkPatterns();
expect(patterns.length).toBeGreaterThan(0);
expect(patterns[0].framework).toBe('Next.js');
expect(patterns[0].questionId).toBe('q_routing');
expect(patterns[0].confidence).toBe(100); // 3/3 Next.js sessions
});
test('should detect user preference patterns', () => {
const skipHistory = { sessions: [] };
const answerHistory = {
sessions: [
{
sessionId: 's1',
answers: [
{ questionId: 'q1', category: 'testing', wordCount: 15 },
{ questionId: 'q2', category: 'testing', wordCount: 18 },
{ questionId: 'q3', category: 'testing', wordCount: 20 }
]
}
]
};
const detector = new PatternDetector(skipHistory, answerHistory);
const patterns = detector.detectUserPreferences();
expect(patterns.length).toBeGreaterThan(0);
expect(patterns[0].category).toBe('testing');
expect(patterns[0].preference).toBe('brief_answers');
expect(patterns[0].avgWordCount).toBeLessThan(30);
});
test('should detect detailed answer preferences', () => {
const skipHistory = { sessions: [] };
const answerHistory = {
sessions: [
{
sessionId: 's1',
answers: [
{ questionId: 'q1', category: 'architecture', wordCount: 120 },
{ questionId: 'q2', category: 'architecture', wordCount: 150 },
{ questionId: 'q3', category: 'architecture', wordCount: 110 }
]
}
]
};
const detector = new PatternDetector(skipHistory, answerHistory);
const patterns = detector.detectUserPreferences();
expect(patterns.length).toBeGreaterThan(0);
expect(patterns[0].category).toBe('architecture');
expect(patterns[0].preference).toBe('detailed_answers');
expect(patterns[0].avgWordCount).toBeGreaterThan(100);
});
test('should aggregate all pattern types', () => {
const skipHistory = {
sessions: [
{
sessionId: 's1',
skips: [
{ questionId: 'q1', category: 'deployment', reason: 'manual' }
]
},
{
sessionId: 's2',
skips: [
{ questionId: 'q1', category: 'deployment', reason: 'manual' }
]
},
{
sessionId: 's3',
skips: [
{ questionId: 'q1', category: 'deployment', reason: 'manual' }
]
}
]
};
const detector = new PatternDetector(skipHistory, {}, { minSessionsForPattern: 3 });
const allPatterns = detector.detectAllPatterns();
expect(allPatterns).toHaveProperty('consistentSkips');
expect(allPatterns).toHaveProperty('categoryPatterns');
expect(allPatterns).toHaveProperty('frameworkPatterns');
expect(allPatterns).toHaveProperty('userPreferences');
expect(allPatterns.consistentSkips.length).toBeGreaterThan(0);
});
});
describe('detectPatterns', () => {
test('should detect patterns from project files', async () => {
const skipHistory = {
version: '1.0',
sessions: [
{
sessionId: 's1',
skips: [
{ questionId: 'q1', category: 'deployment', reason: 'manual' }
]
},
{
sessionId: 's2',
skips: [
{ questionId: 'q1', category: 'deployment', reason: 'manual' }
]
},
{
sessionId: 's3',
skips: [
{ questionId: 'q1', category: 'deployment', reason: 'manual' }
]
}
]
};
await fs.ensureDir(path.join(tempDir, '.adf', 'learning'));
await fs.writeJSON(path.join(tempDir, '.adf', 'learning', 'skip-history.json'), skipHistory);
await fs.writeJSON(path.join(tempDir, '.adf', 'learning', 'answer-history.json'), { version: '1.0', sessions: [] });
await fs.writeJSON(path.join(tempDir, '.adf', 'learning', 'config.json'), { minSessionsForPattern: 3 });
const patterns = await detectPatterns(tempDir);
expect(patterns).toHaveProperty('consistentSkips');
expect(patterns.consistentSkips.length).toBeGreaterThan(0);
});
});
describe('getPatternSummary', () => {
test('should summarize patterns by confidence level', () => {
const patterns = {
consistentSkips: [
{ confidence: 90 },
{ confidence: 70 },
{ confidence: 50 }
],
categoryPatterns: [
{ confidence: 85 }
],
frameworkPatterns: [],
userPreferences: []
};
const summary = getPatternSummary(patterns);
expect(summary.total).toBe(4);
expect(summary.highConfidence).toBe(2); // 90, 85
expect(summary.mediumConfidence).toBe(1); // 70
expect(summary.lowConfidence).toBe(1); // 50
expect(summary.byType.consistentSkips).toBe(3);
expect(summary.byType.categoryPatterns).toBe(1);
});
test('should handle empty patterns', () => {
const patterns = {
consistentSkips: [],
categoryPatterns: [],
frameworkPatterns: [],
userPreferences: []
};
const summary = getPatternSummary(patterns);
expect(summary.total).toBe(0);
expect(summary.highConfidence).toBe(0);
expect(summary.mediumConfidence).toBe(0);
expect(summary.lowConfidence).toBe(0);
});
});
});