feat(engine): composite doc field — authoring, serde, identity, model emit (refs #125)
Composite gains doc: Option<String> (the prose twin of name, a C23 debug symbol): with_doc/doc() on Composite, a doc knob on GraphBuilder, a Tier-1 additive-optional CompositeData field (no format-version bump; absent-field documents keep their exact bytes), identity-stripped like the name. The graph model emits an optional trailing doc fragment in both scopes; json_str is hardened for free text (\n/\t/\r named, control chars as \u00XX — the doc is the first multi-line value through it). e2e: a hand-authored doc'd blueprint renders with the doc embedded; the doc moves the content id but never the identity id. refs #125
This commit is contained in:
@@ -967,6 +967,79 @@ fn graph_build_gang_op_survives_render_round_trip() {
|
||||
);
|
||||
}
|
||||
|
||||
/// Property (#125, the render seam a hand-authored blueprint document actually
|
||||
/// drives): a composite's `doc` field is not merely a `model_to_json` unit-level
|
||||
/// concept — `aura graph <file>` on a real on-disk blueprint envelope that
|
||||
/// carries `"doc":"..."` embeds that exact text in the page's inlined
|
||||
/// `window.AURA_MODEL` JSON. The graph_model.rs golden pins the JSON fragment in
|
||||
/// isolation; this proves the doc survives the actual file-load -> render CLI
|
||||
/// pipeline a consumer runs, not just the library function called directly.
|
||||
#[test]
|
||||
fn graph_render_embeds_the_hand_authored_composite_doc() {
|
||||
let out = std::process::Command::new(BIN)
|
||||
.arg("graph")
|
||||
.arg(fixture("doc_blueprint.json"))
|
||||
.output()
|
||||
.expect("spawn aura graph <blueprint>");
|
||||
assert_eq!(
|
||||
out.status.code(),
|
||||
Some(0),
|
||||
"render exit: {:?}\nstderr: {}",
|
||||
out.status,
|
||||
String::from_utf8_lossy(&out.stderr)
|
||||
);
|
||||
let html = String::from_utf8(out.stdout).expect("utf-8 stdout");
|
||||
assert!(
|
||||
html.contains(r#","doc":"why this graph: single SMA smoke test""#),
|
||||
"rendered page does not embed the composite doc: {html}"
|
||||
);
|
||||
}
|
||||
|
||||
/// Property (#125, doc-blind identity at the CLI seam): a blueprint document
|
||||
/// carrying a `doc` and its doc-less twin are the SAME topology by the identity
|
||||
/// projection (`--identity-id` agrees) but DIFFERENT documents by the canonical
|
||||
/// projection (`--content-id` differs) — mirroring the established renamed-twin
|
||||
/// pattern (`graph_introspect_identity_id_bridges_renamed_op_scripts`) for the
|
||||
/// new field. `blueprint_serde.rs`'s `doc_round_trips_canonically_and_is_
|
||||
/// identity_blind` pins this at the library level; this drives the real binary
|
||||
/// over a real file, the boundary a regression in the CLI's own dispatch could
|
||||
/// still break even with the library invariant intact.
|
||||
#[test]
|
||||
fn graph_doc_field_moves_content_id_but_not_identity_id() {
|
||||
let doced = fixture("doc_blueprint.json");
|
||||
let doceless_text = std::fs::read_to_string(&doced)
|
||||
.expect("read fixture")
|
||||
.replace(r#""doc":"why this graph: single SMA smoke test","#, "");
|
||||
let dir = temp_cwd("doc-field-ids");
|
||||
let doceless = dir.join("doc_blueprint_no_doc.json");
|
||||
std::fs::write(&doceless, &doceless_text).expect("write doc-less twin");
|
||||
|
||||
// `--identity-id` has no FILE slot of its own (main.rs `GraphIntrospectCmd`):
|
||||
// combine it with `--content-id <FILE>` (both id flags share one build) and
|
||||
// read the two printed lines, content id first (mirrors
|
||||
// `graph_introspect_content_and_identity_id_combine`).
|
||||
let (doced_ids, _e, ok1) =
|
||||
run_in(&dir, &["graph", "introspect", "--content-id", &doced, "--identity-id"]);
|
||||
let (doceless_ids, _e2, ok2) = run_in(
|
||||
&dir,
|
||||
&["graph", "introspect", "--content-id", doceless.to_str().unwrap(), "--identity-id"],
|
||||
);
|
||||
assert_eq!(ok1, Some(0), "doc'd id pair exits 0: {doced_ids}");
|
||||
assert_eq!(ok2, Some(0), "doc-less id pair exits 0: {doceless_ids}");
|
||||
let doced_lines: Vec<&str> = doced_ids.lines().collect();
|
||||
let doceless_lines: Vec<&str> = doceless_ids.lines().collect();
|
||||
assert_eq!(doced_lines.len(), 2, "content id then identity id: {doced_ids:?}");
|
||||
assert_eq!(doceless_lines.len(), 2, "content id then identity id: {doceless_ids:?}");
|
||||
assert_ne!(
|
||||
doced_lines[0], doceless_lines[0],
|
||||
"the doc is canonical-byte-bearing (content id moves)"
|
||||
);
|
||||
assert_eq!(
|
||||
doced_lines[1], doceless_lines[1],
|
||||
"the identity projection is doc-blind (identity id agrees)"
|
||||
);
|
||||
}
|
||||
|
||||
/// The `gang` op's arity refusal (fewer than two members) reads as prose at the
|
||||
/// binary seam, attributed to its op index, never the raw `GangArity` Debug name.
|
||||
#[test]
|
||||
|
||||
Reference in New Issue
Block a user