#!/usr/bin/env node /** * Comprehensive WASM Integration Tests * Tests all exported functionality in Node.js environment */ import { readFile } from 'fs/promises'; import { fileURLToPath } from 'url'; import { dirname, join } from 'path'; const __filename = fileURLToPath(import.meta.url); const __dirname = dirname(__filename); // Test results const results = { total: 0, passed: 0, failed: 0, tests: [] }; function test(name, fn) { results.total++; try { fn(); results.passed++; results.tests.push({ name, status: 'PASS', error: null }); console.log(`✅ PASS: ${name}`); } catch (error) { results.failed++; results.tests.push({ name, status: 'FAIL', error: error.message }); console.error(`❌ FAIL: ${name}`); console.error(` Error: ${error.message}`); } } async function testAsync(name, fn) { results.total++; try { await fn(); results.passed++; results.tests.push({ name, status: 'PASS', error: null }); console.log(`✅ PASS: ${name}`); } catch (error) { results.failed++; results.tests.push({ name, status: 'FAIL', error: error.message }); console.error(`❌ FAIL: ${name}`); console.error(` Error: ${error.message}`); } } function assert(condition, message) { if (!condition) { throw new Error(message || 'Assertion failed'); } } function assertApprox(actual, expected, tolerance, message) { if (Math.abs(actual - expected) > tolerance) { throw new Error( message || `Expected ${actual} to be approximately ${expected} (±${tolerance})` ); } } async function runTests() { console.log('🚀 Starting Comprehensive WASM Tests\n'); console.log('═'.repeat(60)); // Load WASM module const wasmPath = join(__dirname, '../pkg/midstream_wasm_bg.wasm'); const wasmBuffer = await readFile(wasmPath); const wasmModule = await WebAssembly.compile(wasmBuffer); // Import JS bindings const { default: init, ...exports } = await import('../pkg/midstream_wasm.js'); await init(wasmModule); const { version, TemporalCompare, NanoScheduler, StrangeLoop, QuicMultistream, benchmark_dtw } = exports; console.log('\n📦 Module Information'); console.log('─'.repeat(60)); test('Version exported', () => { const ver = version(); assert(typeof ver === 'string', 'Version should be a string'); assert(ver.length > 0, 'Version should not be empty'); console.log(` Version: ${ver}`); }); console.log('\n🕒 TemporalCompare Tests'); console.log('─'.repeat(60)); test('TemporalCompare constructor', () => { const tc = new TemporalCompare(); assert(tc !== null, 'TemporalCompare should be created'); }); test('TemporalCompare with window size', () => { const tc = new TemporalCompare(200); assert(tc !== null, 'TemporalCompare should accept window size'); }); test('DTW identical sequences', () => { const tc = new TemporalCompare(); const seq = new Float64Array([1.0, 2.0, 3.0, 4.0, 5.0]); const distance = tc.dtw(seq, seq); assert(distance === 0.0, `DTW of identical sequences should be 0, got ${distance}`); }); test('DTW different sequences', () => { const tc = new TemporalCompare(); const seq1 = new Float64Array([1.0, 2.0, 3.0]); const seq2 = new Float64Array([2.0, 3.0, 4.0]); const distance = tc.dtw(seq1, seq2); assert(distance > 0, `DTW of different sequences should be > 0, got ${distance}`); console.log(` DTW distance: ${distance.toFixed(2)}`); }); test('DTW with realistic time series', () => { const tc = new TemporalCompare(); const seq1 = Float64Array.from({ length: 100 }, (_, i) => Math.sin(i / 10)); const seq2 = Float64Array.from({ length: 100 }, (_, i) => Math.sin(i / 10 + 0.5)); const distance = tc.dtw(seq1, seq2); assert(distance > 0, 'DTW should compute distance'); assert(distance < 1000, 'DTW distance should be reasonable'); console.log(` Time series DTW: ${distance.toFixed(2)}`); }); test('LCS identical sequences', () => { const tc = new TemporalCompare(); const seq = new Int32Array([1, 2, 3, 4, 5]); const length = tc.lcs(seq, seq); assert(length === 5, `LCS of identical sequences should be 5, got ${length}`); }); test('LCS subsequence', () => { const tc = new TemporalCompare(); const seq1 = new Int32Array([1, 2, 3, 4, 5]); const seq2 = new Int32Array([1, 3, 5]); const length = tc.lcs(seq1, seq2); assert(length === 3, `LCS should be 3, got ${length}`); }); test('Edit distance identical strings', () => { const tc = new TemporalCompare(); const distance = tc.edit_distance('hello', 'hello'); assert(distance === 0, `Edit distance of identical strings should be 0, got ${distance}`); }); test('Edit distance classic example', () => { const tc = new TemporalCompare(); const distance = tc.edit_distance('kitten', 'sitting'); assert(distance === 3, `Edit distance should be 3, got ${distance}`); }); test('Comprehensive analyze method', () => { const tc = new TemporalCompare(); const seq1 = Float64Array.from({ length: 50 }, (_, i) => Math.sin(i / 5)); const seq2 = Float64Array.from({ length: 50 }, (_, i) => Math.sin(i / 5 + 0.3)); const metrics = tc.analyze(seq1, seq2); assert(metrics.dtw_distance !== undefined, 'Should have DTW distance'); assert(metrics.lcs_length !== undefined, 'Should have LCS length'); assert(metrics.edit_distance !== undefined, 'Should have edit distance'); assert(metrics.similarity_score !== undefined, 'Should have similarity score'); assert(metrics.similarity_score >= 0 && metrics.similarity_score <= 1, 'Similarity score should be between 0 and 1'); console.log(` DTW Distance: ${metrics.dtw_distance.toFixed(2)}`); console.log(` LCS Length: ${metrics.lcs_length}`); console.log(` Edit Distance: ${metrics.edit_distance}`); console.log(` Similarity: ${(metrics.similarity_score * 100).toFixed(1)}%`); }); console.log('\n⏱️ NanoScheduler Tests'); console.log('─'.repeat(60)); test('NanoScheduler constructor', () => { const scheduler = new NanoScheduler(); assert(scheduler !== null, 'NanoScheduler should be created'); }); test('Schedule task', () => { const scheduler = new NanoScheduler(); let executed = false; const taskId = scheduler.schedule(() => { executed = true; }, 1000000); // 1ms assert(typeof taskId === 'number', 'Should return task ID'); assert(taskId > 0, 'Task ID should be positive'); }); test('Cancel task', () => { const scheduler = new NanoScheduler(); const taskId = scheduler.schedule(() => {}, 1000000); const cancelled = scheduler.cancel(taskId); assert(cancelled === true, 'Should cancel successfully'); }); test('Cancel non-existent task', () => { const scheduler = new NanoScheduler(); const cancelled = scheduler.cancel(99999); assert(cancelled === false, 'Should return false for non-existent task'); }); test('Now nanoseconds', () => { const scheduler = new NanoScheduler(); const now = scheduler.now_ns(); assert(typeof now === 'number', 'Should return number'); assert(now > 0, 'Should return positive number'); }); test('Pending count', () => { const scheduler = new NanoScheduler(); assert(scheduler.pending_count === 0, 'Initial pending count should be 0'); scheduler.schedule(() => {}, 1000000); assert(scheduler.pending_count === 1, 'Pending count should be 1'); }); test('Tick execution', () => { const scheduler = new NanoScheduler(); scheduler.schedule(() => {}, 0); // Immediate const executed = scheduler.tick(); assert(executed >= 0, 'Should return execution count'); }); console.log('\n🧠 StrangeLoop Meta-Learning Tests'); console.log('─'.repeat(60)); test('StrangeLoop constructor', () => { const loop = new StrangeLoop(); assert(loop !== null, 'StrangeLoop should be created'); }); test('StrangeLoop with custom learning rate', () => { const loop = new StrangeLoop(0.2); assert(loop !== null, 'StrangeLoop should accept learning rate'); }); test('Observe pattern', () => { const loop = new StrangeLoop(0.1); loop.observe('test-pattern', 0.8); assert(loop.iteration_count === 1, 'Iteration count should be 1'); assert(loop.pattern_count === 1, 'Pattern count should be 1'); }); test('Get confidence', () => { const loop = new StrangeLoop(0.1); loop.observe('test-pattern', 0.8); const confidence = loop.get_confidence('test-pattern'); assert(confidence !== undefined, 'Should return confidence'); assert(confidence >= 0 && confidence <= 1, 'Confidence should be 0-1'); }); test('Get confidence for unknown pattern', () => { const loop = new StrangeLoop(0.1); const confidence = loop.get_confidence('unknown'); assert(confidence === undefined, 'Should return undefined for unknown pattern'); }); test('Best pattern', () => { const loop = new StrangeLoop(0.1); loop.observe('pattern-a', 0.5); loop.observe('pattern-b', 0.8); loop.observe('pattern-c', 0.3); const best = loop.best_pattern(); assert(best !== undefined, 'Should return best pattern'); assert(best.pattern_id === 'pattern-b', 'Should return pattern-b as best'); console.log(` Best pattern: ${best.pattern_id} (${(best.confidence * 100).toFixed(1)}%)`); }); test('Reflect method', () => { const loop = new StrangeLoop(0.1); loop.observe('pattern-1', 0.6); loop.observe('pattern-2', 0.7); const reflection = loop.reflect(); assert(reflection !== null, 'Should return reflection object'); assert(typeof reflection === 'object', 'Reflection should be object'); }); test('Learning progression', () => { const loop = new StrangeLoop(0.1); for (let i = 0; i < 10; i++) { loop.observe('learning-pattern', 0.5 + i * 0.05); } const confidence = loop.get_confidence('learning-pattern'); assert(loop.iteration_count === 10, 'Should track iterations'); console.log(` Final confidence after 10 observations: ${(confidence * 100).toFixed(1)}%`); }); console.log('\n🌐 QuicMultistream Tests'); console.log('─'.repeat(60)); test('QuicMultistream constructor', () => { const quic = new QuicMultistream(); assert(quic !== null, 'QuicMultistream should be created'); }); test('Open stream', () => { const quic = new QuicMultistream(); const streamId = quic.open_stream(128); assert(typeof streamId === 'number', 'Should return stream ID'); assert(streamId >= 0, 'Stream ID should be non-negative'); assert(quic.stream_count === 1, 'Stream count should be 1'); }); test('Open multiple streams', () => { const quic = new QuicMultistream(); const id1 = quic.open_stream(100); const id2 = quic.open_stream(200); const id3 = quic.open_stream(50); assert(id1 !== id2 && id2 !== id3, 'Stream IDs should be unique'); assert(quic.stream_count === 3, 'Stream count should be 3'); }); test('Close stream', () => { const quic = new QuicMultistream(); const streamId = quic.open_stream(128); const closed = quic.close_stream(streamId); assert(closed === true, 'Should close successfully'); assert(quic.stream_count === 0, 'Stream count should be 0'); }); test('Close non-existent stream', () => { const quic = new QuicMultistream(); const closed = quic.close_stream(99999); assert(closed === false, 'Should return false for non-existent stream'); }); test('Send data', () => { const quic = new QuicMultistream(); const streamId = quic.open_stream(128); const data = new Uint8Array([1, 2, 3, 4, 5]); const sent = quic.send(streamId, data); assert(sent === 5, 'Should return number of bytes sent'); }); test('Send data to non-existent stream', () => { const quic = new QuicMultistream(); const data = new Uint8Array([1, 2, 3]); try { quic.send(99999, data); assert(false, 'Should throw error'); } catch (error) { assert(true, 'Should throw error for non-existent stream'); } }); test('Receive data', () => { const quic = new QuicMultistream(); const streamId = quic.open_stream(128); const received = quic.receive(streamId, 1024); assert(received instanceof Uint8Array, 'Should return Uint8Array'); assert(received.length === 1024, 'Should return correct size'); }); test('Get stream stats', () => { const quic = new QuicMultistream(); const streamId = quic.open_stream(200); const data = new Uint8Array(100); quic.send(streamId, data); quic.receive(streamId, 50); const stats = quic.get_stats(streamId); assert(stats !== null, 'Should return stats'); assert(stats.stream_id === streamId, 'Should have correct stream ID'); assert(stats.priority === 200, 'Should have correct priority'); assert(stats.bytes_sent === 100, 'Should track bytes sent'); assert(stats.bytes_received === 50, 'Should track bytes received'); console.log(` Stream ${streamId} stats: sent=${stats.bytes_sent}, recv=${stats.bytes_received}`); }); console.log('\n⚡ Performance Benchmarks'); console.log('─'.repeat(60)); test('Benchmark DTW function', () => { const avgTime = benchmark_dtw(100, 50); assert(typeof avgTime === 'number', 'Should return number'); assert(avgTime > 0, 'Should return positive time'); console.log(` DTW (100 elements, 50 iterations): ${avgTime.toFixed(3)}ms avg`); console.log(` Throughput: ${(1000 / avgTime).toFixed(0)} ops/sec`); }); test('DTW performance scaling', () => { const time50 = benchmark_dtw(50, 20); const time100 = benchmark_dtw(100, 20); const time200 = benchmark_dtw(200, 20); console.log(` DTW 50 elements: ${time50.toFixed(3)}ms`); console.log(` DTW 100 elements: ${time100.toFixed(3)}ms`); console.log(` DTW 200 elements: ${time200.toFixed(3)}ms`); }); console.log('\n🔒 Error Handling Tests'); console.log('─'.repeat(60)); test('DTW with empty sequences', () => { const tc = new TemporalCompare(); const empty = new Float64Array([]); const seq = new Float64Array([1, 2, 3]); const distance = tc.dtw(empty, seq); assert(distance === Infinity, 'Empty sequence should return Infinity'); }); test('Memory cleanup', () => { // Create and destroy many objects to test memory management for (let i = 0; i < 100; i++) { const tc = new TemporalCompare(); const scheduler = new NanoScheduler(); const loop = new StrangeLoop(); const quic = new QuicMultistream(); // Use them briefly tc.dtw(new Float64Array([1, 2]), new Float64Array([1, 2])); scheduler.schedule(() => {}, 1000); loop.observe('test', 0.5); quic.open_stream(100); } assert(true, 'Should handle memory cleanup'); }); console.log('\n═'.repeat(60)); console.log('\n📊 Test Summary'); console.log('─'.repeat(60)); console.log(`Total Tests: ${results.total}`); console.log(`✅ Passed: ${results.passed}`); console.log(`❌ Failed: ${results.failed}`); console.log(`Success Rate: ${((results.passed / results.total) * 100).toFixed(1)}%`); if (results.failed > 0) { console.log('\n❌ Failed Tests:'); results.tests .filter(t => t.status === 'FAIL') .forEach(t => console.log(` - ${t.name}: ${t.error}`)); } console.log('\n' + '═'.repeat(60)); return results; } // Run tests runTests() .then(results => { process.exit(results.failed > 0 ? 1 : 0); }) .catch(error => { console.error('Fatal error:', error); process.exit(1); });