feat(aura-cli, aura-runner): exec routing-seam refusals + by-name unbound-role prose
Harvest sweep, batch 2 of 6.
- exec's file-target classification is now a four-way shape peek
(campaign / blueprint / op-script / wrong-kind): a valid-JSON op-script
array refuses at the routing seam with a build-first pointer ('aura
graph build') instead of the blueprint leg's false 'not valid JSON'
claim, and a kind-bearing non-campaign document names the found kind
plus exec's two executable classes. Exit-code classes unchanged
(op-script usage-class 2, wrong-kind runtime-class 1).
- the bare-tap measurement leg renders CompileError::UnboundRootRole by
the signal's own root-role NAME (captured pre-consumption), retiring
the raw Debug-form leak; every other variant keeps the total fallback.
refs #342
refs #339
This commit is contained in:
@@ -258,6 +258,51 @@ fn exec_malformed_json_file_refuses_neutrally_not_misrouted() {
|
||||
);
|
||||
}
|
||||
|
||||
/// #342 item 3: a valid-JSON op-script (a top-level JSON array of
|
||||
/// construction ops) handed to `exec` must not fall through to the
|
||||
/// blueprint leg's "blueprint document is not valid JSON" refusal — that
|
||||
/// claim is false, the document IS valid JSON, just the wrong shape. It
|
||||
/// refuses right at the routing seam with prose that names the op-script
|
||||
/// diagnosis and points at the build step that turns it into a runnable
|
||||
/// blueprint (`aura graph build`).
|
||||
#[test]
|
||||
fn exec_op_script_file_refuses_with_build_hint_not_invalid_json() {
|
||||
let dir = temp_cwd("exec-op-script");
|
||||
let op_script = r#"[
|
||||
{"op":"source","role":"price","kind":"F64"},
|
||||
{"op":"add","type":"SMA","name":"fast","bind":{"length":{"I64":2}}},
|
||||
{"op":"feed","role":"price","into":["fast.series"]},
|
||||
{"op":"expose","from":"fast.value","as":"bias"}
|
||||
]"#;
|
||||
write_doc(&dir, "script.json", op_script);
|
||||
let (out, code) = run_code_in(&dir, &["exec", "script.json"]);
|
||||
assert_eq!(code, Some(2), "stdout/stderr: {out}");
|
||||
assert!(out.contains("op-script"), "must name the op-script diagnosis: {out}");
|
||||
assert!(out.contains("graph build"), "must point at the build step: {out}");
|
||||
assert!(
|
||||
!out.contains("not valid JSON"),
|
||||
"the document IS valid JSON — must not claim otherwise: {out}"
|
||||
);
|
||||
}
|
||||
|
||||
/// #342 item 4: a kind-bearing document that is not a campaign (here, a
|
||||
/// process document) refuses naming the found kind and exec's two
|
||||
/// executable classes, instead of the campaign leg's generic "the \"kind\"
|
||||
/// key must be \"campaign\"" — which never says WHAT was found, nor what
|
||||
/// exec accepts at all.
|
||||
#[test]
|
||||
fn exec_process_document_names_found_kind_and_executable_classes() {
|
||||
let dir = temp_cwd("exec-wrong-kind-process");
|
||||
write_doc(&dir, "p.process.json", SWEEP_ONLY_PROCESS_DOC);
|
||||
let (out, code) = run_code_in(&dir, &["exec", "p.process.json"]);
|
||||
assert_eq!(code, Some(1), "stdout/stderr: {out}");
|
||||
assert!(out.contains("\"process\""), "must name the found kind: {out}");
|
||||
assert!(
|
||||
out.contains("campaign document") && out.contains("fully-bound blueprint"),
|
||||
"must name both of exec's executable classes: {out}"
|
||||
);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Task 2: the exec blueprint leg (synthetic single run).
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
@@ -2312,10 +2312,9 @@ fn graph_build_accepts_an_open_input_pattern() {
|
||||
/// through `wrap_r` (an existing, unrelated nesting mechanism unaffected by
|
||||
/// this cycle), so a bare-tap (no-`bias`) pattern is used here: its
|
||||
/// `run_measurement` path compiles the signal directly, hitting
|
||||
/// `CompileError::UnboundRootRole` at bootstrap — via the EXISTING `{e:?}`
|
||||
/// Debug rendering (a raw index, not a role name); a name mapping there is
|
||||
/// left for a follow-up (out of this cycle's scope, per the plan's own
|
||||
/// escape hatch), so this pins the existing form.
|
||||
/// `CompileError::UnboundRootRole` at bootstrap — rendered by role NAME
|
||||
/// (#342 item 3), not the raw flat index the earlier form left as a
|
||||
/// follow-up.
|
||||
#[test]
|
||||
fn running_an_open_blueprint_refuses_at_bootstrap() {
|
||||
let doc = r#"[
|
||||
@@ -2333,7 +2332,8 @@ fn running_an_open_blueprint_refuses_at_bootstrap() {
|
||||
assert_eq!(code, Some(1), "an open blueprint refuses standalone at bootstrap, not finish: {stdout} {stderr}");
|
||||
assert!(stdout.is_empty(), "no report emitted on a bootstrap refusal: {stdout}");
|
||||
assert!(
|
||||
stderr.contains("UnboundRootRole"),
|
||||
"the runnability gate (compile), unchanged, names the fault: {stderr}"
|
||||
stderr.contains("root role \"price\" is unbound"),
|
||||
"the runnability gate (compile), unchanged, now names the role, not a raw index: {stderr}"
|
||||
);
|
||||
assert!(!stderr.contains("UnboundRootRole"), "Debug leak: {stderr}");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user