Files
alkgen/tests/code_fixtures.rs
T
glm-5.3-flash 14d8a49611 test(fixtures): wave-1 engine fixture doubles (ssr-shaped and code-shaped)
- ssr double: page template composing header/footer partials under the
  HTML escaper, typed context built by a builder fn per ADR-004; golden
  pins for composition, escaping of hostile interpolation, the trusted
  {{& }} slot, and render-by-partial-name rejection
- code double: struct-writer template under the no-op escaper, typed
  context built from a schema document with kind mapping and
  source-escaped literals in the builder; golden pins for document-order
  arrays and the active preserve_order feature
- fixture assets are include_str! files under tests/fixtures/; fixture
  modules are per-test-binary so wave-2 proofs can grow them
- task file updated to completed with Notes and Summary

Verification: cargo test green (19 lib + 8 fixture tests), clippy
-D warnings clean, cargo fmt --check clean
2026-11-06 20:05:42 +00:00

253 lines
8.0 KiB
Rust

//! The code-shaped engine fixture double (posture-A/C shape): a
//! struct-writer template over a typed context built from a schema
//! document by a Rust builder, with a no-op escaper and
//! document-ordered arrays, pinned by golden renders.
use alkgen::{EscapePolicy, TemplateSet};
use serde::Serialize;
mod fixtures {
pub const STRUCT_WRITER: &str = include_str!("fixtures/code/struct-writer.hbs");
pub const SCHEMA: &str = include_str!("fixtures/code/schema.json");
}
#[derive(Serialize)]
struct StructWriterContext {
struct_name: String,
fields: Vec<WriterField>,
default_consts: Vec<DefaultConst>,
}
#[derive(Serialize)]
struct WriterField {
name: String,
writer_call: String,
}
#[derive(Serialize)]
struct DefaultConst {
const_name: String,
literal: String,
}
/// The schema-kind to writer-fn mapping, one of the ADR-004 kind
/// mappings: the template renders whatever call name the builder
/// decided and never interprets a schema kind itself.
fn writer_call(schema_kind: &str) -> String {
match schema_kind {
"u32" => "write_u32".to_owned(),
"u64" => "write_u64".to_owned(),
"string" => "write_string".to_owned(),
"bool" => "write_bool".to_owned(),
other => panic!("fixture schema must only carry known kinds, found: {other}"),
}
}
/// The Rust-type-name mapping (snake_case document name to
/// PascalCase type): another ADR-004 decision living in builder code.
fn rust_struct_name(doc_name: &str) -> String {
doc_name
.split('_')
.map(|part| {
let mut chars = part.chars();
match chars.next() {
Some(first) => first.to_uppercase().collect::<String>() + chars.as_str(),
None => String::new(),
}
})
.collect()
}
/// Rust source-level string-literal quoting: the codegen posture
/// escapes literals at the source (the builder), never by entity
/// mangling, which is why the code set renders with the no-op escaper.
fn rust_string_literal(raw: &str) -> String {
let mut literal = String::new();
literal.push('"');
for c in raw.chars() {
match c {
'"' => literal.push_str("\\\""),
'\\' => literal.push_str("\\\\"),
_ => literal.push(c),
}
}
literal.push('"');
literal
}
/// The ADR-004 context builder over the schema document: fields stay
/// in the document's array order (convention 5), string defaults are
/// builder-quoted into a `const`, and the struct name is mapped to the
/// Rust spelling, all in Rust.
fn build_struct_writer(doc: &serde_json::Value) -> StructWriterContext {
let struct_name = doc
.get("name")
.and_then(serde_json::Value::as_str)
.expect("fixture schema should carry a struct name");
let fields_doc = doc
.get("fields")
.and_then(serde_json::Value::as_array)
.expect("fixture schema should carry a fields array");
let mut fields = Vec::new();
let mut default_consts = Vec::new();
for field in fields_doc {
let name = field
.get("name")
.and_then(serde_json::Value::as_str)
.expect("fixture schema field should carry a name");
let kind = field
.get("kind")
.and_then(serde_json::Value::as_str)
.expect("fixture schema field should carry a kind");
fields.push(WriterField {
name: name.to_owned(),
writer_call: writer_call(kind),
});
if let Some(default) = field.get("default").and_then(serde_json::Value::as_str) {
default_consts.push(DefaultConst {
const_name: format!("{}_DEFAULT", name.to_uppercase()),
literal: rust_string_literal(default),
});
}
}
StructWriterContext {
struct_name: rust_struct_name(struct_name),
fields,
default_consts,
}
}
/// The code-shaped fixture set: one `struct-writer` template kind
/// under the no-op escaper.
fn code_fixture_set() -> TemplateSet {
let mut set = TemplateSet::new(EscapePolicy::NoOp);
set.register_template("struct-writer", fixtures::STRUCT_WRITER)
.expect("fixture struct-writer template should be well-formed");
set
}
fn golden_context() -> StructWriterContext {
let doc: serde_json::Value =
serde_json::from_str(fixtures::SCHEMA).expect("fixture schema should parse as JSON");
build_struct_writer(&doc)
}
const GOLDEN_STRUCT_WRITER: &str = concat!(
"pub const LABEL_DEFAULT: &str = \"Say \\\"<b>&\\\" hi\";\n",
"impl SampleRecord {\n",
" pub fn write<W: std::io::Write>(&self, out: &mut W) -> std::io::Result<()> {\n",
" out.write_u32(&self.magic)?;\n",
" out.write_u64(&self.length)?;\n",
" out.write_string(&self.label)?;\n",
" out.write_bool(&self.active)?;\n",
" Ok(())\n",
" }\n",
"}\n",
);
#[test]
fn struct_writer_golden_render_pins_kind_mapping_and_literals() {
let set = code_fixture_set();
let rendered = set
.render("struct-writer", &golden_context())
.expect("the golden struct-writer should render");
assert_eq!(rendered, GOLDEN_STRUCT_WRITER);
}
#[test]
fn noop_escaping_passes_source_escaped_literals_verbatim() {
let set = code_fixture_set();
let rendered = set
.render("struct-writer", &golden_context())
.expect("the struct-writer should render");
assert!(
rendered.contains(r#""Say \"<b>&\" hi""#),
"the builder-quoted literal must pass through the no-op escaper verbatim"
);
assert!(
!rendered.contains("&lt;"),
"the HTML escaper would mangle the literal; the code set must never entity-escape"
);
}
#[test]
fn document_order_arrays_render_in_insertion_order() {
let set = code_fixture_set();
let rendered = set
.render("struct-writer", &golden_context())
.expect("the struct-writer should render");
let magic = rendered
.find("write_u32")
.expect("magic line should render");
let length = rendered
.find("write_u64")
.expect("length line should render");
let label = rendered
.find("write_string")
.expect("label line should render");
let active = rendered
.find("write_bool")
.expect("active line should render");
assert!(magic < length);
assert!(length < label);
assert!(label < active);
}
#[test]
fn insertion_order_drives_render_order_not_sorting() {
let set = code_fixture_set();
let mut doc: serde_json::Value =
serde_json::from_str(fixtures::SCHEMA).expect("fixture schema should parse as JSON");
let fields = doc
.get_mut("fields")
.and_then(serde_json::Value::as_array_mut)
.expect("fixture schema should carry a fields array");
fields.sort_by_key(|field| {
field
.get("name")
.and_then(serde_json::Value::as_str)
.map(str::to_owned)
.unwrap_or_default()
});
let rendered = set
.render("struct-writer", &build_struct_writer(&doc))
.expect("the reordered struct-writer should render");
let active = rendered
.find("write_bool")
.expect("active line should render");
let label = rendered
.find("write_string")
.expect("label line should render");
let length = rendered
.find("write_u64")
.expect("length line should render");
let magic = rendered
.find("write_u32")
.expect("magic line should render");
assert!(active < label);
assert!(label < length);
assert!(length < magic);
}
#[test]
fn serde_json_preserve_order_is_active_in_the_dep_set() {
let mut map = serde_json::Map::new();
map.insert("beta".to_owned(), serde_json::json!(2));
map.insert("alpha".to_owned(), serde_json::json!(1));
let value = serde_json::Value::Object(map);
assert_eq!(
value.to_string(),
r#"{"beta":2,"alpha":1}"#,
"object insertion order must survive serialization; disabling the preserve_order feature sorts keys and fails this pin"
);
}