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
+1 -1
View File
@@ -21,7 +21,7 @@
- [ ] **State:** In progress — the package-native capped xtask entry point is reachable, rejects unknown packages, propagates mutest failure, and mutation-tests `hemx-axum`, `hemx-core`, `hemx-js`, and the full `hemx-test` package cleanly; full package closure remains. - [ ] **State:** In progress — the package-native capped xtask entry point is reachable, rejects unknown packages, propagates mutest failure, and mutation-tests `hemx-axum`, `hemx-core`, `hemx-js`, and the full `hemx-test` package cleanly; full package closure remains.
- **User value:** maintainers can run one bounded repository command and trust that meaningful Rust logic across every mutation-applicable library is either killed or explicitly justified. - **User value:** maintainers can run one bounded repository command and trust that meaningful Rust logic across every mutation-applicable library is either killed or explicitly justified.
- **Build:** add a capped `hemx-xtask` mutation command that invokes `/opt/repositories/mutest`/`mutest` through package-native test targets rather than the broken workspace-wide example path; enumerate only current mutation-applicable library/proc-macro packages; finish adversarial tests or simplify code until every survivor is classified; keep equivalent, invariant-only, and infrastructure-inapplicable classifications inspectable and minimal; document the exact local release command in the existing readiness surface. - **Build:** add a capped `hemx-xtask` mutation command that invokes `/opt/repositories/mutest`/`mutest` through package-native test targets rather than the broken workspace-wide example path; enumerate only current mutation-applicable library/proc-macro packages; finish adversarial tests or simplify code until every survivor is classified; keep equivalent, invariant-only, and infrastructure-inapplicable classifications inspectable and minimal; document the exact local release command in the existing readiness surface.
- **Blocked by:** none; broad survivors currently remain in `hemx-build`, `hemx-derive`, and `hemx-lsp` outside already-clean focused contracts. The current `hemx-build` frontier now also mutation-proves all 43 unknown/selector-attribute and unkeyed-loop diagnostic mutants, with exact `InvalidData` paths/guidance and invalid-scope fail-closed behavior. The remaining package frontier starts at generated-target deduplication, identifier/canonical-symbol helpers, and residual internal diagnostics before another full package gate. The complete 470-mutant `hemx-axum` package gate now passes with 262 caught and 208 unviable after public page/form/multipart/registry/response/runtime proofs and narrow classification of infallible header parsing and streamed multipart unwrap-equivalent mutants. - **Blocked by:** none; broad survivors currently remain in `hemx-build`, `hemx-derive`, and `hemx-lsp` outside already-clean focused contracts. The current `hemx-build` frontier now also mutation-proves ordered cross-kind generated-target deduplication and identifier/literal/canonical-symbol/resource-insertion boundaries, including invalid UTF-8 paths, exact collisions, missing values, and stable first-seen ordering. The remaining package frontier is concentrated in Rust-fact recursive collection and three `Resources::add_surface` propagation/default branches before another full package gate. The complete 470-mutant `hemx-axum` package gate now passes with 262 caught and 208 unviable after public page/form/multipart/registry/response/runtime proofs and narrow classification of infallible header parsing and streamed multipart unwrap-equivalent mutants.
- **Proof:** the new xtask mutation command exits zero within its documented bound, covers each applicable package, emits no unexplained missed mutant, and a deliberate adjacent mutation makes it fail. `cargo run -p hemx-xtask -- test` remains green. req: test/020 req: test/021 - **Proof:** the new xtask mutation command exits zero within its documented bound, covers each applicable package, emits no unexplained missed mutant, and a deliberate adjacent mutation makes it fail. `cargo run -p hemx-xtask -- test` remains green. req: test/020 req: test/021
## 3. Elect and enforce the release license policy ## 3. Elect and enforce the release license policy
+149 -21
View File
@@ -1492,16 +1492,7 @@ fn is_class_continue(byte: u8) -> bool {
} }
fn class_ident(token: &str) -> Option<String> { fn class_ident(token: &str) -> Option<String> {
let mut ident = String::with_capacity(token.len()); rust_ident(&token.replace('-', "_"))
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)
} }
fn is_handle_param_attr(attr: &SurfaceAttribute) -> bool { 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 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] #[test]
fn generated_contract_fingerprint_and_client_bootstrap_are_deterministic() { fn generated_contract_fingerprint_and_client_bootstrap_are_deterministic() {
let resource = |symbol: &str, component: &str, id| Resource { let resource = |symbol: &str, component: &str, id| Resource {
@@ -3463,20 +3562,49 @@ fn main() {{
// req: diagnostics/004 req: diagnostics/005 // req: diagnostics/004 req: diagnostics/005
let targets = generated_targets_for_heml_source( let targets = generated_targets_for_heml_source(
"inline.heml", "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"); .expect("generated targets");
assert_eq!(targets.len(), 3); assert_eq!(
assert!(targets targets,
.iter() vec![
.any(|target| target.kind == "slot" && target.name == "summary")); GeneratedTarget {
assert!(targets kind: "slot".into(),
.iter() name: "summary".into(),
.any(|target| target.kind == "form" && target.name == "save")); },
assert!(targets GeneratedTarget {
.iter() kind: "slot".into(),
.any(|target| target.kind == "handle" && target.name == "submit")); 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] #[test]