From 5d6486c22c990c3c63bfef76d94625a93dcff4db Mon Sep 17 00:00:00 2001 From: slhx agent Date: Fri, 17 Jul 2026 14:47:17 +0200 Subject: [PATCH] test(derive): close seventh mutation shard Prove all state, async, and Result registration combinations and the exact no-generated-component repair diagnostic. req: component/003 req: derive_handler/003 req: test/020 req: test/021 req: test/022 req: test/023 --- PLAN.md | 2 +- hemx-derive/src/lib.rs | 69 ++++++++++++++++++++++++++++++++++++++---- 2 files changed, 64 insertions(+), 7 deletions(-) diff --git a/PLAN.md b/PLAN.md index 8cd5e40..6caa7d3 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 and invalid shards, propagates mutest failure, and mutation-tests `hemx-axum`, `hemx-build`, `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-derive` and `hemx-lsp` outside already-clean focused contracts. The xtask mutation runner now creates shard output parents before invoking mutest, fixing first-use failure for newly sharded packages. `hemx-derive` public shards `1/8` through `6/8` are clean. Shard `6/8` passed with 60 mutants (50 caught, 10 unviable) after malformed parameter-manifest, handler-pattern, app diagnostic, and component registration guard coverage; an unreachable empty-handler branch was deleted, and static quote-owned syntax parsing is narrowly classified as invariant-equivalent. Shards `7/8` and `8/8` remain. The mutation entry point accepts validated one-based `SHARD/TOTAL` operands, maps them to native zero-based shards, uses shard-specific output directories, grants repo-owned compiler probes a 120-second floor, and preserves the unsharded gate. All eight deterministic `hemx-build` shards now pass: 1,304 mutants total, 1,070 caught and 234 unviable, including final shard `8/8` with 159 mutants (139 caught, 20 unviable). The complete 470-mutant `hemx-axum` package gate 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-derive` and `hemx-lsp` outside already-clean focused contracts. The xtask mutation runner now creates shard output parents before invoking mutest, fixing first-use failure for newly sharded packages. `hemx-derive` public shards `1/8` through `7/8` are clean. Shard `7/8` passed with 60 mutants (45 caught, 15 unviable) after exact coverage of all state/async/result registration shapes and the no-generated-component repair diagnostic. Shard `8/8` remains. The mutation entry point accepts validated one-based `SHARD/TOTAL` operands, maps them to native zero-based shards, uses shard-specific output directories, grants repo-owned compiler probes a 120-second floor, and preserves the unsharded gate. All eight deterministic `hemx-build` shards now pass: 1,304 mutants total, 1,070 caught and 234 unviable, including final shard `8/8` with 159 mutants (139 caught, 20 unviable). The complete 470-mutant `hemx-axum` package gate 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-derive/src/lib.rs b/hemx-derive/src/lib.rs index 35be686..2b2a1e4 100644 --- a/hemx-derive/src/lib.rs +++ b/hemx-derive/src/lib.rs @@ -1016,13 +1016,14 @@ fn compile_error(message: &str) -> TokenStream { mod tests { use super::{ add_app_registry_helper, add_component_register_helper, client_handler_has_inputs, - component_handler_names, expand_handler_function, form_contract_errors, form_decode_fields, - form_fields, form_impl_generics, form_model_type, form_resource_id, generic_inner_type, - handle_params, handle_requires_form, handler_arg_names, handler_form_model_type, - handler_placement, handler_syms_path, has_form_param, has_non_unit_return, is_type_named, + component_contract_errors, component_handler_names, component_registration_call, + expand_handler_function, form_contract_errors, form_decode_fields, form_fields, + form_impl_generics, form_model_type, form_resource_id, generic_inner_type, handle_params, + handle_requires_form, handler_arg_names, handler_form_model_type, handler_placement, + handler_syms_path, has_form_param, has_non_unit_return, is_type_named, join_contract_errors, missing_component_handlers, missing_form_generated_files_message, missing_handle_params, missing_handler_generated_files_message, parser_type, - returns_result, rust_ident, syms_contains_handle, HandlerPlacement, + returns_result, rust_ident, syms_contains_handle, ComponentHandler, HandlerPlacement, }; use quote::{quote, ToTokens}; use syn::{parse_quote, ItemFn, Type}; @@ -1469,6 +1470,55 @@ mod tests { let _ = std::fs::remove_file(path); } + #[test] + fn component_registration_selects_each_state_async_result_shape() { + let component_ident: syn::Ident = parse_quote!(todos); + let expected = [ + (0, false, false, quote!(.on(super::todos::save, save))), + (0, false, true, quote!(.on_result(super::todos::save, save))), + (0, true, false, quote!(.on_async(super::todos::save, save))), + ( + 0, + true, + true, + quote!(.on_async_result(super::todos::save, save)), + ), + (1, false, false, quote!(.on_state(super::todos::save, save))), + ( + 1, + false, + true, + quote!(.on_state_result(super::todos::save, save)), + ), + ( + 1, + true, + false, + quote!(.on_state_async(super::todos::save, save)), + ), + ( + 1, + true, + true, + quote!(.on_state_async_result(super::todos::save, save)), + ), + ]; + for (typed_arg_count, is_async, returns_result, expected) in expected { + let handler = ComponentHandler { + ident: parse_quote!(save), + is_async, + returns_result, + typed_arg_count, + }; + assert_eq!( + component_registration_call(&handler, &component_ident).to_string(), + expected.to_string(), + "args={typed_arg_count} async={is_async} result={returns_result}" + ); + } + // test req: component/003 req: derive_handler/003 + } + #[test] fn component_handlers_match_generated_handles() { let path = std::env::temp_dir().join("hemx-derive-component-test.syms"); @@ -1495,7 +1545,14 @@ mod tests { vec!["delete"] ); - let _ = std::fs::remove_file(path); + let _ = std::fs::remove_file(&path); + assert_eq!( + component_contract_errors(&path, Some("missing"), &items), + vec![ + "#[hemx::component(\"missing\")] does not match any generated handles; no generated components with handles are available; add a data-hemx-handle to the component template or check build.rs generation", + "#[hemx::component] handler(s) not declared by this component's generated handles: create; move them to the matching component module or add data-hemx-handle in .heml", + ] + ); } #[test]