Phase 7A+7C: Subsystem aggregator + Flow tracer (post-review fixes)
This commit is contained in:
109
test/test-subsystem.js
Normal file
109
test/test-subsystem.js
Normal file
@@ -0,0 +1,109 @@
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const GraphStore = require('../graph.js');
|
||||
const { buildSubsystems } = require('../subsystem.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}`); }
|
||||
}
|
||||
|
||||
// Load graph and run subsystem aggregator
|
||||
const graph = GraphStore.loadSnapshot(SNAPSHOT);
|
||||
const result = buildSubsystems(graph, { minTraffic: 3, crossCuttingThreshold: 0.6 });
|
||||
const expected = JSON.parse(fs.readFileSync(path.join(FIXTURE_DIR, 'expected-subsystems.json'), 'utf8'));
|
||||
|
||||
console.log('=== 7A: Subsystem Aggregator Tests ===\n');
|
||||
|
||||
// Test 1: Directory clustering — correct number of subsystems
|
||||
console.log('Test 1: Directory clustering');
|
||||
assert(result.subsystems.length === 5, `Found 5 subsystems (got ${result.subsystems.length})`);
|
||||
const names = result.subsystems.map(s => s.name).sort();
|
||||
assert(JSON.stringify(names) === JSON.stringify(['agents', 'channels', 'config', 'gateway', 'utils']),
|
||||
`Subsystem names match: ${names.join(', ')}`);
|
||||
|
||||
// Test 2: File assignment accuracy
|
||||
console.log('\nTest 2: File assignment accuracy');
|
||||
let correctFiles = 0;
|
||||
let totalFiles = 0;
|
||||
for (const expSub of expected.subsystems) {
|
||||
const actualSub = result.subsystems.find(s => s.name === expSub.name);
|
||||
assert(!!actualSub, `Subsystem "${expSub.name}" exists`);
|
||||
if (actualSub) {
|
||||
for (const f of expSub.files) {
|
||||
totalFiles++;
|
||||
if (actualSub.files.includes(f)) correctFiles++;
|
||||
else console.log(` MISSING: ${f} in ${expSub.name}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
const accuracy = totalFiles > 0 ? (correctFiles / totalFiles * 100).toFixed(1) : 0;
|
||||
assert(correctFiles / totalFiles >= 0.9, `File assignment accuracy ≥90% (${accuracy}%, ${correctFiles}/${totalFiles})`);
|
||||
|
||||
// Test 3: Cross-cutting detection
|
||||
console.log('\nTest 3: Cross-cutting detection');
|
||||
assert(result.crossCutting.includes('utils'), 'utils detected as cross-cutting');
|
||||
assert(result.crossCutting.includes('config'), 'config detected as cross-cutting');
|
||||
assert(!result.crossCutting.includes('gateway'), 'gateway is NOT cross-cutting');
|
||||
assert(!result.crossCutting.includes('agents'), 'agents is NOT cross-cutting');
|
||||
assert(!result.crossCutting.includes('channels'), 'channels is NOT cross-cutting');
|
||||
|
||||
// Verify kind field matches
|
||||
for (const expSub of expected.subsystems) {
|
||||
const actualSub = result.subsystems.find(s => s.name === expSub.name);
|
||||
if (actualSub) {
|
||||
assert(actualSub.kind === expSub.kind, `${expSub.name} kind="${actualSub.kind}" (expected "${expSub.kind}")`);
|
||||
}
|
||||
}
|
||||
|
||||
// Test 4: Dependency matrix — key edges exist
|
||||
console.log('\nTest 4: Dependency matrix');
|
||||
const dm = result.dependencyMatrix;
|
||||
assert('gateway→agents' in dm, 'gateway→agents edge exists');
|
||||
assert('agents→gateway' in dm, 'agents→gateway edge exists (circular dep)');
|
||||
assert('channels→gateway' in dm, 'channels→gateway edge exists');
|
||||
assert('gateway→config' in dm, 'gateway→config edge exists');
|
||||
assert('agents→utils' in dm, 'agents→utils edge exists');
|
||||
assert('channels→utils' in dm, 'channels→utils edge exists');
|
||||
|
||||
// Test 5: Empty subsystem handling (channels has only 2 files, no internal CALLS)
|
||||
console.log('\nTest 5: Edge cases');
|
||||
const channelsSub = result.subsystems.find(s => s.name === 'channels');
|
||||
assert(channelsSub && channelsSub.files.length === 2, `channels has 2 files (got ${channelsSub?.files.length})`);
|
||||
|
||||
// Orphan file: config/schema.ts should still be in config subsystem
|
||||
const configSub = result.subsystems.find(s => s.name === 'config');
|
||||
assert(configSub && configSub.files.includes('config/schema.ts'), 'Orphan config/schema.ts assigned to config');
|
||||
|
||||
// Test 6: Public exports populated
|
||||
console.log('\nTest 6: Public exports');
|
||||
const gatewaySub = result.subsystems.find(s => s.name === 'gateway');
|
||||
assert(gatewaySub.publicExports.includes('handleRequest'), 'gateway exports handleRequest');
|
||||
assert(gatewaySub.publicExports.includes('loadSession'), 'gateway exports loadSession');
|
||||
const agentsSub = result.subsystems.find(s => s.name === 'agents');
|
||||
assert(agentsSub.publicExports.includes('runAgent'), 'agents exports runAgent');
|
||||
|
||||
// Test 7: Run on OpenClaw full snapshot (performance)
|
||||
console.log('\nTest 7: OpenClaw full snapshot');
|
||||
const fullSnap = path.join(__dirname, '..', 'snapshots', 'openclaw-full.json');
|
||||
if (fs.existsSync(fullSnap)) {
|
||||
const start = Date.now();
|
||||
const fullGraph = GraphStore.loadSnapshot(fullSnap);
|
||||
const fullResult = buildSubsystems(fullGraph);
|
||||
const elapsed = Date.now() - start;
|
||||
assert(fullResult.subsystems.length > 10, `OpenClaw has >10 subsystems (got ${fullResult.subsystems.length})`);
|
||||
assert(elapsed < 5000, `Completed in <5s (${elapsed}ms)`);
|
||||
assert(fullResult.crossCutting.length > 0, `Detected cross-cutting subsystems: ${fullResult.crossCutting.join(', ')}`);
|
||||
console.log(` OpenClaw: ${fullResult.subsystems.length} subsystems, ${Object.keys(fullResult.dependencyMatrix).length} dep edges, ${elapsed}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