diff --git a/crates/aura-cli/tests/graph_construct.rs b/crates/aura-cli/tests/graph_construct.rs index d879762..8a76d60 100644 --- a/crates/aura-cli/tests/graph_construct.rs +++ b/crates/aura-cli/tests/graph_construct.rs @@ -1214,3 +1214,58 @@ fn graph_build_gang_on_a_bound_member_reports_unknown_param() { "the bound member reads as an ordinary unknown param, not a gang-specific fault: {stderr}" ); } + +/// Like `run_in`, but also pipes `stdin_doc` to the child — the combination +/// `aura graph build` needs, since it reads its op-script from stdin AND must +/// discover the project from the working directory. Neither `run` (stdin, no +/// cwd) nor `run_in` (cwd, no stdin) covers both. +fn run_in_stdin( + dir: &std::path::Path, + args: &[&str], + stdin_doc: &str, +) -> (String, String, Option) { + let mut child = Command::new(BIN) + .args(args) + .current_dir(dir) + .stdin(Stdio::piped()) + .stdout(Stdio::piped()) + .stderr(Stdio::piped()) + .spawn() + .expect("spawn aura"); + child.stdin.take().unwrap().write_all(stdin_doc.as_bytes()).unwrap(); + let out = child.wait_with_output().expect("wait aura"); + ( + String::from_utf8_lossy(&out.stdout).into_owned(), + String::from_utf8_lossy(&out.stderr).into_owned(), + out.status.code(), + ) +} + +/// Property (#244): the tier-aware unresolved-namespace hint fires on the +/// op-script `aura graph build` path exactly as it does on the blueprint LOAD +/// path (`project_load.rs::data_only_project_hints_attach_for_namespaced_ids`, +/// which this mirrors). Inside a data-only project (a bare `Aura.toml`, no node +/// crate attached), an op-script referencing a project-namespaced type +/// (`nosuch::Node`) the vocabulary cannot resolve refuses non-zero AND the +/// refusal carries the tier hint — "binds no node crate" + the `aura nodes new` +/// attach verb — not the bare unknown-type error. Same consumer mistake, same +/// diagnostic quality across both entry surfaces. (The refusal/non-zero exit +/// already works today; the hint text is the absent behaviour.) +#[test] +fn graph_build_hints_attach_for_namespaced_ids_in_a_data_only_project() { + let dir = temp_cwd("graph-build-dataonly-hint"); + std::fs::write(dir.join("Aura.toml"), "").expect("write bare Aura.toml"); + // A single op is enough: `replay` fails fast at the unknown-type `add`. + let ops = r#"[{"op":"add","type":"nosuch::Node","name":"n"}]"#; + let (stdout, stderr, code) = run_in_stdin(&dir, &["graph", "build"], ops); + assert_ne!(code, Some(0), "an unresolvable namespaced type refuses: {stderr}"); + assert!(stdout.is_empty(), "no blueprint emitted on a fault: {stdout}"); + assert!( + stderr.contains("binds no node crate"), + "the data-only tier hint fires on the graph-build path: {stderr}" + ); + assert!( + stderr.contains("aura nodes new"), + "the hint points at the attach verb: {stderr}" + ); +}