Files
reporting-governance-plugin/scripts/test_long_task_governor_wrapper.mjs
2026-04-23 08:52:31 +08:00

141 lines
4.5 KiB
JavaScript

#!/usr/bin/env node
import assert from 'node:assert/strict';
import { execFileSync, spawnSync } from 'node:child_process';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const repoRoot = path.resolve(__dirname, '..');
const wrapperPath = path.join(repoRoot, 'scripts', 'long_task_governor_wrapper.mjs');
const fixtures = [
{
name: 'example',
file: path.join(repoRoot, 'docs', '_artifacts', 'long_task_governor_wrapper_example.json'),
assert(output) {
assert.equal(output.classification, 'long_task');
},
},
{
name: 'borderline wrapper inference',
input: {
requestText: 'Inspect the current hook and compare it to the wrapper outputs before replying.',
canReplyNow: false,
},
assert(output) {
assert.equal(output.classification, 'long_task');
assert.equal(output.needsCheckpoint, true);
},
},
{
name: 'invalid silent',
file: path.join(repoRoot, 'docs', '_artifacts', 'long_task_governor_wrapper_invalid_silent_example.json'),
assert(output) {
assert.equal(output.silentLaunchOk, false);
},
},
{
name: 'general chat',
file: path.join(repoRoot, 'docs', '_artifacts', 'long_task_governor_wrapper_general_chat_example.json'),
assert(output) {
assert.equal(output.classification, 'general_chat');
},
},
{
name: 'non-silent long task',
file: path.join(repoRoot, 'docs', '_artifacts', 'long_task_governor_wrapper_non_silent_long_task_example.json'),
assert(output) {
assert.equal(output.classification, 'long_task');
assert.equal(output.silentCandidate, false);
},
},
{
name: 'owner decision',
file: path.join(repoRoot, 'docs', '_artifacts', 'long_task_governor_wrapper_silent_owner_decision_example.json'),
assert(output) {
assert.equal(output.handoff.mode, 'button_path');
},
},
{
name: 'subagent wait',
file: path.join(repoRoot, 'docs', '_artifacts', 'long_task_governor_wrapper_subagent_wait_example.json'),
assert(output) {
assert.equal(output.silentCandidate, true);
},
},
];
function runFixture(fixture) {
const args = fixture.file
? [wrapperPath, '--compact', '--input', fixture.file]
: [wrapperPath, '--compact'];
const options = {
cwd: repoRoot,
encoding: 'utf8',
};
if (fixture.input) {
options.input = `${JSON.stringify(fixture.input)}\n`;
}
const stdout = execFileSync(process.execPath, args, options);
let output;
try {
output = JSON.parse(stdout);
} catch (error) {
throw new Error(`Fixture \"${fixture.name}\" did not produce valid JSON: ${error.message}\nOutput: ${stdout}`);
}
assert.ok(output.classification !== undefined, `${fixture.name}: missing classification`);
assert.ok(output.silentCandidate !== undefined, `${fixture.name}: missing silentCandidate`);
assert.ok(output.silentLaunchOk !== undefined, `${fixture.name}: missing silentLaunchOk`);
assert.ok(output.requiredNextAction !== undefined, `${fixture.name}: missing requiredNextAction`);
assert.ok(output.handoff && output.handoff.mode !== undefined, `${fixture.name}: missing handoff.mode`);
fixture.assert(output);
return {
name: fixture.name,
output,
};
}
function assertErrorCase(name, args, expectedStderr, input) {
const result = spawnSync(process.execPath, [wrapperPath, ...args], {
cwd: repoRoot,
encoding: 'utf8',
input,
});
assert.notEqual(result.status, 0, `${name}: expected non-zero exit`);
assert.equal(result.stdout, '', `${name}: expected empty stdout`);
assert.equal(result.stderr.trim(), expectedStderr, `${name}: unexpected stderr`);
}
function main() {
const results = fixtures.map(runFixture);
assertErrorCase('invalid json', ['--compact'], 'INVALID_JSON: input must be valid JSON', 'not-json\n');
assertErrorCase('missing input value', ['--input'], 'CLI_ERROR: --input requires a value');
assertErrorCase('unknown argument', ['--bogus'], 'CLI_ERROR: unknown argument: --bogus');
const summary = {
passed: results.length,
fixtures: results.map(({ name, output }) => ({
name,
classification: output.classification,
silentCandidate: output.silentCandidate,
silentLaunchOk: output.silentLaunchOk,
requiredNextAction: output.requiredNextAction,
handoffMode: output.handoff.mode,
})),
errorCases: 3,
};
process.stdout.write(JSON.stringify(summary, null, 2) + '\n');
}
main();