diff --git a/crates/aura-cli/tests/graph_construct.rs b/crates/aura-cli/tests/graph_construct.rs index 05a7090..0f66f24 100644 --- a/crates/aura-cli/tests/graph_construct.rs +++ b/crates/aura-cli/tests/graph_construct.rs @@ -462,6 +462,47 @@ fn graph_register_is_idempotent() { assert_eq!(registered_id(&out1), registered_id(&out2), "same id twice"); } +/// C29 register seam end-to-end (#316): a doc-less root composite refuses at +/// `aura graph register` naming the composite and the rule (exit 1, C14); a +/// doc-less named nested composite refuses naming the nested name; the +/// described shipped example registers. The gate lives at the store write +/// every register path shares — this pins its verb surface. +#[test] +fn register_refuses_undescribed_composites_end_to_end() { + let dir = temp_cwd("register-doc-gate"); + // doc-less root: the shipped example minus its doc member + let text = std::fs::read_to_string(example("r_sma.json")).expect("read example"); + let mut v: serde_json::Value = serde_json::from_str(&text).expect("example parses"); + v["blueprint"].as_object_mut().expect("blueprint object").remove("doc"); + let docless = dir.join("docless.json"); + std::fs::write(&docless, serde_json::to_string(&v).unwrap()).expect("write fixture"); + let (out, err, code) = run_in(&dir, &["graph", "register", docless.to_str().unwrap()]); + assert_eq!(code, Some(1), "doc-less root refuses: {out} {err}"); + assert!( + err.contains("composite `sma_signal` carries no doc"), + "stderr names the composite and the rule: {err}" + ); + assert!(err.contains("C29"), "stderr cites the contract: {err}"); + // doc-less named nested composite inside a described root + v["blueprint"]["doc"] = serde_json::Value::String("a described root".into()); + v["blueprint"]["nodes"] + .as_array_mut() + .expect("nodes array") + .push(serde_json::json!({"composite": {"name": "inner", "nodes": []}})); + let nested = dir.join("nested.json"); + std::fs::write(&nested, serde_json::to_string(&v).unwrap()).expect("write fixture"); + let (out, err, code) = run_in(&dir, &["graph", "register", nested.to_str().unwrap()]); + assert_eq!(code, Some(1), "doc-less nested refuses: {out} {err}"); + assert!( + err.contains("composite `inner` carries no doc"), + "stderr names the nested composite: {err}" + ); + // the described shipped example registers + let (out, err, code) = run_in(&dir, &["graph", "register", &example("r_sma.json")]); + assert_eq!(code, Some(0), "described example registers: {out} {err}"); + assert!(out.starts_with("registered blueprint "), "prints the register line: {out}"); +} + /// Property (#196, the campaign-axis namespace): `--params ` prints the /// RAW composite param space — one `{name}:{kind:?}` line per open param, in /// lowering order, WITHOUT the harness-wrap prefix `aura sweep --list-axes`