Files
Aura/crates/aura-cli/tests/project_load.rs
T
Brummel f7c809e5ce refactor(cli): retire the r-sma demo builder — topology now lives only as data (#159 cut 1b)
Cut 1b of the hard-wired demo retirement — the removal half. With the proven
example shipped in cut 1, delete the r-sma builder and its whole transitive
closure so no production function constructs the r-sma topology any more; it
exists only as data (crates/aura-cli/examples/r_sma{,_open}.json).

Deleted: sma_signal (the builder), r_sma_graph, run_r_sma (aura run --harness
r-sma), r_sma_sweep_family (aura sweep --strategy r-sma), the synthetic inline
r-sma walkforward/mc machinery (r_sma_space/r_sma_sweep_over/run_oos_r/
run_mc_r_bootstrap/mc_r_bootstrap_report + the walkforward_family RSma arm), both
fixture emitters, the round-trip + cut-1 byte-fidelity tests, the R_SMA_FAST/
SLOW/BIAS_SCALE consts, and the Strategy::RSma / HarnessKind::RSma / McArgs::RealR
variants. The three dissolved-verb synth sites (generalize/walkforward/mc
--strategy r-sma --real) now embed the shipped open example via include_str!
instead of building it — byte-safe, guarded by the unchanged dissolved-verb
real-archive goldens. The two now-redundant fixtures move to the examples
(deleted, ~40 readers repointed). The --cost-* flags on `aura run` retire with
the harness (their sole consumer was run_r_sma; cost-model coverage survives
engine/std/composites-level).

The retired demo surfaces (--harness r-sma, sweep/walkforward/mc --strategy r-sma
inline) have working data successors: aura run examples/r_sma.json, aura sweep
examples/r_sma_open.json --axis …, and the --strategy r-sma --real campaign path.

Deviation from the plan, noted: StopArm collapsed to StopRule in wrap_r's
signature (its only other variant, Open, died with r_sma_graph) — cleaner than
the planned vestigial single-variant enum; behaviour-preserving, touching
campaign_run.rs and the r-breakout/r-meanrev call sites.

Follow-up (not this cut): wrap_r's cost-node-building branch is now unreachable
(nothing passes Some(CostConfig) after run_r_sma's deletion) yet clippy-clean —
a latent cleanup for when the inline cost surface fully retires.

The stage1-r param-suffix remapping (r_sma_friendly_name + the FAST/SLOW/
STOP_LENGTH/STOP_K_SUFFIX consts) went dead and was removed, closing #167 by
deletion.

Verification (own, not the workflow's report): full `cargo test --workspace`
green; `cargo clippy --workspace --all-targets -- -D warnings` clean, no
dead-code residue; the cut-1 grade anchor (shipped_r_sma_example_reproduces_the_
builtin_grade) and the dissolved-verb real goldens (generalize/walkforward/mc
_dissolves_through_the_campaign_path) stay byte-identical. A first workflow
attempt was discarded after a subagent's `git checkout -- main.rs` recovery
silently reverted earlier tasks' edits (filed on the plugin tracker as
Brummel/Skills#23); this landing is the hardened re-run, verified by hand.

closes #167
refs #159
2026-07-07 17:41:59 +02:00

200 lines
7.5 KiB
Rust

//! E2E: the project-as-crate load boundary (cycle 0102). Builds the fixture
//! project once (cargo, same toolchain, path-dep on this workspace's
//! aura-core), then drives the aura binary from inside the fixture dir.
//! `aura run` persists nothing without --trace, so the tracked fixture stays
//! clean (target/, Cargo.lock, runs/ are fixture-gitignored).
use std::path::{Path, PathBuf};
use std::process::{Command, Output};
use std::sync::OnceLock;
fn fixture_dir() -> PathBuf {
Path::new(env!("CARGO_MANIFEST_DIR")).join("tests/fixtures/demo-project")
}
fn badcharter_dir() -> PathBuf {
Path::new(env!("CARGO_MANIFEST_DIR")).join("tests/fixtures/badcharter-project")
}
fn built_fixture() -> &'static PathBuf {
static BUILT: OnceLock<PathBuf> = OnceLock::new();
BUILT.get_or_init(|| {
let dir = fixture_dir();
let out = Command::new("cargo")
.arg("build")
.current_dir(&dir)
.output()
.expect("spawn cargo build for the fixture project");
assert!(
out.status.success(),
"fixture build failed:\n{}",
String::from_utf8_lossy(&out.stderr)
);
dir
})
}
fn aura(args: &[&str], cwd: &Path) -> Output {
Command::new(env!("CARGO_BIN_EXE_aura"))
.args(args)
.current_dir(cwd)
.output()
.expect("run aura")
}
#[test]
fn project_run_resolves_demo_node_and_is_bit_identical() {
let dir = built_fixture();
let a = aura(&["run", "demo_signal.json"], dir);
assert!(
a.status.success(),
"run failed: {}",
String::from_utf8_lossy(&a.stderr)
);
let b = aura(&["run", "demo_signal.json"], dir);
assert!(b.status.success());
assert_eq!(a.stdout, b.stdout, "two runs must be byte-identical (C1)");
}
#[test]
fn project_run_manifest_carries_provenance() {
let dir = built_fixture();
let out = aura(&["run", "demo_signal.json"], dir);
assert!(out.status.success());
let v: serde_json::Value =
serde_json::from_slice(&out.stdout).expect("run report is JSON");
let p = &v["manifest"]["project"];
assert_eq!(p["namespace"], "demo");
let sha = p["dylib_sha256"].as_str().expect("hash present");
assert_eq!(sha.len(), 64, "sha256 hex");
}
#[test]
fn introspect_vocabulary_lists_project_types() {
let dir = built_fixture();
let out = aura(&["graph", "introspect", "--vocabulary"], dir);
assert!(out.status.success());
let text = String::from_utf8_lossy(&out.stdout);
assert!(text.contains("demo::Identity"), "project id listed");
assert!(text.contains("SMA"), "std ids still listed");
}
/// (#184/#185) Outside any project, `demo::Identity` resolves nowhere: the base
/// failure is house-style prose (never the raw `UnknownNodeType` Debug leak),
/// and — because the unresolved id LOOKS project-namespaced (`::`) while no
/// `Aura.toml` was found up from cwd — the message carries a hint pointing at
/// the missing project, distinguishing "no project" from "genuinely unknown
/// std id" (the bare-id case in `cli_run.rs` gets no such hint).
#[test]
fn outside_a_project_the_demo_blueprint_is_unknown() {
// cwd = the aura-cli crate dir: no Aura.toml anywhere up the tree.
let cwd = Path::new(env!("CARGO_MANIFEST_DIR"));
let bp = fixture_dir().join("demo_signal.json");
let out = aura(&["run", bp.to_str().unwrap()], cwd);
assert!(!out.status.success());
let err = String::from_utf8_lossy(&out.stderr);
assert!(
err.contains(r#"unknown node type "demo::Identity""#),
"house-style prose names the type: {err}"
);
assert!(!err.contains("UnknownNodeType"), "does not leak the Debug variant name: {err}");
assert!(
err.contains("Aura.toml"),
"hints that no project was found for the namespaced id: {err}"
);
}
/// `Env::runs_root()` resolves `Aura.toml`'s `[paths] runs = "runs"` against
/// the DISCOVERED project root (the `Aura.toml` directory found by walking up
/// from cwd), not against the invocation cwd itself: a family recorded while
/// invoking `aura` from a project subdirectory must still land under
/// `<project-root>/runs/`, and no stray `runs/` must appear under the
/// subdirectory. This is the property that makes `[paths]` project-relative
/// rather than shell-relative (C17).
#[test]
fn project_registry_anchors_at_discovered_root_not_invocation_cwd() {
let dir = built_fixture();
let sub = dir.join("src");
let runs_dir = dir.join("runs");
std::fs::remove_dir_all(&runs_dir).ok();
let open_bp =
format!("{}/examples/r_sma_open.json", env!("CARGO_MANIFEST_DIR"));
let out = Command::new(env!("CARGO_BIN_EXE_aura"))
.args([
"sweep",
&open_bp,
"--axis",
"sma_signal.fast.length=2,4",
"--axis",
"sma_signal.slow.length=8,16",
"--name",
"proj-anchor",
])
.current_dir(&sub)
.output()
.expect("spawn aura sweep");
assert!(
out.status.success(),
"sweep failed: {}",
String::from_utf8_lossy(&out.stderr)
);
assert!(
runs_dir.join("families.jsonl").is_file(),
"family record must land at the project root's runs/, not the invocation cwd's"
);
assert!(
!sub.join("runs").exists(),
"no stray runs/ must be created under the invocation subdirectory"
);
std::fs::remove_dir_all(&runs_dir).ok();
}
/// A project whose listed type id is not `<namespace>::`-prefixed is refused
/// (exit 1, cause named) through the REAL load path — `libloading` +
/// descriptor read + `check_charter` wired together in `project::load` — not
/// merely the pure `check_charter` function (already unit-tested in
/// `aura-cli::project`). Pins the "refuse-don't-guess" vocabulary charter as
/// an end-to-end guarantee: a regression that skipped the charter call in the
/// `load()` glue would pass every existing unit test but still be caught here.
#[test]
fn vocabulary_charter_violation_refuses_end_to_end() {
let dir = badcharter_dir();
let build = Command::new("cargo")
.arg("build")
.current_dir(&dir)
.output()
.expect("spawn cargo build for the badcharter fixture");
assert!(
build.status.success(),
"badcharter fixture build failed:\n{}",
String::from_utf8_lossy(&build.stderr)
);
let run = aura(&["run", "x.json"], &dir);
assert_eq!(
run.status.code(),
Some(1),
"a charter violation is a runtime refusal (exit 1), not a usage error"
);
let err = String::from_utf8_lossy(&run.stderr);
assert!(err.contains("lacks the project prefix"), "stderr must name the charter cause: {err}");
}
#[test]
fn missing_artifact_refuses_with_build_hint() {
// A minimal never-built project: valid cargo metadata, no dylib on disk.
let tmp = std::env::temp_dir().join(format!("aura-nobuild-{}", std::process::id()));
std::fs::create_dir_all(tmp.join("src")).unwrap();
std::fs::write(tmp.join("Aura.toml"), "").unwrap();
std::fs::write(tmp.join("src/lib.rs"), "").unwrap();
std::fs::write(
tmp.join("Cargo.toml"),
"[package]\nname = \"nobuild\"\nversion = \"0.1.0\"\nedition = \"2024\"\n\n[lib]\ncrate-type = [\"cdylib\"]\n\n[workspace]\n",
)
.unwrap();
let out = aura(&["run", "x.json"], &tmp);
assert_eq!(out.status.code(), Some(1), "runtime refusal");
let err = String::from_utf8_lossy(&out.stderr);
assert!(err.contains("cargo build"), "build hint present: {err}");
std::fs::remove_dir_all(&tmp).ok();
}