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

204 lines
6.6 KiB
TypeScript

/**
* Test: LearningPhase (Phase 05)
* Tests learning system initialization, HNSW setup, and pattern loading.
*/
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
import { LearningPhase } from '../../../../src/init/phases/05-learning.js';
import type { InitContext } from '../../../../src/init/phases/phase-interface.js';
import type { AQEInitConfig } from '../../../../src/init/types.js';
// Mock fs
vi.mock('fs', async () => {
const actual = await vi.importActual('fs');
return {
...actual,
existsSync: vi.fn().mockReturnValue(false),
mkdirSync: vi.fn(),
writeFileSync: vi.fn(),
};
});
import { existsSync, mkdirSync, writeFileSync } from 'fs';
function createMockContext(
learningConfig: Partial<AQEInitConfig['learning']> = {},
optionsOverrides: Partial<InitContext['options']> = {},
): InitContext {
return {
projectRoot: '/tmp/test-learning',
options: {
skipPatterns: false,
...optionsOverrides,
},
config: {
learning: {
enabled: true,
embeddingModel: 'transformer',
hnswConfig: { M: 16, efConstruction: 200 },
qualityThreshold: 0.7,
promotionThreshold: 0.85,
pretrainedPatterns: true,
...learningConfig,
},
} as AQEInitConfig,
enhancements: { claudeFlow: false, ruvector: false },
results: new Map(),
services: {
log: vi.fn(),
warn: vi.fn(),
error: vi.fn(),
},
};
}
describe('LearningPhase', () => {
let phase: LearningPhase;
beforeEach(() => {
vi.clearAllMocks();
phase = new LearningPhase();
vi.mocked(existsSync).mockReturnValue(false);
vi.mocked(mkdirSync).mockReturnValue(undefined as any);
vi.mocked(writeFileSync).mockReturnValue(undefined);
});
afterEach(() => {
vi.restoreAllMocks();
});
describe('phase metadata', () => {
it('should have name "learning"', () => {
expect(phase.name).toBe('learning');
});
it('should have order 50', () => {
expect(phase.order).toBe(50);
});
it('should not be critical', () => {
expect(phase.critical).toBe(false);
});
it('should require database and configuration phases', () => {
expect(phase.requiresPhases).toContain('database');
expect(phase.requiresPhases).toContain('configuration');
});
});
describe('shouldRun', () => {
it('should return true when learning is enabled', async () => {
const context = createMockContext({ enabled: true });
expect(await phase.shouldRun(context)).toBe(true);
});
it('should return false when learning is disabled', async () => {
const context = createMockContext({ enabled: false });
expect(await phase.shouldRun(context)).toBe(false);
});
it('should default to true when config is missing', async () => {
const context = createMockContext();
context.config = {} as any;
expect(await phase.shouldRun(context)).toBe(true);
});
});
describe('execute', () => {
it('should create data and HNSW directories', async () => {
const context = createMockContext();
await phase.execute(context);
expect(mkdirSync).toHaveBeenCalledWith(
expect.stringContaining('data'),
expect.objectContaining({ recursive: true })
);
expect(mkdirSync).toHaveBeenCalledWith(
expect.stringContaining('hnsw'),
expect.objectContaining({ recursive: true })
);
});
it('should write learning config JSON file', async () => {
const context = createMockContext();
await phase.execute(context);
expect(writeFileSync).toHaveBeenCalledWith(
expect.stringContaining('learning-config.json'),
expect.any(String),
'utf-8'
);
// Verify JSON content
const writtenContent = vi.mocked(writeFileSync).mock.calls.find(
(call: any[]) => (call[0] as string).includes('learning-config.json')
);
expect(writtenContent).toBeDefined();
const parsed = JSON.parse(writtenContent![1] as string);
expect(parsed).toHaveProperty('embeddingModel');
expect(parsed).toHaveProperty('hnswConfig');
});
it('should return success with learning result', async () => {
const context = createMockContext();
const result = await phase.execute(context);
expect(result.success).toBe(true);
expect(result.data).toBeDefined();
expect(result.data!.enabled).toBe(true);
expect(result.data!.dataDir).toContain('data');
expect(result.data!.hnswDir).toContain('hnsw');
});
it('should return disabled result when learning is disabled in config', async () => {
const context = createMockContext({ enabled: false });
const result = await phase.execute(context);
expect(result.success).toBe(true);
expect(result.data!.enabled).toBe(false);
expect(result.data!.patternsLoaded).toBe(0);
});
it('should attempt to load pretrained patterns when configured', async () => {
const context = createMockContext({ pretrainedPatterns: true });
const result = await phase.execute(context);
expect(result.success).toBe(true);
// Patterns dir should be created
expect(mkdirSync).toHaveBeenCalledWith(
expect.stringContaining('patterns'),
expect.objectContaining({ recursive: true })
);
});
it('should skip pattern loading when skipPatterns option is set', async () => {
const context = createMockContext(
{ pretrainedPatterns: true },
{ skipPatterns: true }
);
const result = await phase.execute(context);
expect(result.success).toBe(true);
expect(result.data!.patternsLoaded).toBe(0);
// When patterns are skipped, no pattern index.json should be written
const patternIndexWrites = vi.mocked(writeFileSync).mock.calls.filter(
(call: any[]) => (call[0] as string).includes('patterns/index.json')
);
expect(patternIndexWrites).toHaveLength(0);
});
it('should log data directory and pattern count', async () => {
const logFn = vi.fn();
const context = createMockContext();
context.services.log = logFn;
await phase.execute(context);
expect(logFn).toHaveBeenCalledWith(expect.stringContaining('Data dir'));
expect(logFn).toHaveBeenCalledWith(expect.stringContaining('HNSW dir'));
expect(logFn).toHaveBeenCalledWith(expect.stringContaining('Patterns loaded'));
});
});
});