diff --git a/scripts/long_task_governor_wrapper.mjs b/scripts/long_task_governor_wrapper.mjs index 45c897c..2aff442 100644 --- a/scripts/long_task_governor_wrapper.mjs +++ b/scripts/long_task_governor_wrapper.mjs @@ -1,12 +1,25 @@ #!/usr/bin/env node import fs from 'fs'; +function fail(code, message) { + process.stderr.write(`${code}: ${message}\n`); + process.exit(1); +} + function parseArgs(argv) { const args = { input: '', pretty: true }; for (let i = 2; i < argv.length; i += 1) { const arg = argv[i]; - if (arg === '--input') args.input = argv[++i] || ''; - else if (arg === '--compact') args.pretty = false; + if (arg === '--input') { + const value = argv[i + 1]; + if (!value || value.startsWith('--')) fail('CLI_ERROR', '--input requires a value'); + args.input = value; + i += 1; + } else if (arg === '--compact') { + args.pretty = false; + } else { + fail('CLI_ERROR', `unknown argument: ${arg}`); + } } return args; } @@ -17,7 +30,13 @@ function readInput(path) { } function normalizeRequest(raw) { - const data = JSON.parse(raw); + let data; + try { + data = JSON.parse(raw); + } catch { + fail('INVALID_JSON', 'input must be valid JSON'); + } + return { requestText: data.requestText || '', hasFilesOrSystems: Boolean(data.hasFilesOrSystems), @@ -155,4 +174,8 @@ function main() { process.stdout.write(JSON.stringify(output, null, args.pretty ? 2 : 0) + '\n'); } -main(); +try { + main(); +} catch (error) { + fail('CLI_ERROR', error && error.message ? error.message : 'unexpected error'); +} diff --git a/scripts/test_long_task_governor_wrapper.mjs b/scripts/test_long_task_governor_wrapper.mjs index af20580..d38bcd1 100644 --- a/scripts/test_long_task_governor_wrapper.mjs +++ b/scripts/test_long_task_governor_wrapper.mjs @@ -1,6 +1,6 @@ #!/usr/bin/env node import assert from 'node:assert/strict'; -import { execFileSync } from 'node:child_process'; +import { execFileSync, spawnSync } from 'node:child_process'; import path from 'node:path'; import { fileURLToPath } from 'node:url'; @@ -102,8 +102,25 @@ function runFixture(fixture) { }; } +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 }) => ({ @@ -114,6 +131,7 @@ function main() { requiredNextAction: output.requiredNextAction, handoffMode: output.handoff.mode, })), + errorCases: 3, }; process.stdout.write(JSON.stringify(summary, null, 2) + '\n');