按 critic + 3 reviewer agent 反馈:
- HIGH: bare catch {} 改 catch(e){console.error(...)} 暴露 bug
- MED: regex 去重复 (develop|develops? → develops?, fix|fixes → fixes?, requirement|requirements? → requirements?)
- MED: 4 处 {continue:true,suppressOutput:true} 抽 NOOP 常量
- MED: test JSON.stringify(.sort()) → 不可变的 sortKey helper
- skip: createHookOutput 抽 lib (违反"不改 OMC keyword-detector" 决策)
- skip: ROLES enum (3 值 over-engineering)
post-deslop 回归: 10/10 PASS
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
69 lines
2.6 KiB
JavaScript
Executable File
69 lines
2.6 KiB
JavaScript
Executable File
#!/usr/bin/env node
|
|
/**
|
|
* role-router unit tests — 10 tests (8 fixture + 2 subprocess integration)
|
|
* 跑法: node role-router.test.mjs
|
|
*/
|
|
|
|
import { detectRoles, buildContext, DEV_RE, TEST_RE, PM_RE } from './role-router.mjs';
|
|
import { execFileSync } from 'child_process';
|
|
import { dirname, join } from 'path';
|
|
import { fileURLToPath } from 'url';
|
|
|
|
const __filename = fileURLToPath(import.meta.url);
|
|
const __dirname = dirname(__filename);
|
|
const HOOK = join(__dirname, 'role-router.mjs');
|
|
|
|
const fixtures = [
|
|
{ prompt: '帮我实现一下登录页面', expect: ['dev'] },
|
|
{ prompt: 'refactor the auth middleware', expect: ['dev'] },
|
|
{ prompt: '跑一下回归测试看看', expect: ['test'] },
|
|
{ prompt: 'write unit tests for the parser', expect: ['test'] },
|
|
{ prompt: '排期一下下周的需求', expect: ['pm'] },
|
|
{ prompt: 'draft the PRD for new chat module', expect: ['pm'] },
|
|
{ prompt: '今天天气怎么样', expect: [] },
|
|
{ prompt: 'PM 要我先调研后排期再让 dev 实现 + 写测试', expect: ['dev', 'test', 'pm'] },
|
|
];
|
|
|
|
let pass = 0, fail = 0;
|
|
const failures = [];
|
|
|
|
const sortKey = a => a.slice().sort().join(',');
|
|
|
|
for (const f of fixtures) {
|
|
const got = detectRoles(f.prompt);
|
|
const ok = sortKey(got) === sortKey(f.expect);
|
|
if (ok) {
|
|
pass++;
|
|
console.log(`PASS "${f.prompt}" → [${got.join(',')}]`);
|
|
} else {
|
|
fail++;
|
|
failures.push({ prompt: f.prompt, expected: f.expect, got });
|
|
console.log(`FAIL "${f.prompt}" expected=[${f.expect.join(',')}] got=[${got.join(',')}]`);
|
|
}
|
|
}
|
|
|
|
// integration: pipe JSON to hook subprocess
|
|
function runHook(promptObj) {
|
|
const input = JSON.stringify(promptObj);
|
|
const out = execFileSync('node', [HOOK], { input, encoding: 'utf8' });
|
|
return JSON.parse(out);
|
|
}
|
|
|
|
console.log('\n--- integration (subprocess pipe) ---');
|
|
const t1 = runHook({ prompt: '帮我修个 bug' });
|
|
const hasContextDev = t1?.hookSpecificOutput?.additionalContext?.includes('role="dev"');
|
|
if (hasContextDev) { pass++; console.log('PASS subprocess injects dev role'); }
|
|
else { fail++; failures.push({prompt:'pipe-dev', got:t1}); console.log('FAIL subprocess dev:', JSON.stringify(t1)); }
|
|
|
|
const t2 = runHook({ prompt: 'hello world' });
|
|
const continued = t2?.continue === true;
|
|
if (continued) { pass++; console.log('PASS no-match passes through'); }
|
|
else { fail++; failures.push({prompt:'pipe-noop', got:t2}); console.log('FAIL no-match:', JSON.stringify(t2)); }
|
|
|
|
console.log(`\nResult: ${pass} pass, ${fail} fail`);
|
|
if (fail > 0) {
|
|
console.error('FAILURES:', JSON.stringify(failures, null, 2));
|
|
process.exit(1);
|
|
}
|
|
process.exit(0);
|