test(build): close target and identifier mutants

Prove first-seen cross-kind generated-target ordering and deduplication, exact identifier/literal/canonical-symbol boundaries, invalid UTF-8 and component names, and resource insertion reuse/collisions; simplify CSS class normalization through the shared Rust identifier validator.

req: codegen/005

req: diagnostics/004

req: diagnostics/005

req: surface/008

req: test/021
This commit is contained in:
slhx agent
2026-07-17 06:58:36 +02:00
parent 31534a538b
commit 05154a58ed
2 changed files with 150 additions and 22 deletions
+149 -21
View File
@@ -1492,16 +1492,7 @@ fn is_class_continue(byte: u8) -> bool {
}
fn class_ident(token: &str) -> Option<String> {
let mut ident = String::with_capacity(token.len());
for ch in token.chars() {
match ch {
'-' => ident.push('_'),
'_' => ident.push('_'),
ch if ch.is_ascii_alphanumeric() => ident.push(ch),
_ => return None,
}
}
rust_ident(&ident)
rust_ident(&token.replace('-', "_"))
}
fn is_handle_param_attr(attr: &SurfaceAttribute) -> bool {
@@ -2373,6 +2364,114 @@ mod tests {
// test req: diagnostics/004 req: diagnostics/006 req: surface/008
}
#[test]
fn identifier_and_literal_helpers_fail_closed_at_boundaries() {
assert_eq!(rust_ident("alpha_9"), Some("alpha_9".into()));
for invalid in ["", "9alpha", "alpha-beta", "alpha beta", "alphaé"] {
assert_eq!(
rust_ident(invalid),
None,
"accepted Rust identifier {invalid:?}"
);
}
assert_eq!(class_ident("alpha-beta_9"), Some("alpha_beta_9".into()));
for invalid in ["", "9alpha", "alphaé", "alpha.beta", "alpha beta"] {
assert_eq!(class_ident(invalid), None, "accepted CSS class {invalid:?}");
}
assert_eq!(rust_str("a\n\"b\\c"), "\"a\\n\\\"b\\\\c\"");
assert_eq!(rust_str_opt(None), "None");
assert_eq!(rust_str_opt(Some("a\n\"b\\c")), "Some(\"a\\n\\\"b\\\\c\")");
assert_eq!(
canonical_symbol(
Path::new("templates"),
Path::new("templates/nested/panel.heml"),
"save",
),
"nested/panel.heml::save"
);
let root = Path::new("templates");
assert_eq!(
component_ident(root, Path::new("templates/nested/profile_card.heml")).unwrap(),
"profile_card"
);
assert_eq!(
component_ident(root, Path::new("templates/9bad.heml"))
.unwrap_err()
.kind(),
io::ErrorKind::InvalidData
);
assert_eq!(
component_ident(root, Path::new("outside.heml")).unwrap(),
"outside"
);
let mut resources = BTreeMap::new();
let first = insert_resource(
&mut resources,
"slot",
"panel.heml::summary".into(),
"summary".into(),
"panel".into(),
)
.unwrap();
first.keyed = true;
assert!(
insert_resource(
&mut resources,
"slot",
"panel.heml::summary".into(),
"summary".into(),
"panel".into(),
)
.unwrap()
.keyed
);
assert_eq!(
insert_resource(
&mut resources,
"slot",
"other.heml::summary".into(),
"summary".into(),
"other".into(),
)
.unwrap_err()
.to_string(),
"duplicate generated identifier `summary` for `panel.heml::summary` and `other.heml::summary`"
);
let invalid_surface = surface_for_heml_source(
Path::new("123.heml"),
r#"<section data-hemx-slot="summary"></section>"#.to_owned(),
)
.unwrap();
let mut extracted = Resources::default();
assert_eq!(
extracted
.add_surface(
Path::new("templates"),
Path::new("templates/123.heml"),
&invalid_surface,
)
.unwrap_err()
.kind(),
io::ErrorKind::InvalidData
);
#[cfg(unix)]
{
use std::os::unix::ffi::OsStrExt;
let invalid =
PathBuf::from("templates").join(std::ffi::OsStr::from_bytes(b"\xff.heml"));
assert_eq!(
component_ident(root, &invalid).unwrap_err().kind(),
io::ErrorKind::InvalidData
);
}
// test req: codegen/005 req: diagnostics/004
}
#[test]
fn generated_contract_fingerprint_and_client_bootstrap_are_deterministic() {
let resource = |symbol: &str, component: &str, id| Resource {
@@ -3463,20 +3562,49 @@ fn main() {{
// req: diagnostics/004 req: diagnostics/005
let targets = generated_targets_for_heml_source(
"inline.heml",
r#"<main data-hemx-root="todos"><section data-hemx-slot="summary"></section><form data-hemx-form="save"><button data-hemx-handle="submit">Save</button></form></main>"#,
r#"
<main data-hemx-root="todos">
<section class="card" data-hemx-slot="summary"></section>
<section data-hemx-root="nested" data-hemx-slot="after-root"></section>
<section data-hemx-slot="summary"></section>
<form data-hemx-form="summary"></form>
<span data-hemx-handle data-hemx-slot="after-missing"></span>
<button data-hemx-handle="submit">Save</button>
<aside data-hemx-slot="later"></aside>
</main>
"#,
)
.expect("generated targets");
assert_eq!(targets.len(), 3);
assert!(targets
.iter()
.any(|target| target.kind == "slot" && target.name == "summary"));
assert!(targets
.iter()
.any(|target| target.kind == "form" && target.name == "save"));
assert!(targets
.iter()
.any(|target| target.kind == "handle" && target.name == "submit"));
assert_eq!(
targets,
vec![
GeneratedTarget {
kind: "slot".into(),
name: "summary".into(),
},
GeneratedTarget {
kind: "slot".into(),
name: "after-root".into(),
},
GeneratedTarget {
kind: "form".into(),
name: "summary".into(),
},
GeneratedTarget {
kind: "slot".into(),
name: "after-missing".into(),
},
GeneratedTarget {
kind: "handle".into(),
name: "submit".into(),
},
GeneratedTarget {
kind: "slot".into(),
name: "later".into(),
},
]
);
}
#[test]