From 4e7d772311174ff4e20fbe27d91fa9cc07ce37bc Mon Sep 17 00:00:00 2001 From: slhx agent Date: Fri, 17 Jul 2026 06:07:04 +0200 Subject: [PATCH] test(build): close placement diagnostic mutants Assert exact InvalidData diagnostics for navigation, boost, client-module, and SSE misplacement and prove each accepted placement through AppBuilder::run. req: page_swap/001 req: page_swap/007 req: push/006 req: diagnostics/002 req: test/021 --- PLAN.md | 2 +- hemx-build/src/lib.rs | 72 ++++++++++++++++++++++++++++++------------- 2 files changed, 51 insertions(+), 23 deletions(-) diff --git a/PLAN.md b/PLAN.md index 5a56322..ed58428 100644 --- a/PLAN.md +++ b/PLAN.md @@ -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. - **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. -- **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 31 loop-local inference mutants: only one Rust identifier bound directly to a `self` vector field yields facts, malformed/non-vector bindings fail closed, unknown element types preserve an empty local, and repeated locals deduplicate. The remaining package frontier starts at placement/attribute diagnostics. 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 all 59 placement-diagnostic mutants with exact `InvalidData` paths, attributes, and guidance for navigation, boost containers, client modules, and SSE roots, plus their valid placements. The remaining package frontier starts at static attribute-value and keyed-scope diagnostics. 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 ## 3. Elect and enforce the release license policy diff --git a/hemx-build/src/lib.rs b/hemx-build/src/lib.rs index 1578cff..89643ce 100644 --- a/hemx-build/src/lib.rs +++ b/hemx-build/src/lib.rs @@ -3920,73 +3920,101 @@ fn main() {{ } #[test] - fn rejects_invalid_hemx_attr_placement() { + fn placement_contracts_fail_closed_with_exact_diagnostics() { // req: page_swap/001 req: page_swap/007 req: push/006 req: diagnostics/002 - for (case, template, attr, guidance) in [ + for (case, template, attr, expectation) in [ ( "nav-button", r#""#, "data-hemx-nav", - "", + "expected a real `` link so navigation works without JavaScript", ), ( "nav-missing-href", r#"Docs"#, "data-hemx-nav", - "", + "expected a real `` link so navigation works without JavaScript", ), ( "nav-empty-href", - r#"Docs"#, + r#"Docs"#, "data-hemx-nav", - "", + "expected a real `` link so navigation works without JavaScript", ), ( "boost-anchor", r#"Docs"#, "data-hemx-boost", - "data-hemx-nav", + "expected a container around descendant links/forms; use `data-hemx-nav` on anchors or `data-hemx-handle` on forms", ), ( "boost-form", r#"
"#, "data-hemx-boost", - "data-hemx-handle", + "expected a container around descendant links/forms; use `data-hemx-nav` on anchors or `data-hemx-handle` on forms", + ), + ( + "client-module-child", + r#"
"#, + "data-hemx-client-module", + "expected placement on the same element as `data-hemx-root`", ), ( "sse-child", r#"
"#, "data-hemx-sse", - "data-hemx-root", + "expected placement on the same element as `data-hemx-root`", ), ] { let base = test_dir(&format!("hemx-build-invalid-placement-{case}-test")); let templates = base.join("templates"); let out = base.join("out"); + let template_path = templates.join("placement.heml"); let _ = std::fs::remove_dir_all(&base); std::fs::create_dir_all(&templates).unwrap(); - std::fs::write(templates.join("placement.heml"), template).unwrap(); + std::fs::write(&template_path, template).unwrap(); - let err = app() + let error = app() .template_dir(&templates) .out_dir(&out) .run() .unwrap_err(); - assert!( - err.to_string().contains(attr), - "missing attr in diagnostic: {err}" - ); - assert!( - err.to_string().contains("invalid"), - "missing invalid-placement diagnostic: {err}" - ); - assert!( - err.to_string().contains(guidance), - "missing placement guidance {guidance:?}: {err}" + assert_eq!(error.kind(), io::ErrorKind::InvalidData); + assert_eq!( + error.to_string(), + format!( + "{}: invalid {attr} placement; {expectation}", + template_path.display() + ) ); let _ = std::fs::remove_dir_all(&base); } + + for (case, template) in [ + ("nav-link", r#"Docs"#), + ( + "boost-container", + r#""#, + ), + ( + "client-module-root", + r#"
"#, + ), + ( + "sse-root", + r#"
"#, + ), + ] { + let base = test_dir(&format!("hemx-build-valid-placement-{case}-test")); + let templates = base.join("templates"); + let out = base.join("out"); + let _ = std::fs::remove_dir_all(&base); + std::fs::create_dir_all(&templates).unwrap(); + std::fs::write(templates.join("placement.heml"), template).unwrap(); + app().template_dir(&templates).out_dir(&out).run().unwrap(); + let _ = std::fs::remove_dir_all(base); + } } #[test]