//! End-to-end pins for the self-description surfaces (#315, #323): the help //! opens with the two-layer concept, the sugar verbs name the document shape //! they desugar to, every introspection roster carries per-entry meanings, //! and `graph build --help` carries the op-list reference. Driven over the //! built binary — the zero-setup surface a reader without repo access gets. use std::process::Command; const BIN: &str = env!("CARGO_BIN_EXE_aura"); /// Run `aura ` (no stdin); return (stdout, stderr, success). fn run(args: &[&str]) -> (String, String, bool) { let out = Command::new(BIN).args(args).output().expect("spawn aura"); ( String::from_utf8_lossy(&out.stdout).into_owned(), String::from_utf8_lossy(&out.stderr).into_owned(), out.status.success(), ) } /// #315: `aura --help` opens with the concepts paragraph — the two layers, /// the bias-as-target-position + protective-stop execution model, and how /// traces come to exist. Each pin sits on one authored line of the explicit /// `long_about` string, so clap's wrapping cannot split it. #[test] fn top_level_help_opens_with_the_two_layer_concept() { let (out, err, ok) = run(&["--help"]); assert!(ok, "aura --help failed: {err}"); assert!(out.contains("research verbs"), "names the sugar layer: {out}"); assert!(out.contains("directly authorable"), "names the document data plane: {out}"); assert!(out.contains("bias in [-1,+1]"), "names the bias output: {out}"); assert!(out.contains("defines the risk unit"), "names the protective stop / R: {out}"); assert!(out.contains("introspect --unwired"), "names the bare-{{}} ramp: {out}"); assert!(out.contains("aura chart"), "names the trace consumers: {out}"); } /// #315: each document-bridged sugar verb's long help names the process /// shape it desugars to and points at the document layer. `run` (not /// document-bridged) points at the canonical document-first form instead. #[test] fn sugar_verbs_name_their_document_shape() { for (verb, needle) in [ ("sweep", "std::sweep"), ("walkforward", "std::walk_forward"), ("mc", "std::monte_carlo"), ("generalize", "std::generalize"), ] { let (out, err, ok) = run(&[verb, "--help"]); assert!(ok, "aura {verb} --help failed: {err}"); assert!(out.contains("Sugar"), "{verb} --help names the sugar relation: {out}"); assert!(out.contains(needle), "{verb} --help names {needle}: {out}"); assert!(out.contains("aura campaign"), "{verb} --help points at the documents: {out}"); } let (out, err, ok) = run(&["run", "--help"]); assert!(ok, "aura run --help failed: {err}"); assert!(out.contains("document-first"), "run --help names the canonical form: {out}"); } /// #323: `graph build --help` carries the op-list reference — the op kinds /// and fields are learnable from the binary, not only from serde refusals. #[test] fn graph_build_help_carries_the_op_reference() { let (out, err, ok) = run(&["graph", "build", "--help"]); assert!(ok, "aura graph build --help failed: {err}"); for op in [ r#"{"op":"source""#, r#"{"op":"input""#, r#"{"op":"add""#, r#"{"op":"feed""#, r#"{"op":"connect""#, r#"{"op":"expose""#, r#"{"op":"tap""#, r#"{"op":"gang""#, r#"{"op":"doc""#, ] { assert!(out.contains(op), "op reference carries {op}: {out}"); } assert!(out.contains("graph introspect --vocabulary"), "points at the node roster: {out}"); } /// #315: the node vocabulary listing carries each type's one-line meaning — /// no more bare names that teach nothing. #[test] fn graph_introspect_vocabulary_carries_meanings() { let (out, err, ok) = run(&["graph", "introspect", "--vocabulary"]); assert!(ok, "vocabulary listing failed: {err}"); let sma = out .lines() .find(|l| l.split_whitespace().next() == Some("SMA")) .unwrap_or_else(|| panic!("no SMA line: {out}")); assert!(sma.contains("moving average"), "SMA line carries its meaning: {sma}"); for l in out.lines().filter(|l| !l.trim().is_empty()) { assert!(l.split_whitespace().count() >= 2, "bare name without meaning: {l}"); } } /// #315: `--node `'s head line carries the type's meaning beside its label. #[test] fn graph_introspect_node_head_line_carries_the_meaning() { let (out, err, ok) = run(&["graph", "introspect", "--node", "SMA"]); assert!(ok, "node introspect failed: {err}"); let head = out.lines().next().unwrap_or(""); assert!(head.contains("moving average"), "head line carries the meaning: {out}"); } /// #315: the fold vocabulary is introspectable from the binary — name, bind /// rule, output kind, and meaning per fold (it lived only in source comments). #[test] fn graph_introspect_folds_lists_the_fold_vocabulary() { let (out, err, ok) = run(&["graph", "introspect", "--folds"]); assert!(ok, "folds listing failed: {err}"); let lines: Vec<&str> = out.lines().filter(|l| !l.trim().is_empty()).collect(); assert_eq!(lines.len(), 7, "one line per fold kind: {out}"); let mean = lines.iter().find(|l| l.starts_with("Mean")).expect("Mean row"); assert!(mean.contains("f64") && mean.contains("mean"), "Mean row rules + meaning: {mean}"); let count = lines.iter().find(|l| l.starts_with("Count")).expect("Count row"); assert!(count.contains("i64") && count.contains("any"), "Count row rules: {count}"); } /// #315: the metric roster carries each metric's one-line meaning beside its /// applicability tags. #[test] fn process_introspect_metrics_carries_meanings() { let (out, err, ok) = run(&["process", "introspect", "--metrics"]); assert!(ok, "metric roster failed: {err}"); let er = out .lines() .find(|l| l.split_whitespace().next() == Some("expectancy_r")) .unwrap_or_else(|| panic!("no expectancy_r line: {out}")); assert!(er.contains("mean realized R"), "meaning text on the roster line: {er}"); for l in out.lines().filter(|l| !l.trim().is_empty()) { assert!(l.contains(" — "), "roster line without meaning: {l}"); } } /// #315: the persisted-tap vocabulary's meanings are readable where the taps /// are advertised — under the `std::presentation` block's slot listing. #[test] fn campaign_introspect_block_presentation_lists_tap_meanings() { let (out, err, ok) = run(&["campaign", "introspect", "--block", "std::presentation"]); assert!(ok, "presentation describe failed: {err}"); assert!(out.contains("cumulative pip equity"), "equity tap meaning: {out}"); assert!(out.contains("after the cost model"), "net_r_equity tap meaning: {out}"); } /// #323: `introspect --unwired` enumerates the optional C29 `description` /// slot for both document kinds — the authoring side of the gate is /// advertised where every other slot is. #[test] fn introspect_unwired_lists_the_description_slot() { let dir = std::env::temp_dir().join(format!("aura-selfdesc-{}", std::process::id())); std::fs::create_dir_all(&dir).unwrap(); let bare = dir.join("bare.json"); std::fs::write(&bare, "{}").unwrap(); for family in ["process", "campaign"] { let (out, err, ok) = run(&[family, "introspect", "--unwired", bare.to_str().unwrap()]); assert!(ok, "{family} --unwired failed: {err}"); assert!( out.contains("open slot: description"), "{family} --unwired lists the description slot: {out}" ); assert!(out.contains("C29-gated"), "{family} hint names the gate: {out}"); } let _ = std::fs::remove_dir_all(&dir); }