Phase 7A+7C: Subsystem aggregator + Flow tracer (post-review fixes)
This commit is contained in:
93
test/test-flow.js
Normal file
93
test/test-flow.js
Normal file
@@ -0,0 +1,93 @@
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const GraphStore = require('../graph.js');
|
||||
const { buildSubsystems } = require('../subsystem.js');
|
||||
const { buildFlowIndex, traceFlow } = require('../flow.js');
|
||||
|
||||
const FIXTURE_DIR = path.join(__dirname, 'fixtures/system-docs');
|
||||
const SNAPSHOT = path.join(FIXTURE_DIR, 'snapshot.json');
|
||||
|
||||
let passed = 0;
|
||||
let failed = 0;
|
||||
|
||||
function assert(condition, name) {
|
||||
if (condition) { passed++; console.log(` ✓ ${name}`); }
|
||||
else { failed++; console.log(` ✗ ${name}`); }
|
||||
}
|
||||
|
||||
const graph = GraphStore.loadSnapshot(SNAPSHOT);
|
||||
const subsystemMap = buildSubsystems(graph, { minTraffic: 3, crossCuttingThreshold: 0.6 });
|
||||
const index = buildFlowIndex(graph, subsystemMap);
|
||||
|
||||
console.log('=== 7C: Flow Tracer Tests ===\n');
|
||||
|
||||
// Test 1: Simple linear flow across subsystems
|
||||
console.log('Test 1: Linear flow');
|
||||
const t1 = traceFlow('channels/telegram.ts:onTelegramMessage', index);
|
||||
assert(t1.subsystemSequence.includes('channels'), 'Started in channels');
|
||||
assert(t1.subsystemSequence.includes('gateway'), 'Crossed to gateway');
|
||||
assert(t1.flow.some(f => f.entity === 'gateway/server.ts:handleRequest' && f.crossedVia === 'CALLS'), 'Recorded CALLS crossing');
|
||||
|
||||
// Test 2: Cycle detection (gateway <-> agents)
|
||||
console.log('\nTest 2: Cycle detection');
|
||||
const t2 = traceFlow('gateway/session.ts:refreshSession', index);
|
||||
assert(t2.subsystemSequence.includes('gateway'), 'Started in gateway');
|
||||
assert(t2.subsystemSequence.includes('agents'), 'Crossed to agents');
|
||||
assert(t2.flow.length < 20, 'Trace terminated without infinite loop');
|
||||
|
||||
// Test 3: God object exclusion
|
||||
console.log('\nTest 3: God object exclusion');
|
||||
const indexLowThreshold = buildFlowIndex(graph, subsystemMap, { godThreshold: 2 });
|
||||
const t3 = traceFlow('channels/telegram.ts:onTelegramMessage', indexLowThreshold);
|
||||
assert(t3.excludedNodes.includes('utils/logger.ts:log'), 'logger.ts:log was excluded as god object');
|
||||
assert(!t3.flow.some(f => f.entity === 'utils/logger.ts:log'), 'logger does not appear in flow');
|
||||
|
||||
// Test 4: Depth limit
|
||||
console.log('\nTest 4: Depth limit');
|
||||
const t4 = traceFlow('channels/telegram.ts:onTelegramMessage', index, { maxDepth: 1.2 });
|
||||
assert(t4.flow.some(f => f.entity === 'gateway/server.ts:handleRequest'), 'Included depth 1 node');
|
||||
assert(!t4.flow.some(f => f.entity === 'gateway/session.ts:loadSession'), 'Excluded depth 1.5 node');
|
||||
|
||||
// Test 5: Empty trace
|
||||
console.log('\nTest 5: Empty trace');
|
||||
const t5 = traceFlow('utils/crypto.ts:hashString', index);
|
||||
assert(t5.flow.length === 1, 'Only entry point in flow');
|
||||
assert(t5.subsystemSequence.length === 1, 'Only one subsystem');
|
||||
|
||||
// Test 6: Invalid entry point
|
||||
console.log('\nTest 6: Invalid entry point');
|
||||
const t6 = traceFlow('nonexistent/file.ts:foo', index);
|
||||
assert(t6.error !== undefined, 'Returns error for invalid entry point');
|
||||
assert(t6.flow.length === 0, 'Empty flow for invalid entry');
|
||||
|
||||
// Test 7: Multiple traces reuse same index (performance)
|
||||
console.log('\nTest 7: Index reuse + OpenClaw performance');
|
||||
const fullSnap = path.join(__dirname, '..', 'snapshots', 'openclaw-full.json');
|
||||
if (fs.existsSync(fullSnap)) {
|
||||
const fullGraph = GraphStore.loadSnapshot(fullSnap);
|
||||
const fullSubs = buildSubsystems(fullGraph);
|
||||
|
||||
const indexStart = Date.now();
|
||||
const fullIndex = buildFlowIndex(fullGraph, fullSubs, { godThreshold: 50 });
|
||||
const indexTime = Date.now() - indexStart;
|
||||
console.log(` Index built in ${indexTime}ms`);
|
||||
|
||||
// Run 3 traces on the same index
|
||||
const entries = ['cli/route.ts:tryRouteCli', 'gateway/session-utils.ts', 'pairing/pairing-store.ts'];
|
||||
let totalTraceTime = 0;
|
||||
for (const ep of entries) {
|
||||
const start = Date.now();
|
||||
const t = traceFlow(ep, fullIndex, { maxDepth: 8 });
|
||||
const elapsed = Date.now() - start;
|
||||
totalTraceTime += elapsed;
|
||||
console.log(` Trace "${ep}": ${t.flow.length} steps, ${t.subsystemSequence.length} subsystems, ${elapsed}ms`);
|
||||
}
|
||||
|
||||
assert(indexTime < 3000, `Index built in <3s (${indexTime}ms)`);
|
||||
assert(totalTraceTime < 5000, `All 3 traces completed in <5s (${totalTraceTime}ms)`);
|
||||
} else {
|
||||
console.log(' (skipped — openclaw-full.json not found)');
|
||||
}
|
||||
|
||||
console.log(`\n=== Results: ${passed} passed, ${failed} failed ===`);
|
||||
process.exit(failed > 0 ? 1 : 0);
|
||||
Reference in New Issue
Block a user