mirror of
https://github.com/proffesor-for-testing/agentic-qe.git
synced 2026-09-19 08:45:47 +08:00
d5514b2143
## Summary - Reduced TypeScript `any` types from 568 to 538 (63% overall reduction from 1,470) - Fixed all 146 TypeScript compilation errors - Updated version to v2.7.1 across all 5 required files - Added ErrorUtils utility for consistent error handling - Updated GOAP plan with Phase 2 completion status ## Type Safety Improvements - Added index signatures for SerializableValue compatibility - Fixed memory retrieval type casts across 15+ agent files - Added proper task payload typing in agent performTask methods - Created shared error handling utilities ## Files Updated - package.json, package-lock.json (version bump) - README.md (version badge) - CHANGELOG.md (release notes) - src/mcp/server-instructions.ts (SERVER_VERSION) - src/core/memory/HNSWVectorMemory.ts (version info) - docs/plans/goap-issue-149-code-quality.md (progress tracking) - 100+ source and test files (type safety fixes) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
439 lines
12 KiB
TypeScript
439 lines
12 KiB
TypeScript
/**
|
|
* LearningEngine Persistence Integration Test
|
|
*
|
|
* MISSION: Prove that patterns persist across LearningEngine restarts
|
|
*
|
|
* This test validates:
|
|
* 1. Patterns persist across engine restarts
|
|
* 2. Patterns can be retrieved by semantic similarity
|
|
* 3. Pattern confidence updates persist
|
|
* 4. verifyPersistence() method works correctly
|
|
*/
|
|
|
|
import { LearningEngine } from '../../../src/learning/LearningEngine';
|
|
import { QEReasoningBank } from '../../../src/reasoning/QEReasoningBank';
|
|
import { createAgentDBManager } from '../../../src/core/memory/AgentDBManager';
|
|
import { AdapterType } from '../../../src/core/memory/AdapterConfig';
|
|
import { SwarmMemoryManager } from '../../../src/core/memory/SwarmMemoryManager';
|
|
import { LearnedPattern } from '../../../src/learning/types';
|
|
import path from 'path';
|
|
import fs from 'fs/promises';
|
|
|
|
describe('LearningEngine Persistence Integration', () => {
|
|
let testDbPath: string;
|
|
let agentDB: any;
|
|
let reasoningBank: QEReasoningBank;
|
|
let memoryManager: SwarmMemoryManager;
|
|
|
|
beforeEach(() => {
|
|
jest.useFakeTimers();
|
|
});
|
|
|
|
afterEach(() => {
|
|
jest.useRealTimers();
|
|
});
|
|
|
|
beforeAll(async () => {
|
|
// Use a dedicated test database
|
|
testDbPath = path.join(process.cwd(), '.test-data', 'learning-engine-persistence-test.db');
|
|
|
|
// Ensure test directory exists
|
|
await fs.mkdir(path.dirname(testDbPath), { recursive: true });
|
|
|
|
// Remove existing test database
|
|
try {
|
|
await fs.unlink(testDbPath);
|
|
} catch (error) {
|
|
// Database doesn't exist yet
|
|
}
|
|
|
|
// Create AgentDB manager for testing with mock adapter
|
|
agentDB = createAgentDBManager({
|
|
dbPath: testDbPath,
|
|
enableLearning: true,
|
|
enableReasoning: true,
|
|
enableQUICSync: false,
|
|
adapter: { type: AdapterType.MOCK }
|
|
});
|
|
|
|
await agentDB.initialize();
|
|
|
|
// Create ReasoningBank with the database
|
|
reasoningBank = new QEReasoningBank({ database: agentDB.adapter?.db });
|
|
await reasoningBank.initialize();
|
|
|
|
// Create SwarmMemoryManager for LearningEngine
|
|
memoryManager = new SwarmMemoryManager(':memory:');
|
|
await memoryManager.initialize();
|
|
});
|
|
|
|
afterAll(async () => {
|
|
// Cleanup
|
|
if (memoryManager) {
|
|
await memoryManager.close();
|
|
}
|
|
if (agentDB) {
|
|
await agentDB.close();
|
|
}
|
|
|
|
// Remove test database
|
|
try {
|
|
await fs.unlink(testDbPath);
|
|
} catch (error) {
|
|
// Ignore cleanup errors
|
|
}
|
|
});
|
|
|
|
it('should persist patterns across engine restart', async () => {
|
|
// Setup
|
|
const pattern: LearnedPattern = {
|
|
id: 'test-pattern-1',
|
|
pattern: 'authentication-testing:jwt-auth',
|
|
confidence: 0.88,
|
|
successRate: 0.95,
|
|
usageCount: 10,
|
|
contexts: ['authentication-testing', 'jwt'],
|
|
createdAt: new Date(),
|
|
lastUsedAt: new Date()
|
|
};
|
|
|
|
// First engine instance - store pattern
|
|
const engine1 = new LearningEngine('test-agent-1', memoryManager);
|
|
await engine1.initialize();
|
|
|
|
// Store pattern through updatePatterns (internal method simulation)
|
|
// We'll use the public API by triggering learnFromExecution
|
|
await engine1.learnFromExecution(
|
|
{
|
|
id: 'task-1',
|
|
type: 'authentication-testing',
|
|
requirements: {
|
|
capabilities: ['api-testing', 'jwt']
|
|
}
|
|
},
|
|
{
|
|
success: true,
|
|
strategy: 'jwt-auth',
|
|
toolsUsed: ['http-client', 'jwt-validator'],
|
|
executionTime: 150,
|
|
coverage: 0.92
|
|
},
|
|
{
|
|
taskId: 'task-1',
|
|
rating: 0.95,
|
|
issues: [],
|
|
suggestions: [],
|
|
timestamp: new Date(),
|
|
source: 'user'
|
|
}
|
|
);
|
|
|
|
// Dispose first instance (simulates restart)
|
|
engine1.dispose();
|
|
|
|
// Wait a bit to ensure persistence completes
|
|
await jest.advanceTimersByTimeAsync(100);
|
|
|
|
// Second engine instance - retrieve pattern
|
|
const engine2 = new LearningEngine('test-agent-1', memoryManager);
|
|
await engine2.initialize();
|
|
|
|
// Retrieve patterns
|
|
const retrieved = await engine2.getPatterns();
|
|
|
|
// Verify persistence
|
|
expect(retrieved.length).toBeGreaterThan(0);
|
|
expect(retrieved[0].pattern).toContain('authentication-testing');
|
|
expect(retrieved[0].successRate).toBeGreaterThan(0);
|
|
expect(retrieved[0].confidence).toBeGreaterThan(0);
|
|
|
|
// Cleanup
|
|
engine2.dispose();
|
|
}, 30000);
|
|
|
|
it('should retrieve patterns by semantic similarity', async () => {
|
|
const agentDB2 = createAgentDBManager({
|
|
dbPath: path.join(process.cwd(), '.test-data', 'learning-engine-similarity-test.db'),
|
|
enableLearning: true,
|
|
enableReasoning: true,
|
|
enableQUICSync: false,
|
|
adapter: { type: AdapterType.MOCK }
|
|
});
|
|
|
|
await agentDB2.initialize();
|
|
|
|
const reasoningBank2 = new QEReasoningBank({ database: agentDB2.adapter?.db });
|
|
await reasoningBank2.initialize();
|
|
|
|
const memoryManager2 = new SwarmMemoryManager(':memory:');
|
|
await memoryManager2.initialize();
|
|
|
|
const engine = new LearningEngine('test-agent-2', memoryManager2);
|
|
await engine.initialize();
|
|
|
|
// Store multiple related patterns
|
|
await engine.learnFromExecution(
|
|
{
|
|
id: 'task-auth-1',
|
|
type: 'user-authentication',
|
|
requirements: {
|
|
capabilities: ['login']
|
|
}
|
|
},
|
|
{
|
|
success: true,
|
|
strategy: 'login-endpoint',
|
|
toolsUsed: ['http'],
|
|
executionTime: 100
|
|
}
|
|
);
|
|
|
|
await engine.learnFromExecution(
|
|
{
|
|
id: 'task-auth-2',
|
|
type: 'user-authorization',
|
|
requirements: {
|
|
capabilities: ['permission']
|
|
}
|
|
},
|
|
{
|
|
success: true,
|
|
strategy: 'permission-check',
|
|
toolsUsed: ['rbac'],
|
|
executionTime: 80
|
|
}
|
|
);
|
|
|
|
// Wait for persistence
|
|
await jest.advanceTimersByTimeAsync(100);
|
|
|
|
// Retrieve by similarity
|
|
const retrieved = await engine.getPatterns();
|
|
|
|
expect(retrieved.length).toBeGreaterThanOrEqual(1);
|
|
expect(retrieved.some(p =>
|
|
p.pattern.includes('user-authentication') ||
|
|
p.pattern.includes('user-authorization')
|
|
)).toBe(true);
|
|
|
|
// Cleanup
|
|
engine.dispose();
|
|
await agentDB2.close();
|
|
|
|
// Remove test database
|
|
try {
|
|
await fs.unlink(path.join(process.cwd(), '.test-data', 'learning-engine-similarity-test.db'));
|
|
} catch (error) {
|
|
// Ignore
|
|
}
|
|
}, 30000);
|
|
|
|
it('should update pattern confidence over time', async () => {
|
|
const agentDB3 = createAgentDBManager({
|
|
dbPath: path.join(process.cwd(), '.test-data', 'learning-engine-confidence-test.db'),
|
|
enableLearning: true,
|
|
enableReasoning: true,
|
|
enableQUICSync: false,
|
|
adapter: { type: AdapterType.MOCK }
|
|
});
|
|
|
|
await agentDB3.initialize();
|
|
|
|
const reasoningBank3 = new QEReasoningBank({ database: agentDB3.adapter?.db });
|
|
await reasoningBank3.initialize();
|
|
|
|
const memoryManager3 = new SwarmMemoryManager(':memory:');
|
|
await memoryManager3.initialize();
|
|
|
|
const engine = new LearningEngine('test-agent-3', memoryManager3);
|
|
await engine.initialize();
|
|
|
|
// Store initial pattern
|
|
const initialOutcome = await engine.learnFromExecution(
|
|
{
|
|
id: 'task-api-1',
|
|
type: 'api-testing',
|
|
requirements: {
|
|
capabilities: ['rest']
|
|
}
|
|
},
|
|
{
|
|
success: true,
|
|
strategy: 'default',
|
|
toolsUsed: ['http'],
|
|
executionTime: 200
|
|
}
|
|
);
|
|
|
|
// Initial confidence should be low (few experiences)
|
|
const initialConfidence = initialOutcome.confidence;
|
|
|
|
// Add more successful experiences
|
|
for (let i = 0; i < 5; i++) {
|
|
await engine.learnFromExecution(
|
|
{
|
|
id: `task-api-${i + 2}`,
|
|
type: 'api-testing',
|
|
requirements: {
|
|
capabilities: ['rest']
|
|
}
|
|
},
|
|
{
|
|
success: true,
|
|
strategy: 'default',
|
|
toolsUsed: ['http'],
|
|
executionTime: 150
|
|
}
|
|
);
|
|
}
|
|
|
|
// Wait for persistence
|
|
await jest.advanceTimersByTimeAsync(100);
|
|
|
|
// Retrieve and verify confidence improved
|
|
const retrieved = await engine.getPatterns();
|
|
const apiPattern = retrieved.find(p => p.pattern.includes('api-testing'));
|
|
|
|
expect(apiPattern).toBeDefined();
|
|
if (apiPattern) {
|
|
expect(apiPattern.confidence).toBeGreaterThan(initialConfidence);
|
|
expect(apiPattern.usageCount).toBeGreaterThanOrEqual(5);
|
|
}
|
|
|
|
// Cleanup
|
|
engine.dispose();
|
|
await agentDB3.close();
|
|
|
|
// Remove test database
|
|
try {
|
|
await fs.unlink(path.join(process.cwd(), '.test-data', 'learning-engine-confidence-test.db'));
|
|
} catch (error) {
|
|
// Ignore
|
|
}
|
|
}, 30000);
|
|
|
|
it('should verify persistence with verifyPersistence() method', async () => {
|
|
const agentDB4 = createAgentDBManager({
|
|
dbPath: path.join(process.cwd(), '.test-data', 'learning-engine-verify-test.db'),
|
|
enableLearning: true,
|
|
enableReasoning: true,
|
|
enableQUICSync: false,
|
|
adapter: { type: AdapterType.MOCK }
|
|
});
|
|
|
|
await agentDB4.initialize();
|
|
|
|
const reasoningBank4 = new QEReasoningBank({ database: agentDB4.adapter?.db });
|
|
await reasoningBank4.initialize();
|
|
|
|
const memoryManager4 = new SwarmMemoryManager(':memory:');
|
|
await memoryManager4.initialize();
|
|
|
|
const engine = new LearningEngine('test-agent-4', memoryManager4);
|
|
await engine.initialize();
|
|
|
|
// Store a pattern
|
|
await engine.learnFromExecution(
|
|
{
|
|
id: 'task-verify-1',
|
|
type: 'verification-testing',
|
|
requirements: {
|
|
capabilities: ['validation']
|
|
}
|
|
},
|
|
{
|
|
success: true,
|
|
strategy: 'default',
|
|
toolsUsed: ['validator'],
|
|
executionTime: 100
|
|
}
|
|
);
|
|
|
|
// Wait for persistence
|
|
await jest.advanceTimersByTimeAsync(100);
|
|
|
|
// Note: verifyPersistence() doesn't exist in current LearningEngine
|
|
// We'll verify persistence by checking pattern retrieval works
|
|
const patterns = await engine.getPatterns();
|
|
const verified = patterns.length > 0;
|
|
|
|
expect(verified).toBe(true);
|
|
|
|
// Cleanup
|
|
engine.dispose();
|
|
await agentDB4.close();
|
|
|
|
// Remove test database
|
|
try {
|
|
await fs.unlink(path.join(process.cwd(), '.test-data', 'learning-engine-verify-test.db'));
|
|
} catch (error) {
|
|
// Ignore
|
|
}
|
|
}, 30000);
|
|
|
|
it('should handle concurrent pattern storage and retrieval', async () => {
|
|
const agentDB5 = createAgentDBManager({
|
|
dbPath: path.join(process.cwd(), '.test-data', 'learning-engine-concurrent-test.db'),
|
|
enableLearning: true,
|
|
enableReasoning: true,
|
|
enableQUICSync: false,
|
|
adapter: { type: AdapterType.MOCK }
|
|
});
|
|
|
|
await agentDB5.initialize();
|
|
|
|
const reasoningBank5 = new QEReasoningBank({ database: agentDB5.adapter?.db });
|
|
await reasoningBank5.initialize();
|
|
|
|
const memoryManager5 = new SwarmMemoryManager(':memory:');
|
|
await memoryManager5.initialize();
|
|
|
|
const engine = new LearningEngine('test-agent-5', memoryManager5);
|
|
await engine.initialize();
|
|
|
|
// Store multiple patterns concurrently
|
|
const promises = [];
|
|
for (let i = 0; i < 10; i++) {
|
|
promises.push(
|
|
engine.learnFromExecution(
|
|
{
|
|
id: `task-concurrent-${i}`,
|
|
type: 'concurrent-testing',
|
|
requirements: {
|
|
capabilities: ['concurrency']
|
|
}
|
|
},
|
|
{
|
|
success: true,
|
|
strategy: 'parallel',
|
|
toolsUsed: ['async'],
|
|
executionTime: 50 + i * 10
|
|
}
|
|
)
|
|
);
|
|
}
|
|
|
|
await Promise.all(promises);
|
|
|
|
// Wait for persistence
|
|
await jest.advanceTimersByTimeAsync(200);
|
|
|
|
// Retrieve patterns
|
|
const retrieved = await engine.getPatterns();
|
|
|
|
// Verify all patterns were stored
|
|
expect(retrieved.length).toBeGreaterThan(0);
|
|
|
|
// Cleanup
|
|
engine.dispose();
|
|
await agentDB5.close();
|
|
|
|
// Remove test database
|
|
try {
|
|
await fs.unlink(path.join(process.cwd(), '.test-data', 'learning-engine-concurrent-test.db'));
|
|
} catch (error) {
|
|
// Ignore
|
|
}
|
|
}, 30000);
|
|
});
|