diff --git a/crates/ail/tests/describe_type_scoped_pin.rs b/crates/ail/tests/describe_type_scoped_pin.rs new file mode 100644 index 0000000..10472d3 --- /dev/null +++ b/crates/ail/tests/describe_type_scoped_pin.rs @@ -0,0 +1,63 @@ +//! Pins the canonical type-scoped resolution path of `ail describe`. +//! +//! Property protected: `ail describe` resolves the CANONICAL +//! `.` form for a type-associated operation (model 0007 §1 +//! declares `.` the canonical form; the op lives in the +//! type's home module). For `series_sma`, `Series` is a known TYPE +//! (its `TypeDef` is defined in module `series`), not a module — so +//! `Series.push` must resolve to the SAME `push` fn that the +//! module-scoped `series.push` and the bare `push` already resolve to. +//! +//! Scope: this pins ONLY `Type.op` resolution (issue #64 part 1). It +//! does NOT pin "describe Type lists the type's ops" (part 2). +//! +//! Pure reader: spawns the committed `ail` binary against the +//! committed `examples/series_sma.ail` fixture; mutates nothing. + +use ailang_test_support::examples_dir; +use std::path::PathBuf; +use std::process::Command; + +fn ail_bin() -> PathBuf { + // CARGO_BIN_EXE_ is set by cargo when building integration tests. + PathBuf::from(env!("CARGO_BIN_EXE_ail")) +} + +/// `ail describe --workspace examples/series_sma.ail Series.push` +/// exits 0 and prints the `series.push` function definition — the +/// same def the module-scoped `series.push` resolves to. `Series` is +/// a type defined in module `series`, so the canonical `.` +/// form must resolve through the type's home module. +#[test] +fn describe_resolves_type_scoped_op() { + let fixture = examples_dir().join("series_sma.ail"); + let output = Command::new(ail_bin()) + .args([ + "describe", + "--workspace", + fixture.to_str().unwrap(), + "Series.push", + ]) + .output() + .expect("ail binary must launch"); + + let stdout = String::from_utf8_lossy(&output.stdout); + let stderr = String::from_utf8_lossy(&output.stderr); + + assert!( + output.status.success(), + "describe Series.push must exit 0 (resolve the canonical \ + type-scoped form); exit={:?}\nstdout: {stdout}\nstderr: {stderr}", + output.status.code(), + ); + // Same def the module-scoped `series.push` prints: the home + // module line and the `push` fn header (Form-A projection). + assert!( + stdout.contains("module: series"), + "expected the home-module line `module: series`; got:\n{stdout}" + ); + assert!( + stdout.contains("(fn push"), + "expected the `push` fn definition in the output; got:\n{stdout}" + ); +}