tasq/node_modules/agentic-flow/validation/test-wasm-e2e.ts

355 lines
11 KiB
TypeScript

/**
* Comprehensive E2E Test for ReasoningBank WASM Integration
*
* Tests all functionality in a clean Docker environment simulating remote npx usage.
* Validates: WASM loading, storage operations, API completeness, performance, memory.
*/
import { ReasoningBankAdapter, createReasoningBank, type PatternInput } from '../dist/reasoningbank/wasm-adapter.js';
import { existsSync, statSync } from 'fs';
import { resolve } from 'path';
interface TestResult {
name: string;
passed: boolean;
duration: number;
error?: string;
details?: any;
}
const results: TestResult[] = [];
function test(name: string, fn: () => Promise<void>) {
return async (): Promise<TestResult> => {
const start = Date.now();
try {
await fn();
const duration = Date.now() - start;
console.log(`${name} (${duration}ms)`);
return { name, passed: true, duration };
} catch (error) {
const duration = Date.now() - start;
const errorMessage = error instanceof Error ? error.message : String(error);
console.error(`${name} (${duration}ms):`, errorMessage);
return { name, passed: false, duration, error: errorMessage };
}
};
}
// Test Suite
const tests = {
'WASM Files Exist': test('WASM Files Exist', async () => {
const wasmPath = resolve(import.meta.dirname, '../wasm/reasoningbank/reasoningbank_wasm_bg.wasm');
if (!existsSync(wasmPath)) {
throw new Error(`WASM file not found at ${wasmPath}`);
}
const stats = statSync(wasmPath);
if (stats.size < 100000) {
throw new Error(`WASM file too small (${stats.size} bytes)`);
}
console.log(` WASM size: ${(stats.size / 1024).toFixed(1)}KB`);
}),
'TypeScript Wrapper Compiled': test('TypeScript Wrapper Compiled', async () => {
const wrapperPath = resolve(import.meta.dirname, '../dist/reasoningbank/wasm-adapter.js');
if (!existsSync(wrapperPath)) {
throw new Error(`TypeScript wrapper not found at ${wrapperPath}`);
}
}),
'Create ReasoningBank Instance': test('Create ReasoningBank Instance', async () => {
const rb = new ReasoningBankAdapter('e2e-test-instance');
const stats = await rb.getStats();
if (typeof stats.total_patterns !== 'number') {
throw new Error('Invalid stats structure');
}
}),
'Store Pattern': test('Store Pattern', async () => {
const rb = await createReasoningBank('e2e-test-store');
const pattern: PatternInput = {
task_description: 'E2E test pattern for WASM validation',
task_category: 'e2e-testing',
strategy: 'comprehensive-validation',
success_score: 0.98,
duration_seconds: 2.5,
};
const id = await rb.storePattern(pattern);
if (!id || typeof id !== 'string') {
throw new Error('Pattern ID not returned');
}
console.log(` Pattern ID: ${id.substring(0, 8)}...`);
}),
'Retrieve Pattern': test('Retrieve Pattern', async () => {
const rb = await createReasoningBank('e2e-test-retrieve');
const pattern: PatternInput = {
task_description: 'Test retrieval pattern',
task_category: 'retrieval-test',
strategy: 'test-strategy',
success_score: 0.95,
};
const id = await rb.storePattern(pattern);
const retrieved = await rb.getPattern(id);
if (!retrieved) {
throw new Error('Pattern not retrieved');
}
if (retrieved.task_description !== pattern.task_description) {
throw new Error('Pattern data mismatch');
}
}),
'Search by Category': test('Search by Category', async () => {
const rb = await createReasoningBank('e2e-test-search');
// Store multiple patterns
for (let i = 0; i < 5; i++) {
await rb.storePattern({
task_description: `Search test pattern ${i}`,
task_category: 'search-category',
strategy: `strategy-${i}`,
success_score: 0.8 + (i * 0.02),
});
}
const results = await rb.searchByCategory('search-category', 10);
if (results.length < 5) {
throw new Error(`Expected at least 5 patterns, got ${results.length}`);
}
console.log(` Found ${results.length} patterns`);
}),
'Find Similar Patterns': test('Find Similar Patterns', async () => {
const rb = await createReasoningBank('e2e-test-similar');
// Store reference patterns
await rb.storePattern({
task_description: 'Optimize database query performance',
task_category: 'performance',
strategy: 'indexing',
success_score: 0.92,
});
await rb.storePattern({
task_description: 'Improve API response time',
task_category: 'performance',
strategy: 'caching',
success_score: 0.88,
});
// Search for similar
const similar = await rb.findSimilar(
'Speed up database operations',
'performance',
5
);
if (similar.length === 0) {
throw new Error('No similar patterns found');
}
console.log(` Found ${similar.length} similar, top score: ${similar[0].similarity_score.toFixed(3)}`);
}),
'Get Storage Statistics': test('Get Storage Statistics', async () => {
const rb = await createReasoningBank('e2e-test-stats');
const stats = await rb.getStats();
if (typeof stats.total_patterns !== 'number') {
throw new Error('Invalid total_patterns');
}
if (typeof stats.total_categories !== 'number') {
throw new Error('Invalid total_categories');
}
console.log(` Stats: ${stats.total_patterns} patterns, ${stats.total_categories} categories`);
}),
'Concurrent Operations': test('Concurrent Operations', async () => {
const rb = await createReasoningBank('e2e-test-concurrent');
const operations = Array.from({ length: 10 }, (_, i) =>
rb.storePattern({
task_description: `Concurrent pattern ${i}`,
task_category: 'concurrency-test',
strategy: 'parallel',
success_score: 0.9,
})
);
const ids = await Promise.all(operations);
if (ids.length !== 10) {
throw new Error(`Expected 10 IDs, got ${ids.length}`);
}
if (new Set(ids).size !== 10) {
throw new Error('Duplicate IDs detected');
}
}),
'Performance Benchmark': test('Performance Benchmark', async () => {
const rb = await createReasoningBank('e2e-test-perf');
const iterations = 50;
const start = Date.now();
for (let i = 0; i < iterations; i++) {
await rb.storePattern({
task_description: `Performance test pattern ${i}`,
task_category: 'benchmark',
strategy: 'speed-test',
success_score: 0.85,
});
}
const duration = Date.now() - start;
const avgTime = duration / iterations;
console.log(` ${iterations} ops in ${duration}ms (avg: ${avgTime.toFixed(2)}ms/op)`);
if (avgTime > 100) {
throw new Error(`Performance too slow: ${avgTime}ms/op (expected <100ms)`);
}
}),
'Memory Stability': test('Memory Stability', async () => {
const initialMem = process.memoryUsage().heapUsed;
const rb = await createReasoningBank('e2e-test-memory');
// Store 100 patterns
for (let i = 0; i < 100; i++) {
await rb.storePattern({
task_description: `Memory test pattern ${i}`,
task_category: 'memory-test',
strategy: 'stability',
success_score: 0.9,
});
}
// Force garbage collection if available
if (global.gc) {
global.gc();
}
const finalMem = process.memoryUsage().heapUsed;
const memDelta = (finalMem - initialMem) / 1024 / 1024;
console.log(` Memory delta: ${memDelta.toFixed(2)}MB`);
if (memDelta > 50) {
throw new Error(`Memory leak detected: ${memDelta}MB increase`);
}
}),
'Error Handling': test('Error Handling', async () => {
const rb = await createReasoningBank('e2e-test-errors');
// Test invalid UUID
try {
await rb.getPattern('invalid-uuid');
throw new Error('Should have thrown error for invalid UUID');
} catch (error) {
if (error instanceof Error && error.message.includes('Should have thrown')) {
throw error;
}
// Expected error
}
// Test empty category
const results = await rb.searchByCategory('nonexistent-category', 10);
if (!Array.isArray(results)) {
throw new Error('Should return empty array for nonexistent category');
}
}),
'Zero Regression Check': test('Zero Regression Check', async () => {
// Verify all existing APIs still work
const rb = await createReasoningBank('e2e-regression-check');
// Test all methods exist
const methods = ['storePattern', 'getPattern', 'searchByCategory', 'findSimilar', 'getStats'];
for (const method of methods) {
if (typeof (rb as any)[method] !== 'function') {
throw new Error(`Missing method: ${method}`);
}
}
// Test basic workflow
const id = await rb.storePattern({
task_description: 'Regression test',
task_category: 'regression',
strategy: 'verification',
success_score: 1.0,
});
const pattern = await rb.getPattern(id);
if (!pattern) throw new Error('Regression: Pattern not stored');
const similar = await rb.findSimilar('Regression verification', 'regression', 5);
if (!Array.isArray(similar)) throw new Error('Regression: findSimilar broken');
const stats = await rb.getStats();
if (!stats.total_patterns) throw new Error('Regression: stats broken');
}),
};
// Run all tests
async function runE2ETests() {
console.log('\n🧪 Starting Comprehensive E2E Test Suite for WASM Integration\n');
console.log('=' .repeat(70));
console.log();
const startTime = Date.now();
for (const [name, testFn] of Object.entries(tests)) {
const result = await testFn();
results.push(result);
}
const totalDuration = Date.now() - startTime;
console.log();
console.log('='.repeat(70));
console.log('\n📊 Test Results Summary\n');
const passed = results.filter(r => r.passed).length;
const failed = results.filter(r => r.passed === false).length;
console.log(`Total Tests: ${results.length}`);
console.log(`✅ Passed: ${passed}`);
console.log(`❌ Failed: ${failed}`);
console.log(`⏱️ Total Duration: ${totalDuration}ms`);
console.log();
if (failed > 0) {
console.log('Failed Tests:');
results
.filter(r => !r.passed)
.forEach(r => console.log(` - ${r.name}: ${r.error}`));
console.log();
}
// Performance summary
const perfResult = results.find(r => r.name === 'Performance Benchmark');
if (perfResult?.passed) {
console.log('✅ Performance: WASM operations meeting targets');
}
// Memory summary
const memResult = results.find(r => r.name === 'Memory Stability');
if (memResult?.passed) {
console.log('✅ Memory: No leaks detected');
}
// Zero regressions
const regressionResult = results.find(r => r.name === 'Zero Regression Check');
if (regressionResult?.passed) {
console.log('✅ Regressions: Zero detected - all APIs functional');
}
console.log();
console.log('='.repeat(70));
if (failed === 0) {
console.log('\n🎉 All E2E tests PASSED! WASM integration fully functional.\n');
return 0;
} else {
console.log('\n❌ Some E2E tests FAILED. Review errors above.\n');
return 1;
}
}
// Execute test suite
runE2ETests()
.then(exitCode => process.exit(exitCode))
.catch(error => {
console.error('\n💥 Fatal error running E2E tests:', error);
process.exit(1);
});