mirror of
https://github.com/proffesor-for-testing/agentic-qe.git
synced 2026-09-19 08:45:47 +08:00
2253d05618
Remove the nested v3/ subdirectory by moving all active source code, tests, configs, and assets directly to the project root. Archive v2/ (dead code, 847MB) to v2-archive.tar.gz. Key changes: - Move v3/src/ → src/, v3/tests/ → tests/, v3/packages/ → packages/ - Move v3/tsconfig.json, v3/vitest.config.ts to root - Merge v3/package.json into root package.json (ESM, scripts, deps, exports) - Merge v3/scripts/ into scripts/, v3/implementation/ into docs/implementation/ - Update all CI workflows to remove "cd v3 &&" and v3/ path prefixes - Fix all init installer path resolution (3-level-up → 2-level-up) - Fix hooks to use "node ./dist/cli/bundle.js" instead of bare "aqe" - Update settings.json ADR/DDD directories to docs/implementation/ - Fix helper scripts (statusline, adr-compliance, ddd-tracker, guidance-hooks) - Update 58 agent definitions: aqe/v3/ → aqe/ memory namespaces - Update 15 guidance shards: v3/src/ → src/ path references - Fix sync interfaces: remove v3/ and v2/ path references - Fix test path calculations (process.cwd() + '..' no longer needed) - Fix flaky perf test threshold (500ms → 1000ms for CI environments) - Fix complexity-analyzer test to match ADR-051 keyword-based eligibility Build passes, 17,901 tests pass, CLI and MCP bundles verified. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
494 lines
15 KiB
TypeScript
494 lines
15 KiB
TypeScript
/**
|
|
* Self-Configurator Tests
|
|
* ADR-025: Enhanced Init with Self-Configuration
|
|
*/
|
|
|
|
import { describe, it, expect } from 'vitest';
|
|
import {
|
|
SelfConfigurator,
|
|
createSelfConfigurator,
|
|
recommendConfig,
|
|
} from '../../../src/init/self-configurator.js';
|
|
import type { ProjectAnalysis } from '../../../src/init/types.js';
|
|
|
|
// Helper to create a base project analysis
|
|
// Note: We set totalCount > 0 to avoid triggering the "no-tests" rule which overwrites domains
|
|
function createBaseAnalysis(overrides: Partial<ProjectAnalysis> = {}): ProjectAnalysis {
|
|
return {
|
|
projectName: 'test-project',
|
|
projectRoot: '/test/project',
|
|
projectType: 'single',
|
|
packageManager: 'npm',
|
|
hasTypeScript: false,
|
|
hasCIConfig: false,
|
|
ciProvider: undefined,
|
|
frameworks: [],
|
|
languages: [],
|
|
existingTests: {
|
|
totalCount: 10, // Non-zero to avoid triggering no-tests rule
|
|
byType: { unit: 8, integration: 2, e2e: 0, other: 0 },
|
|
byFramework: { jest: 10 },
|
|
directories: ['tests'],
|
|
},
|
|
codeComplexity: {
|
|
totalFiles: 50,
|
|
totalLines: 5000,
|
|
averageFileSize: 100,
|
|
recommendation: 'moderate',
|
|
},
|
|
coverage: {
|
|
hasReport: false,
|
|
lines: 0,
|
|
branches: 0,
|
|
functions: 0,
|
|
statements: 0,
|
|
},
|
|
analysisTimestamp: new Date(),
|
|
analysisDurationMs: 100,
|
|
...overrides,
|
|
};
|
|
}
|
|
|
|
describe('SelfConfigurator', () => {
|
|
describe('createSelfConfigurator', () => {
|
|
it('should create a self-configurator', () => {
|
|
const configurator = createSelfConfigurator();
|
|
expect(configurator).toBeInstanceOf(SelfConfigurator);
|
|
});
|
|
|
|
it('should create a minimal configurator', () => {
|
|
const configurator = createSelfConfigurator({ minimal: true });
|
|
expect(configurator).toBeInstanceOf(SelfConfigurator);
|
|
});
|
|
});
|
|
|
|
describe('recommend', () => {
|
|
it('should generate base configuration', () => {
|
|
const analysis = createBaseAnalysis();
|
|
const configurator = createSelfConfigurator();
|
|
const config = configurator.recommend(analysis);
|
|
|
|
expect(config.version).toBe('3.0.0');
|
|
expect(config.project.name).toBe('test-project');
|
|
expect(config.project.root).toBe('/test/project');
|
|
expect(config.project.type).toBe('single');
|
|
});
|
|
|
|
it('should enable learning by default', () => {
|
|
const analysis = createBaseAnalysis();
|
|
const config = recommendConfig(analysis);
|
|
|
|
expect(config.learning.enabled).toBe(true);
|
|
expect(config.learning.hnswConfig).toBeDefined();
|
|
});
|
|
|
|
it('should configure HNSW for large codebases', () => {
|
|
const analysis = createBaseAnalysis({
|
|
codeComplexity: {
|
|
totalFiles: 1500,
|
|
totalLines: 150000,
|
|
averageFileSize: 100,
|
|
recommendation: 'complex',
|
|
},
|
|
});
|
|
|
|
const config = recommendConfig(analysis);
|
|
|
|
expect(config.learning.hnswConfig.M).toBe(32);
|
|
expect(config.learning.hnswConfig.efConstruction).toBe(400);
|
|
expect(config.learning.hnswConfig.efSearch).toBe(200);
|
|
});
|
|
|
|
it('should configure HNSW for small codebases', () => {
|
|
const analysis = createBaseAnalysis({
|
|
codeComplexity: {
|
|
totalFiles: 30,
|
|
totalLines: 3000,
|
|
averageFileSize: 100,
|
|
recommendation: 'simple',
|
|
},
|
|
});
|
|
|
|
const config = recommendConfig(analysis);
|
|
|
|
expect(config.learning.hnswConfig.M).toBe(8);
|
|
expect(config.learning.hnswConfig.efConstruction).toBe(100);
|
|
});
|
|
|
|
it('should use transformer embeddings for TypeScript', () => {
|
|
const analysis = createBaseAnalysis({
|
|
hasTypeScript: true,
|
|
languages: [{ name: 'typescript', percentage: 80, fileCount: 40 }],
|
|
});
|
|
|
|
const config = recommendConfig(analysis);
|
|
|
|
expect(config.learning.embeddingModel).toBe('transformer');
|
|
});
|
|
|
|
it('should use rules routing for simple projects', () => {
|
|
const analysis = createBaseAnalysis({
|
|
codeComplexity: {
|
|
totalFiles: 20,
|
|
totalLines: 2000,
|
|
averageFileSize: 100,
|
|
recommendation: 'simple',
|
|
},
|
|
});
|
|
|
|
const config = recommendConfig(analysis);
|
|
|
|
expect(config.routing.mode).toBe('rules');
|
|
});
|
|
|
|
it('should use ML routing for complex projects', () => {
|
|
const analysis = createBaseAnalysis({
|
|
codeComplexity: {
|
|
totalFiles: 500,
|
|
totalLines: 100000,
|
|
averageFileSize: 200,
|
|
recommendation: 'complex',
|
|
},
|
|
});
|
|
|
|
const config = recommendConfig(analysis);
|
|
|
|
expect(config.routing.mode).toBe('ml');
|
|
});
|
|
|
|
it('should use hybrid routing for moderate projects', () => {
|
|
const analysis = createBaseAnalysis({
|
|
codeComplexity: {
|
|
totalFiles: 100,
|
|
totalLines: 10000,
|
|
averageFileSize: 100,
|
|
recommendation: 'moderate',
|
|
},
|
|
});
|
|
|
|
const config = recommendConfig(analysis);
|
|
|
|
expect(config.routing.mode).toBe('hybrid');
|
|
});
|
|
});
|
|
|
|
describe('Configuration Rules', () => {
|
|
it('should apply TypeScript + Vitest rule', () => {
|
|
const analysis = createBaseAnalysis({
|
|
hasTypeScript: true,
|
|
frameworks: [{ name: 'vitest', version: '1.0.0', confidence: 1.0 }],
|
|
languages: [{ name: 'typescript', percentage: 90, fileCount: 45 }],
|
|
});
|
|
|
|
const config = recommendConfig(analysis);
|
|
|
|
expect(config.learning.embeddingModel).toBe('transformer');
|
|
expect(config.routing.confidenceThreshold).toBe(0.75);
|
|
});
|
|
|
|
it('should apply large codebase rule', () => {
|
|
const analysis = createBaseAnalysis({
|
|
codeComplexity: {
|
|
totalFiles: 800,
|
|
totalLines: 80000,
|
|
averageFileSize: 100,
|
|
recommendation: 'complex',
|
|
},
|
|
});
|
|
|
|
const config = recommendConfig(analysis);
|
|
|
|
expect(config.agents.maxConcurrent).toBe(15);
|
|
});
|
|
|
|
it('should apply small project rule', () => {
|
|
const analysis = createBaseAnalysis({
|
|
codeComplexity: {
|
|
totalFiles: 25,
|
|
totalLines: 2500,
|
|
averageFileSize: 100,
|
|
recommendation: 'simple',
|
|
},
|
|
});
|
|
|
|
const config = recommendConfig(analysis);
|
|
|
|
expect(config.agents.maxConcurrent).toBe(5);
|
|
expect(config.workers.maxConcurrent).toBe(2);
|
|
});
|
|
|
|
it('should apply low coverage rule', () => {
|
|
const analysis = createBaseAnalysis({
|
|
coverage: {
|
|
hasReport: true,
|
|
lines: 30,
|
|
branches: 20,
|
|
functions: 40,
|
|
statements: 30,
|
|
},
|
|
});
|
|
|
|
const config = recommendConfig(analysis);
|
|
|
|
expect(config.workers.enabled).toContain('coverage-gap-scanner');
|
|
});
|
|
|
|
it('should apply monorepo rule', () => {
|
|
const analysis = createBaseAnalysis({
|
|
projectType: 'monorepo',
|
|
});
|
|
|
|
const config = recommendConfig(analysis);
|
|
|
|
expect(config.agents.maxConcurrent).toBe(15);
|
|
expect(config.workers.maxConcurrent).toBe(6);
|
|
expect(config.domains.enabled).toContain('code-intelligence');
|
|
});
|
|
|
|
it('should apply E2E framework rule', () => {
|
|
const analysis = createBaseAnalysis({
|
|
frameworks: [{ name: 'playwright', version: '1.40.0', confidence: 1.0 }],
|
|
});
|
|
|
|
const config = recommendConfig(analysis);
|
|
|
|
expect(config.domains.enabled).toContain('visual-accessibility');
|
|
expect(config.domains.enabled).toContain('chaos-resilience');
|
|
});
|
|
|
|
it('should apply CI integration rule', () => {
|
|
const analysis = createBaseAnalysis({
|
|
hasCIConfig: true,
|
|
ciProvider: 'github-actions',
|
|
});
|
|
|
|
const config = recommendConfig(analysis);
|
|
|
|
expect(config.hooks.ciIntegration).toBe(true);
|
|
expect(config.hooks.claudeCode).toBe(true);
|
|
expect(config.domains.enabled).toContain('quality-assessment');
|
|
});
|
|
|
|
it('should apply no-tests rule', () => {
|
|
const analysis = createBaseAnalysis({
|
|
existingTests: {
|
|
totalCount: 0, // Explicitly zero to trigger no-tests rule
|
|
byType: { unit: 0, integration: 0, e2e: 0, other: 0 },
|
|
byFramework: {},
|
|
directories: [],
|
|
},
|
|
});
|
|
|
|
const config = recommendConfig(analysis);
|
|
|
|
expect(config.learning.qualityThreshold).toBe(0.5);
|
|
expect(config.learning.promotionThreshold).toBe(2);
|
|
expect(config.domains.enabled).toContain('test-generation');
|
|
// No-tests rule focuses on test generation domains only
|
|
expect(config.domains.enabled).toContain('coverage-analysis');
|
|
expect(config.domains.enabled).toContain('learning-optimization');
|
|
});
|
|
|
|
it('should apply many-tests rule', () => {
|
|
const analysis = createBaseAnalysis({
|
|
existingTests: {
|
|
totalCount: 600,
|
|
byType: { unit: 400, integration: 150, e2e: 50, other: 0 },
|
|
byFramework: { jest: 600 },
|
|
directories: ['tests'],
|
|
},
|
|
});
|
|
|
|
const config = recommendConfig(analysis);
|
|
|
|
expect(config.workers.enabled).toContain('flaky-test-detector');
|
|
expect(config.domains.enabled).toContain('test-execution');
|
|
});
|
|
|
|
it('should apply Python project rule', () => {
|
|
const analysis = createBaseAnalysis({
|
|
languages: [{ name: 'python', percentage: 80, fileCount: 40 }],
|
|
});
|
|
|
|
const config = recommendConfig(analysis);
|
|
|
|
expect(config.routing.mode).toBe('hybrid');
|
|
expect(config.domains.enabled).toContain('security-compliance');
|
|
});
|
|
|
|
it('should apply Java project rule', () => {
|
|
const analysis = createBaseAnalysis({
|
|
languages: [{ name: 'java', percentage: 70, fileCount: 35 }],
|
|
});
|
|
|
|
const config = recommendConfig(analysis);
|
|
|
|
expect(config.agents.defaultTimeout).toBe(120000);
|
|
});
|
|
});
|
|
|
|
describe('getApplicableRules', () => {
|
|
it('should return list of applicable rules', () => {
|
|
const analysis = createBaseAnalysis({
|
|
hasTypeScript: true,
|
|
frameworks: [{ name: 'vitest', version: '1.0.0', confidence: 1.0 }],
|
|
hasCIConfig: true,
|
|
ciProvider: 'github-actions',
|
|
});
|
|
|
|
const configurator = createSelfConfigurator();
|
|
const rules = configurator.getApplicableRules(analysis);
|
|
|
|
expect(rules).toContain('typescript-vitest');
|
|
expect(rules).toContain('has-ci');
|
|
expect(rules).toContain('github-actions');
|
|
});
|
|
|
|
it('should return empty array for minimal analysis', () => {
|
|
const analysis = createBaseAnalysis();
|
|
const configurator = createSelfConfigurator({ minimal: true });
|
|
const rules = configurator.getApplicableRules(analysis);
|
|
|
|
// Minimal configurator only includes typescript-vitest, large-codebase, small-project
|
|
// Base analysis doesn't match any of these
|
|
expect(Array.isArray(rules)).toBe(true);
|
|
});
|
|
});
|
|
|
|
describe('Domain Recommendations', () => {
|
|
it('should always include core domains', () => {
|
|
const analysis = createBaseAnalysis();
|
|
const config = recommendConfig(analysis);
|
|
|
|
expect(config.domains.enabled).toContain('test-generation');
|
|
expect(config.domains.enabled).toContain('test-execution');
|
|
expect(config.domains.enabled).toContain('coverage-analysis');
|
|
expect(config.domains.enabled).toContain('learning-optimization');
|
|
});
|
|
|
|
it('should add security for larger projects', () => {
|
|
const analysis = createBaseAnalysis({
|
|
codeComplexity: {
|
|
totalFiles: 200,
|
|
totalLines: 20000,
|
|
averageFileSize: 100,
|
|
recommendation: 'moderate',
|
|
},
|
|
});
|
|
|
|
const config = recommendConfig(analysis);
|
|
|
|
expect(config.domains.enabled).toContain('security-compliance');
|
|
});
|
|
|
|
it('should add code intelligence for TypeScript projects', () => {
|
|
const analysis = createBaseAnalysis({
|
|
hasTypeScript: true,
|
|
});
|
|
|
|
const config = recommendConfig(analysis);
|
|
|
|
expect(config.domains.enabled).toContain('code-intelligence');
|
|
});
|
|
|
|
it('should deduplicate domain list', () => {
|
|
const analysis = createBaseAnalysis({
|
|
hasTypeScript: true,
|
|
hasCIConfig: true,
|
|
codeComplexity: {
|
|
totalFiles: 200,
|
|
totalLines: 20000,
|
|
averageFileSize: 100,
|
|
recommendation: 'complex',
|
|
},
|
|
});
|
|
|
|
const config = recommendConfig(analysis);
|
|
|
|
// Check no duplicates
|
|
const uniqueDomains = [...new Set(config.domains.enabled)];
|
|
expect(config.domains.enabled.length).toBe(uniqueDomains.length);
|
|
});
|
|
});
|
|
|
|
describe('Worker Recommendations', () => {
|
|
it('should always include core workers', () => {
|
|
const analysis = createBaseAnalysis();
|
|
const config = recommendConfig(analysis);
|
|
|
|
expect(config.workers.enabled).toContain('pattern-consolidator');
|
|
expect(config.workers.enabled).toContain('routing-accuracy-monitor');
|
|
});
|
|
|
|
it('should add coverage scanner for missing coverage', () => {
|
|
const analysis = createBaseAnalysis({
|
|
coverage: {
|
|
hasReport: false,
|
|
lines: 0,
|
|
branches: 0,
|
|
functions: 0,
|
|
statements: 0,
|
|
},
|
|
});
|
|
|
|
const config = recommendConfig(analysis);
|
|
|
|
expect(config.workers.enabled).toContain('coverage-gap-scanner');
|
|
});
|
|
|
|
it('should add flaky detector for many tests', () => {
|
|
const analysis = createBaseAnalysis({
|
|
existingTests: {
|
|
totalCount: 200,
|
|
byType: { unit: 150, integration: 40, e2e: 10, other: 0 },
|
|
byFramework: { jest: 200 },
|
|
directories: ['tests'],
|
|
},
|
|
});
|
|
|
|
const config = recommendConfig(analysis);
|
|
|
|
expect(config.workers.enabled).toContain('flaky-test-detector');
|
|
});
|
|
});
|
|
|
|
describe('Agent Recommendations', () => {
|
|
it('should recommend 15 agents for monorepos', () => {
|
|
const analysis = createBaseAnalysis({ projectType: 'monorepo' });
|
|
const config = recommendConfig(analysis);
|
|
|
|
expect(config.agents.maxConcurrent).toBe(15);
|
|
});
|
|
|
|
it('should recommend 12 agents for large codebases', () => {
|
|
const analysis = createBaseAnalysis({
|
|
codeComplexity: {
|
|
totalFiles: 600,
|
|
totalLines: 60000,
|
|
averageFileSize: 100,
|
|
recommendation: 'complex',
|
|
},
|
|
});
|
|
|
|
// Note: large-codebase rule (>500 files) sets it to 15
|
|
const config = recommendConfig(analysis);
|
|
expect(config.agents.maxConcurrent).toBe(15);
|
|
});
|
|
|
|
it('should recommend 5 agents for small projects', () => {
|
|
const analysis = createBaseAnalysis({
|
|
codeComplexity: {
|
|
totalFiles: 30,
|
|
totalLines: 3000,
|
|
averageFileSize: 100,
|
|
recommendation: 'simple',
|
|
},
|
|
});
|
|
|
|
const config = recommendConfig(analysis);
|
|
|
|
expect(config.agents.maxConcurrent).toBe(5);
|
|
});
|
|
});
|
|
});
|