test(graph_model): cover a producer backing >1 re-exported output field
#47 — close a coverage gap in the graph model serializer: a single interior producer node backing more than one re-exported output field was never exercised by a fixture (every existing one maps each output to a distinct producer or a single field). The issue was filed at cycle 0022 against `aura-cli/src/graph.rs`'s `render_definition` / `output_binding` helper, which rendered a textual `(n1, n2) := <producer>` tuple binding. That file and helper no longer exist — the textual definition renderer was replaced by the JSON model emission now in `aura-engine/src/graph_model.rs`, where the multi-OutField case is plain flat iteration over `c.output()` (one `[name,kind]` entry + one `#N` boundary binding per `OutField`, by `(node, field)`), not a special tuple-binding arm. So the original branch is gone; this guards the flat emission that replaced it. New test `multi_outfield_producer_emits_one_binding_per_field` (+ two fixtures): a two-field producer (`hi`:f64, `lo`:i64 — distinct kinds, so the per-field `field_kind` lookup is exercised) re-exported as both composite outputs, nested in a root so it renders through `composite_def_json`. Asserts both fields surface with their own kind (`"outputs":[["hi","f64"],["lo","i64"]]`) and both bindings reference the same producer at distinct ordinals (`0.o0`/`#0`, `0.o1`/`#1`). Test-only; no production code touched (the behaviour was already correct). Verified: cargo test -p aura-engine green (237 in the lib suite); clippy clean. closes #47
This commit is contained in:
@@ -399,6 +399,49 @@ mod tests {
|
||||
)
|
||||
}
|
||||
|
||||
/// A primitive producer whose output record carries TWO fields of DIFFERENT
|
||||
/// kinds (`hi`: f64, `lo`: i64) — the smallest interior node that can back more
|
||||
/// than one re-exported composite output field, with distinct per-field kinds so
|
||||
/// the `field_kind` lookup is meaningfully exercised (not two identical kinds).
|
||||
fn two_field_producer() -> PrimitiveBuilder {
|
||||
PrimitiveBuilder::new(
|
||||
"Split",
|
||||
NodeSchema {
|
||||
inputs: vec![],
|
||||
output: vec![
|
||||
FieldSpec { name: "hi".into(), kind: ScalarKind::F64 },
|
||||
FieldSpec { name: "lo".into(), kind: ScalarKind::I64 },
|
||||
],
|
||||
params: vec![],
|
||||
},
|
||||
|_| Box::new(Bare),
|
||||
)
|
||||
}
|
||||
|
||||
/// A root harness whose sole interior node is a composite (`splitter`) that
|
||||
/// re-exports BOTH fields of one two-field producer (node 0). Nested in a root
|
||||
/// so `model_to_json` renders `splitter` through the `composite_def_json` path
|
||||
/// (which is what emits the `outputs` list and the `#N` boundary bindings).
|
||||
fn multi_outfield_root() -> Composite {
|
||||
let splitter = Composite::new(
|
||||
"splitter",
|
||||
vec![two_field_producer().into()],
|
||||
vec![],
|
||||
vec![],
|
||||
vec![
|
||||
OutField { node: 0, field: 0, name: "hi".into() },
|
||||
OutField { node: 0, field: 1, name: "lo".into() },
|
||||
],
|
||||
);
|
||||
Composite::new(
|
||||
"root",
|
||||
vec![BlueprintNode::Composite(splitter)],
|
||||
vec![],
|
||||
vec![],
|
||||
vec![],
|
||||
)
|
||||
}
|
||||
|
||||
// -- Task 2: primitive record ------------------------------------------------
|
||||
|
||||
#[test]
|
||||
@@ -518,6 +561,24 @@ mod tests {
|
||||
assert!(def.starts_with(r#"{"inputs":[["f64","any","a"],["f64","any","b"]],"#), "{def}");
|
||||
}
|
||||
|
||||
/// Property: a single interior producer node that backs MORE THAN ONE
|
||||
/// re-exported output field is serialized as one boundary binding per field —
|
||||
/// each `outputs` entry carries that field's OWN kind (`hi`→f64, `lo`→i64, not a
|
||||
/// shared kind), and each `#N` binding endpoint references the SAME producer node
|
||||
/// at a DISTINCT field ordinal (`o0` vs `o1`). Guards the flat per-`OutField`
|
||||
/// model emission (`composite_def_json`) that replaced the retired textual
|
||||
/// `(n1, n2) := <producer>` tuple-binding renderer (#47).
|
||||
#[test]
|
||||
fn multi_outfield_producer_emits_one_binding_per_field() {
|
||||
let model = model_to_json(&multi_outfield_root());
|
||||
// both fields surface, each with its OWN per-field kind (f64 vs i64):
|
||||
assert!(model.contains(r#""outputs":[["hi","f64"],["lo","i64"]]"#), "{model}");
|
||||
// both boundary bindings reference the SAME producer node (0) at distinct
|
||||
// field ordinals — o0 → #0 and o1 → #1:
|
||||
assert!(model.contains(r##"["0.o0","#0"]"##), "{model}");
|
||||
assert!(model.contains(r##"["0.o1","#1"]"##), "{model}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn structural_miswire_changes_the_model() {
|
||||
let ok = model_to_json(&sample_root());
|
||||
|
||||
Reference in New Issue
Block a user