From 216465f779557eb9dd46c3008cfedf42d4394f18 Mon Sep 17 00:00:00 2001 From: slhx agent Date: Fri, 17 Jul 2026 07:59:26 +0200 Subject: [PATCH] test(build): close residual helper mutants Prove compact Rust type and vector facts plus default template paths, alias collision suppression, data parameter prefixes, keyed descendants, and absent Cargo roots. Classify only hemplate's infallible surface parser seam. req: diagnostics/006 req: diagnostics/004 req: codegen/005 req: test/021 --- .cargo/mutants.toml | 2 ++ PLAN.md | 2 +- hemx-build/src/lib.rs | 56 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 59 insertions(+), 1 deletion(-) diff --git a/.cargo/mutants.toml b/.cargo/mutants.toml index ff80e0e..810870d 100644 --- a/.cargo/mutants.toml +++ b/.cargo/mutants.toml @@ -10,6 +10,7 @@ # are behaviorally equivalent at these validated boundaries. # - hemx-build source inspection delegates to hemplate's currently infallible # Surface parser; file I/O and invalid Rust-context errors remain explicitly proven. +# The direct surface_for_heml_source unwrap mutant is equivalent for the same seam. # - AppBuilder reuses that same parser seam. Directory-open and recursive errors are # proven, while a per-entry readdir fault cannot be injected portably after a # successful read_dir; its unwrap mutant is classified as infrastructure-only. @@ -35,6 +36,7 @@ exclude_re = [ "replace String::from_utf8.* with String::from_utf8.*unwrap\\(\\) in InteractionForm::parse_multipart", "replace HeaderValue::from_str.*runtime_js_hash.* with HeaderValue::from_str.*unwrap\\(\\) in ::into_response", 'replace "runtime hash is a valid ETag" with "" in ::into_response', + "replace build_ast.* with build_ast.*unwrap\\(\\) in surface_for_heml_source", "replace surface_for_heml_source.* with surface_for_heml_source.*unwrap\\(\\) in diagnostics_for_heml_source", "replace surface_for_heml_source.* with surface_for_heml_source.*unwrap\\(\\) in generated_targets_for_heml_source", "replace surface_for_heml_source.* with surface_for_heml_source.*unwrap\\(\\) in template_context_facts_for_heml_source", diff --git a/PLAN.md b/PLAN.md index 0d9d9e2..21708c3 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 38 recursive Rust-fact collection mutants and all 115 `Resources::add_surface` mutants, including nested/inline modules, ignored non-Rust/out-of-tree files, invalid parse propagation, atom/handle collisions, and missing form-fact defaults. The focused survivor frontier is closed; rerun the full package mutation gate to expose any remaining uncaught functions. 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 compact Rust-type/vector-fact and residual default/alias/parameter/keyed-descendant/Cargo-root helper mutants exposed during the full gate. That gate produced no missed-mutant report before the 30-minute command timeout, so the next slice must either complete it with deterministic package shards or expose and close the next shard's survivors. 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 b095279..2dae543 100644 --- a/hemx-build/src/lib.rs +++ b/hemx-build/src/lib.rs @@ -1973,6 +1973,7 @@ fn compact_tokens(tokens: &impl ToTokens) -> String { .replace(" < ", "<") .replace(" >", ">") .replace(" ,", ",") + .replace(", ", ",") .replace(" & ", "&") .replace("& ", "&") } @@ -2399,6 +2400,47 @@ mod tests { // test req: diagnostics/004 req: diagnostics/006 req: surface/008 } + #[test] + fn rust_type_facts_are_compact_and_vector_specific() { + let qualified: syn::Type = + syn::parse_str("std::collections::HashMap>") + .unwrap(); + assert_eq!( + compact_tokens(&qualified), + "std::collections::HashMap>" + ); + let mutable: syn::Type = syn::parse_str("&mut crate::Thing").unwrap(); + assert_eq!(compact_tokens(&mutable), "&mut crate::Thing"); + let tuple: syn::Type = syn::parse_str("(&crate::Thing, String)").unwrap(); + assert_eq!(compact_tokens(&tuple), "(&crate::Thing,String)"); + let function: syn::Type = syn::parse_str("fn(&crate::Thing) -> &crate::Thing").unwrap(); + assert_eq!( + compact_tokens(&function), + "fn (&crate::Thing) ->&crate::Thing" + ); + + assert_eq!(vec_element_type("Vec"), Some("String".into())); + assert_eq!( + vec_element_type("std::vec::Vec<&crate::Thing>"), + Some("&crate::Thing".into()) + ); + assert_eq!(vec_element_type("Vec< String >"), Some("String".into())); + for invalid in [ + "", + "String", + "Vec", + "alloc::vec::Vec", + ] { + assert_eq!( + vec_element_type(invalid), + None, + "accepted non-vector type {invalid:?}" + ); + } + // test req: diagnostics/006 + } + #[test] fn rust_fact_collection_propagates_parse_and_recursive_directory_errors() { let base = test_dir("hemx-build-rust-fact-errors"); @@ -2464,6 +2506,18 @@ mod tests { component_ident(root, Path::new("outside.heml")).unwrap(), "outside" ); + assert_eq!(data_param_ident("data-item-id"), Some("item_id".into())); + assert_eq!(data_param_ident("aria-label"), None); + assert_eq!(app().template_dir, PathBuf::from("templates")); + assert_eq!(nearest_dir_with(Path::new("/"), "hemx-absent-marker"), None); + + let mut exports = String::new(); + let mut used = BTreeSet::from(["shared".to_owned(), "shared_target".to_owned()]); + push_root_export(&mut exports, "", "targets", &mut used, "shared", "target"); + assert!( + exports.is_empty(), + "colliding alias must not be emitted twice" + ); let mut resources = BTreeMap::new(); let first = insert_resource( @@ -3988,6 +4042,8 @@ fn main() {{ assert!(is_inside_keyed_for(&surface, nested_scope)); assert!(!is_inside_keyed_for(&surface, sibling_scope)); assert!(!is_inside_keyed_for(&surface, ScopeId(u32::MAX))); + assert!(has_descendant_keyed_for_scope(&surface, ScopeId(0))); + assert!(!has_descendant_keyed_for_scope(&surface, sibling_scope)); assert!(unkeyed_generated_target_diagnostic_for_scope( &surface, ScopeId(u32::MAX),