Files
Profa b91627f282 fix(quality): implement GOAP Issue #149 code quality remediation
Phase 1: Test Determinism
- Create SeededRandom utility with Mulberry32 PRNG (src/utils/SeededRandom.ts)
- Migrate 258+ Math.random() calls to SeededRandom across 65+ test files
- Add timer test utilities (tests/helpers/timerTestUtils.ts)
- Add jest.useFakeTimers() pattern to 13 test files

Phase 2: Type Safety (63.5% reduction)
- Reduce `any` types from ~1,800 to 656
- Add 50+ new interfaces across agents, core, mcp, cli modules
- Fix all TypeScript errors (0 errors, build passes)
- Replace Record<string, any> with Record<string, unknown>
- Add proper type guards for hook methods (PostTaskData, TaskErrorData)

Key improvements:
- agents/: 105 → 0 any types (100% reduction)
- mcp/: 226 → 102 any types (55% reduction)
- cli/: 277 → 168 any types (39% reduction)
- core/: 199 → 175 any types (12% reduction)

Closes #149

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-27 17:08:12 +00:00

231 lines
6.7 KiB
TypeScript

/**
* CLI Learning Commands Tests
* Tests for `aqe learn` commands (status, history, export)
*/
import { describe, it, expect, beforeEach, afterEach, jest } from '@jest/globals';
import { SwarmMemoryManager } from '@core/memory/SwarmMemoryManager';
import { LearningEngine } from '@learning/LearningEngine';
import { Database } from '@utils/Database';
import { createSeededRandom } from '../../../src/utils/SeededRandom';
import * as fs from 'fs-extra';
import * as path from 'path';
describe('CLI Learning Commands', () => {
let memoryManager: SwarmMemoryManager;
let learningEngine: LearningEngine;
let database: Database;
let testDbPath: string;
const TEST_AGENT_ID = 'cli-test-agent';
beforeEach(async () => {
// Create test database
testDbPath = path.join(__dirname, '../../temp', `cli-learn-${Date.now()}.db`);
await fs.ensureDir(path.dirname(testDbPath));
database = new Database(testDbPath);
await database.initialize();
memoryManager = new SwarmMemoryManager(testDbPath);
await memoryManager.initialize();
learningEngine = new LearningEngine(TEST_AGENT_ID, memoryManager, {
enabled: true,
learningRate: 0.1,
discountFactor: 0.95
});
await learningEngine.initialize();
});
afterEach(async () => {
if (learningEngine) {
await learningEngine.dispose();
}
if (database) {
await database.close();
}
if (memoryManager) {
await memoryManager.close();
}
if (fs.existsSync(testDbPath)) {
await fs.remove(testDbPath);
}
});
describe('aqe learn status', () => {
it('should show learning status for agent', async () => {
// Record some learning experiences
const rng0 = createSeededRandom(23000);
const rng1 = createSeededRandom(23001);
for (let i = 0; i < 10; i++) {
await learningEngine.learnFromExecution(
{ id: `task-${i}`, type: 'test-generation', previousAttempts: 0 },
{
success: rng0.random() > 0.3,
executionTime: 1000 + rng1.random() * 1000,
strategy: 'parallel',
toolsUsed: ['jest'],
parallelization: 0.8,
retryPolicy: 'exponential',
resourceAllocation: 0.7
}
);
}
const config = learningEngine.getConfig();
const enabled = learningEngine.isEnabled();
expect(enabled).toBe(true);
expect(config.learningRate).toBe(0.1);
expect(config.discountFactor).toBe(0.95);
});
it('should show empty status when no learning data', async () => {
const config = learningEngine.getConfig();
expect(config).toBeDefined();
expect(config.learningRate).toBe(0.1);
});
});
describe('aqe learn history', () => {
it('should return learning history', async () => {
// Record learning experiences
const experiences = [];
for (let i = 0; i < 5; i++) {
const task = { id: `task-${i}`, type: 'test-execution', previousAttempts: i };
const result = {
success: true,
executionTime: 1000,
strategy: 'parallel',
toolsUsed: ['jest'],
parallelization: 0.8,
retryPolicy: 'exponential',
resourceAllocation: 0.7
};
await learningEngine.learnFromExecution(task, result);
experiences.push({ task, result });
}
// Verify learning occurred
expect(experiences.length).toBe(5);
});
it('should limit history with --limit flag', async () => {
// Record 20 experiences
for (let i = 0; i < 20; i++) {
await learningEngine.learnFromExecution(
{ id: `task-${i}`, type: 'test', previousAttempts: 0 },
{
success: true,
executionTime: 1000,
strategy: 'parallel',
toolsUsed: [],
parallelization: 0.5,
retryPolicy: 'none',
resourceAllocation: 0.5
}
);
}
// Limit should work (simulated - actual CLI would limit database query)
const limit = 10;
expect(limit).toBe(10);
});
});
describe('aqe learn export', () => {
it('should export learning data to JSON', async () => {
// Record some learning data
for (let i = 0; i < 5; i++) {
await learningEngine.learnFromExecution(
{ id: `task-${i}`, type: 'test', previousAttempts: 0 },
{
success: true,
executionTime: 1000,
strategy: 'parallel',
toolsUsed: [],
parallelization: 0.5,
retryPolicy: 'none',
resourceAllocation: 0.5
}
);
}
const config = learningEngine.getConfig();
// Verify exportable data exists
expect(config).toBeDefined();
expect(config.learningRate).toBeDefined();
expect(config.discountFactor).toBeDefined();
});
it('should export to specified output file', async () => {
const outputPath = path.join(__dirname, '../../temp', 'learning-export.json');
// Record data
await learningEngine.learnFromExecution(
{ id: 'task-1', type: 'test', previousAttempts: 0 },
{
success: true,
executionTime: 1000,
strategy: 'parallel',
toolsUsed: [],
parallelization: 0.5,
retryPolicy: 'none',
resourceAllocation: 0.5
}
);
const exportData = {
agent: TEST_AGENT_ID,
config: learningEngine.getConfig(),
timestamp: new Date().toISOString()
};
// Simulate export
await fs.writeJson(outputPath, exportData, { spaces: 2 });
// Verify export file
expect(await fs.pathExists(outputPath)).toBe(true);
const exported = await fs.readJson(outputPath);
expect(exported.agent).toBe(TEST_AGENT_ID);
expect(exported.config).toBeDefined();
// Cleanup
await fs.remove(outputPath);
});
});
describe('aqe learn integration', () => {
it('should show learning progress over time', async () => {
const rng = createSeededRandom(23002);
const iterations = 50;
let successCount = 0;
for (let i = 0; i < iterations; i++) {
const success = rng.random() > 0.2; // 80% success rate
if (success) successCount++;
await learningEngine.learnFromExecution(
{ id: `task-${i}`, type: 'test', previousAttempts: 0 },
{
success,
executionTime: 1000,
strategy: 'parallel',
toolsUsed: [],
parallelization: 0.5,
retryPolicy: 'none',
resourceAllocation: 0.5
}
);
}
const successRate = successCount / iterations;
expect(successRate).toBeGreaterThan(0.7); // Should be around 80%
});
});
});