- full consumer-shaped fixture set registered from the test binary via
register_template/register_partial only (ADR-008 machinery equality
in three caller-supplied text shapes: fixtures, runtime String,
inline literal)
- page composes fragment partials; the card fragment registers as its
own template, renders directly by name, and composes via include
- exact goldens pin html-escape posture (hostile data escaped, = and '
escapes, raw-output {{& }} slot verbatim)
- strict-mode render failure maps consumer-side to an error page built
from the typed Error's data, panic-free; source chain reachable
- determinism pinned across repeats, registry rebuilds, fresh
contexts, and both sinks (byte-identical, ADR-003)
- posture-B hygiene rows: no banner, no filesystem; no core changes
required (stop-and-book: nothing to book)
309 lines
11 KiB
Rust
309 lines
11 KiB
Rust
//! The wave-2 ssr-html proof (posture B, consumer-shaped): a full
|
|
//! fixture set modeled on the alkforge posture, rendered entirely through
|
|
//! the public API and pinned by goldens. Where the wave-1 double proved
|
|
//! the machinery minimally, this proof walks the ssr-html target's real
|
|
//! shape at fuller scale: if the shipped core serves this set without a
|
|
//! core change, ADR-001's generality claim holds on shipped code.
|
|
|
|
use std::error::Error as StdError;
|
|
|
|
use alkgen::{Error, EscapePolicy, TemplateSet};
|
|
use serde::Serialize;
|
|
use serde_json::json;
|
|
|
|
mod fixtures {
|
|
pub const PAGE: &str = include_str!("fixtures/ssr_proof/page.hbs");
|
|
pub const HEADER: &str = include_str!("fixtures/ssr_proof/header.hbs");
|
|
pub const NAV: &str = include_str!("fixtures/ssr_proof/nav.hbs");
|
|
pub const FOOTER: &str = include_str!("fixtures/ssr_proof/footer.hbs");
|
|
pub const SETTINGS_PAGE: &str = include_str!("fixtures/ssr_proof/settings_page.hbs");
|
|
}
|
|
|
|
/// The caller-supplied error page the failed render maps toward: built
|
|
/// on the consumer side from the typed error's data, never from a core
|
|
/// path.
|
|
const ERROR_PAGE: &str = "<!doctype html>\n<html lang=\"en\">\n<body><h1>Something went wrong</h1><p class=\"detail\">{{message}}</p></body>\n</html>\n";
|
|
|
|
#[derive(Serialize)]
|
|
struct NavLink {
|
|
href: String,
|
|
label: String,
|
|
}
|
|
|
|
#[derive(Serialize)]
|
|
struct Card {
|
|
kind: String,
|
|
title: String,
|
|
body: String,
|
|
}
|
|
|
|
#[derive(Serialize)]
|
|
struct PageContext {
|
|
site_name: String,
|
|
tagline: String,
|
|
nav_links: Vec<NavLink>,
|
|
cards: Vec<Card>,
|
|
show_banner: bool,
|
|
banner_text: String,
|
|
trusted_embed: String,
|
|
year: u16,
|
|
}
|
|
|
|
fn nav_link(href: &str, label: &str) -> NavLink {
|
|
NavLink {
|
|
href: href.to_owned(),
|
|
label: label.to_owned(),
|
|
}
|
|
}
|
|
|
|
fn card(kind: &str, title: &str, body: &str) -> Card {
|
|
Card {
|
|
kind: kind.to_owned(),
|
|
title: title.to_owned(),
|
|
body: body.to_owned(),
|
|
}
|
|
}
|
|
|
|
fn context_with(show_banner: bool, banner_text: &str) -> PageContext {
|
|
PageContext {
|
|
site_name: "Alk Forge".into(),
|
|
tagline: "Your repositories".into(),
|
|
nav_links: vec![
|
|
nav_link("/", "Home"),
|
|
nav_link("/search?q=<script>&lang=en", "<script>alert('x')</script>"),
|
|
],
|
|
cards: vec![
|
|
card(
|
|
"repo",
|
|
"Repo \"settings\" & <config>",
|
|
"if (a < b) { alert(\"<b>hi</b>\") }",
|
|
),
|
|
card("link", "Plain card", "A plain body."),
|
|
],
|
|
show_banner,
|
|
banner_text: banner_text.to_owned(),
|
|
trusted_embed: r#"<aside id="notice"><b>system notice</b></aside>"#.into(),
|
|
year: 2026,
|
|
}
|
|
}
|
|
|
|
/// The card fragment's template text arrives as a consumer-built
|
|
/// `String`: the caller owns the text however it wants (inline literal,
|
|
/// runtime-built, or fixture file) and registers it through the same
|
|
/// API engine-owned sets use (ADR-008 machinery equality).
|
|
fn caller_supplied_card_text() -> String {
|
|
let mut text = String::from("<article class=\"card\" data-kind=\"{{kind}}\">");
|
|
text.push_str("<h3>{{title}}</h3>");
|
|
text.push_str("<p>{{body}}</p>");
|
|
text.push_str("</article>");
|
|
text
|
|
}
|
|
|
|
/// The full consumer-shaped fixture set: pages, fragments, and the
|
|
/// error page all register from the test binary through the public API.
|
|
/// Partials exist to be composed; fragments a consumer serves directly
|
|
/// register as their own templates.
|
|
fn consumer_fixture_set() -> TemplateSet {
|
|
let mut set = TemplateSet::new(EscapePolicy::Html);
|
|
set.register_template("page", fixtures::PAGE)
|
|
.expect("fixture page template should be well-formed");
|
|
set.register_template("card", &caller_supplied_card_text())
|
|
.expect("caller-supplied card template should be well-formed");
|
|
set.register_template("settings_page", fixtures::SETTINGS_PAGE)
|
|
.expect("fixture settings page template should be well-formed");
|
|
set.register_template("error_page", ERROR_PAGE)
|
|
.expect("caller-supplied error page template should be well-formed");
|
|
set.register_partial("header", fixtures::HEADER)
|
|
.expect("fixture header partial should be well-formed");
|
|
set.register_partial("nav", fixtures::NAV)
|
|
.expect("fixture nav partial should be well-formed");
|
|
set.register_partial("footer", fixtures::FOOTER)
|
|
.expect("fixture footer partial should be well-formed");
|
|
set
|
|
}
|
|
|
|
const GOLDEN_PAGE: &str = concat!(
|
|
"<!doctype html>\n",
|
|
"<html lang=\"en\">\n",
|
|
"<header><h1>Alk Forge</h1><p class=\"tagline\">Your repositories</p></header>\n",
|
|
"<body>\n",
|
|
"<nav><ul><li><a href=\"/\">Home</a></li><li><a href=\"/search?q=<script>&lang=en\"><script>alert('x')</script></a></li></ul></nav>\n",
|
|
"<main>\n",
|
|
"<article class=\"card\" data-kind=\"repo\"><h3>Repo "settings" & <config></h3><p>if (a < b) { alert("<b>hi</b>") }</p></article><article class=\"card\" data-kind=\"link\"><h3>Plain card</h3><p>A plain body.</p></article>\n</main>\n",
|
|
"<div class=\"banner\"><em>scheduled</em> maintenance</div>\n",
|
|
"<aside id=\"notice\"><b>system notice</b></aside>\n",
|
|
"<footer><p>© 2026 Alk Forge</p></footer>\n",
|
|
"</body>\n",
|
|
"</html>\n",
|
|
);
|
|
|
|
const GOLDEN_CARD: &str = concat!(
|
|
"<article class=\"card\" data-kind=\"repo\">",
|
|
"<h3>Repo "settings" & <config></h3>",
|
|
"<p>if (a < b) { alert("<b>hi</b>") }</p>",
|
|
"</article>",
|
|
);
|
|
|
|
const GOLDEN_ERROR_PAGE: &str = concat!(
|
|
"<!doctype html>\n",
|
|
"<html lang=\"en\">\n",
|
|
"<body><h1>Something went wrong</h1><p class=\"detail\">render failed: Error rendering "settings_page" line 5, col 48: Failed to access variable in strict mode Some("account.email")</p></body>\n",
|
|
"</html>\n",
|
|
);
|
|
|
|
#[test]
|
|
fn page_golden_pins_composition_escaping_and_raw_slot_at_fuller_scale() {
|
|
let set = consumer_fixture_set();
|
|
let context = context_with(true, "<em>scheduled</em> maintenance");
|
|
|
|
let rendered = set
|
|
.render("page", &context)
|
|
.expect("the full consumer fixture set should render through the public API");
|
|
assert_eq!(rendered, GOLDEN_PAGE);
|
|
|
|
assert!(
|
|
rendered.contains(r#"<a href="/search?q=<script>&lang=en"><script>alert('x')</script></a>"#),
|
|
"hostile interpolated data must be entity-escaped"
|
|
);
|
|
assert!(
|
|
rendered.contains(r#"<h3>Repo "settings" & <config></h3>"#),
|
|
"hostile card data must be entity-escaped everywhere it interpolates"
|
|
);
|
|
assert!(
|
|
rendered.contains(r#"<aside id="notice"><b>system notice</b></aside>"#),
|
|
"the deliberate raw-output slot ({{& }}) must pass through verbatim"
|
|
);
|
|
assert!(
|
|
rendered.starts_with("<!doctype html>"),
|
|
"the output unit is the served page, unadorned"
|
|
);
|
|
assert!(
|
|
!rendered.contains("DO NOT EDIT"),
|
|
"posture-B hygiene: rendered HTML is the product, not a checked-in generated artifact"
|
|
);
|
|
assert!(
|
|
!rendered.contains("Auto-generated"),
|
|
"posture-B hygiene: no emit-layer banner on rendered HTML"
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn card_fragment_registers_as_template_and_renders_directly_by_name() {
|
|
let set = consumer_fixture_set();
|
|
assert!(set.is_template("card"));
|
|
|
|
let rendered = set
|
|
.render(
|
|
"card",
|
|
&card(
|
|
"repo",
|
|
"Repo \"settings\" & <config>",
|
|
"if (a < b) { alert(\"<b>hi</b>\") }",
|
|
),
|
|
)
|
|
.expect("a fragment registered as its own template must render directly by name");
|
|
assert_eq!(rendered, GOLDEN_CARD);
|
|
|
|
let err = set
|
|
.render("header", &context_with(false, ""))
|
|
.expect_err("partial names are never render targets (the render-name rule)");
|
|
assert!(matches!(err, Error::Render(_)));
|
|
}
|
|
|
|
#[test]
|
|
fn strict_failure_maps_to_the_error_page_shape_panic_free() {
|
|
let set = consumer_fixture_set();
|
|
let context = json!({
|
|
"site_name": "Alk Forge",
|
|
"tagline": "",
|
|
"year": 2026
|
|
});
|
|
|
|
let err = set
|
|
.render("settings_page", &context)
|
|
.expect_err("a strict-mode render failure must return the typed error, never a panic");
|
|
assert!(matches!(err, Error::Render(_)));
|
|
|
|
let source = err
|
|
.source()
|
|
.expect("everything a consumer needs for the mapping is reachable through the public enum");
|
|
assert!(
|
|
format!("{source}").contains("account.email"),
|
|
"the substrate's missing-variable detail stays reachable consumer-side"
|
|
);
|
|
|
|
let error_page = set
|
|
.render("error_page", &json!({ "message": err.to_string() }))
|
|
.expect("the consumer-built error page must render over the error's data");
|
|
assert_eq!(error_page, GOLDEN_ERROR_PAGE);
|
|
assert!(
|
|
error_page.contains(""settings_page"")
|
|
&& error_page.contains(""account.email""),
|
|
"the failing template's identity and the missing path surface entity-escaped"
|
|
);
|
|
let detail_begin = error_page
|
|
.find(r#"<p class="detail">"#)
|
|
.map_or(0, |at| at + r#"<p class="detail">"#.len());
|
|
let detail_end = error_page[detail_begin..]
|
|
.find("</p>")
|
|
.expect("the detail slot closes");
|
|
let detail = &error_page[detail_begin..detail_begin + detail_end];
|
|
assert!(
|
|
detail.contains(""") && !detail.contains('"') && !detail.contains('<'),
|
|
"interpolated error detail is entity-escaped; the static markup alone carries quotes"
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn repeated_renders_across_rebuilds_and_sinks_are_byte_identical() {
|
|
let set_a = consumer_fixture_set();
|
|
let context = context_with(true, "<em>scheduled</em> maintenance");
|
|
|
|
let first = set_a
|
|
.render("page", &context)
|
|
.expect("the page should render");
|
|
for _ in 0..4 {
|
|
let again = set_a
|
|
.render("page", &context)
|
|
.expect("repeated renders must succeed");
|
|
assert_eq!(
|
|
again, first,
|
|
"repeated renders must be byte-identical (ADR-003, the caching substrate)"
|
|
);
|
|
}
|
|
|
|
let rebuilt_context = context_with(true, "<em>scheduled</em> maintenance");
|
|
let rebuilt = consumer_fixture_set()
|
|
.render("page", &rebuilt_context)
|
|
.expect("a rebuilt set should render");
|
|
assert_eq!(
|
|
rebuilt, first,
|
|
"same registry contents rebuilt must render byte-identical output"
|
|
);
|
|
|
|
let card_again = consumer_fixture_set()
|
|
.render(
|
|
"card",
|
|
&card(
|
|
"repo",
|
|
"Repo \"settings\" & <config>",
|
|
"if (a < b) { alert(\"<b>hi</b>\") }",
|
|
),
|
|
)
|
|
.expect("the fragment should render from the rebuilt set");
|
|
assert_eq!(
|
|
card_again, GOLDEN_CARD,
|
|
"fragment renders are byte-identical across registry rebuilds"
|
|
);
|
|
|
|
let mut sink = Vec::new();
|
|
consumer_fixture_set()
|
|
.render_to_write("page", &context, &mut sink)
|
|
.expect("the write sink should render the page");
|
|
assert_eq!(
|
|
&sink[..],
|
|
first.as_bytes(),
|
|
"both sinks must agree byte for byte"
|
|
);
|
|
}
|