Apply stable CLI failure exit codes
This commit is contained in:
parent
50d298ead7
commit
b808dad0e5
8 changed files with 224 additions and 7 deletions
|
|
@ -14,6 +14,7 @@ node scripts/release-blocker-smoke.js
|
|||
node scripts/resource-metering-contract-smoke.js
|
||||
node scripts/hostile-input-contract-smoke.js
|
||||
node scripts/tenant-isolation-contract-smoke.js
|
||||
node scripts/cli-error-exit-smoke.js
|
||||
scripts/release-source-scan.sh
|
||||
cargo test --manifest-path private/hosted-policy/Cargo.toml
|
||||
node private/hosted-policy/scripts/prepare-public-release-dryrun-deployment.js
|
||||
|
|
|
|||
|
|
@ -39,6 +39,7 @@ cargo build --workspace --bins
|
|||
node scripts/docs-smoke.js
|
||||
node scripts/cli-output-mode-smoke.js
|
||||
node scripts/cli-login-smoke.js
|
||||
node scripts/cli-error-exit-smoke.js
|
||||
node scripts/cli-browser-login-flow-smoke.js
|
||||
node scripts/cli-install-smoke.js
|
||||
node scripts/user-session-token-boundary-smoke.js
|
||||
|
|
|
|||
93
scripts/cli-error-exit-smoke.js
Executable file
93
scripts/cli-error-exit-smoke.js
Executable file
|
|
@ -0,0 +1,93 @@
|
|||
#!/usr/bin/env node
|
||||
|
||||
const assert = require("assert");
|
||||
const cp = require("child_process");
|
||||
const net = require("net");
|
||||
const path = require("path");
|
||||
|
||||
const repo = path.resolve(__dirname, "..");
|
||||
const project = path.join(repo, "examples/launch-build-demo");
|
||||
|
||||
function runDisasmer(args) {
|
||||
return new Promise((resolve) => {
|
||||
const child = cp.spawn(
|
||||
"cargo",
|
||||
["run", "-q", "-p", "disasmer-cli", "--bin", "disasmer", "--", ...args],
|
||||
{
|
||||
cwd: repo,
|
||||
stdio: ["ignore", "pipe", "pipe"],
|
||||
}
|
||||
);
|
||||
let stdout = "";
|
||||
let stderr = "";
|
||||
child.stdout.setEncoding("utf8");
|
||||
child.stderr.setEncoding("utf8");
|
||||
child.stdout.on("data", (chunk) => {
|
||||
stdout += chunk;
|
||||
});
|
||||
child.stderr.on("data", (chunk) => {
|
||||
stderr += chunk;
|
||||
});
|
||||
child.on("close", (code, signal) => {
|
||||
resolve({ code, signal, stdout, stderr });
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async function main() {
|
||||
let request = "";
|
||||
const server = net.createServer((socket) => {
|
||||
socket.setEncoding("utf8");
|
||||
socket.on("data", (chunk) => {
|
||||
request += chunk;
|
||||
if (!request.includes("\n")) return;
|
||||
socket.write(
|
||||
JSON.stringify({
|
||||
type: "error",
|
||||
message: "quota unavailable: resource limit exceeded for api_calls",
|
||||
}) + "\n"
|
||||
);
|
||||
socket.end();
|
||||
server.close();
|
||||
});
|
||||
});
|
||||
|
||||
const address = await new Promise((resolve) => {
|
||||
server.listen(0, "127.0.0.1", () => resolve(server.address()));
|
||||
});
|
||||
const coordinator = `http://${address.address}:${address.port}`;
|
||||
|
||||
const result = await runDisasmer([
|
||||
"run",
|
||||
"build",
|
||||
"--project",
|
||||
project,
|
||||
"--coordinator",
|
||||
coordinator,
|
||||
"--json",
|
||||
]);
|
||||
|
||||
assert.strictEqual(result.signal, null, result.stderr);
|
||||
assert.strictEqual(result.code, 22, result.stderr);
|
||||
assert.match(request, /"type":"start_process"/);
|
||||
const report = JSON.parse(result.stdout);
|
||||
assert.strictEqual(report.status, "coordinator_rejected");
|
||||
assert.strictEqual(report.run_start.machine_error.category, "quota");
|
||||
assert.strictEqual(report.run_start.machine_error.stable_exit_code, 22);
|
||||
assert.strictEqual(
|
||||
report.run_start.machine_error.process_exit_code_applied,
|
||||
true
|
||||
);
|
||||
assert(
|
||||
report.run_start.machine_error.next_actions.includes("disasmer quota status")
|
||||
);
|
||||
}
|
||||
|
||||
main()
|
||||
.then(() => {
|
||||
console.log("CLI error exit smoke passed");
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error(error);
|
||||
process.exit(1);
|
||||
});
|
||||
|
|
@ -34,6 +34,7 @@ const criteria = read("cli_acceptance_criteria.md");
|
|||
const cli = read("crates/disasmer-cli/src/main.rs");
|
||||
const coordinator = read("crates/disasmer-coordinator/src/service.rs");
|
||||
const outputModeSmoke = read("scripts/cli-output-mode-smoke.js");
|
||||
const errorExitSmoke = read("scripts/cli-error-exit-smoke.js");
|
||||
|
||||
expect(criteria, "header", /^# Disasmer CLI-First MVP Acceptance Criteria/m);
|
||||
expect(
|
||||
|
|
@ -116,6 +117,7 @@ for (const [name, pattern] of [
|
|||
["project coordinator status coverage", /fn project_status_queries_public_coordinator_state\(\)/],
|
||||
["run coordinator active-process coverage", /fn run_contacts_configured_coordinator_and_reports_active_process_conflicts\(\)/],
|
||||
["CLI error classifier coverage", /fn cli_error_classifier_distinguishes_mvp_failure_categories\(\)/],
|
||||
["CLI command exit-code coverage", /fn command_report_exit_code_marks_command_failures_only\(\)/],
|
||||
["CLI run rejection category coverage", /fn run_rejection_reports_machine_readable_error_category\(\)/],
|
||||
["node attach grant disclosure coverage", /fn node_attach_discloses_dangerous_capability_grants\(\)/],
|
||||
["quota local status coverage", /fn quota_status_uses_project_config_and_generic_public_limits\(\)/],
|
||||
|
|
@ -240,6 +242,16 @@ expect(
|
|||
"CLI attaches machine errors to task failures",
|
||||
/task_failure_machine_error[\s\S]*cli_error_summary_with_default/
|
||||
);
|
||||
expect(
|
||||
cli,
|
||||
"CLI applies process exit code after printing command failure report",
|
||||
/fn emit_report<T: Serialize>[\s\S]*apply_command_report_exit_code[\s\S]*std::process::exit\(exit_code\)/
|
||||
);
|
||||
expect(
|
||||
cli,
|
||||
"CLI wraps fallible main with classified exit",
|
||||
/fn main\(\)[\s\S]*cli_error_summary\(&message\)[\s\S]*std::process::exit\(exit_code\)/
|
||||
);
|
||||
expect(
|
||||
cli,
|
||||
"CLI parses dangerous capability overrides",
|
||||
|
|
@ -269,4 +281,13 @@ for (const [name, pattern] of [
|
|||
expect(outputModeSmoke, name, pattern);
|
||||
}
|
||||
|
||||
for (const [name, pattern] of [
|
||||
["fake coordinator quota rejection", /quota unavailable: resource limit exceeded for api_calls/],
|
||||
["run uses JSON mode", /"run"[\s\S]*"--json"/],
|
||||
["actual quota exit code", /assert\.strictEqual\(result\.code, 22/],
|
||||
["exit-code application in JSON", /process_exit_code_applied[\s\S]*true/],
|
||||
]) {
|
||||
expect(errorExitSmoke, name, pattern);
|
||||
}
|
||||
|
||||
console.log("CLI-first contract smoke passed");
|
||||
|
|
|
|||
|
|
@ -166,6 +166,10 @@ for (const script of [publicAcceptance, publicSplit]) {
|
|||
script.includes("node scripts/cli-login-smoke.js"),
|
||||
"public acceptance gates must run cli-login-smoke.js"
|
||||
);
|
||||
assert(
|
||||
script.includes("node scripts/cli-error-exit-smoke.js"),
|
||||
"public acceptance gates must run cli-error-exit-smoke.js"
|
||||
);
|
||||
assert(
|
||||
script.includes("node scripts/acceptance-report-smoke.js"),
|
||||
"public acceptance gates must run acceptance-report-smoke.js"
|
||||
|
|
|
|||
|
|
@ -48,6 +48,7 @@ fi
|
|||
(cd "$tmp_dir" && node scripts/docs-smoke.js)
|
||||
(cd "$tmp_dir" && node scripts/cli-output-mode-smoke.js)
|
||||
(cd "$tmp_dir" && node scripts/cli-login-smoke.js)
|
||||
(cd "$tmp_dir" && node scripts/cli-error-exit-smoke.js)
|
||||
(cd "$tmp_dir" && node scripts/cli-browser-login-flow-smoke.js)
|
||||
(cd "$tmp_dir" && node scripts/cli-install-smoke.js)
|
||||
(cd "$tmp_dir" && node scripts/user-session-token-boundary-smoke.js)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue