Files
Profa 2253d05618 feat: flatten project structure — archive v2, promote v3 to root
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>
2026-03-01 11:35:41 +00:00

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