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),