test(derive): close sixth mutation shard

Exercise malformed parameter manifests, handler patterns, app diagnostics, and registration guards; delete an unreachable empty-handler branch and narrowly classify quote-owned parse invariants.

req: derive_app/001

req: component/003

req: derive_handler/003

req: test/020

req: test/021

req: test/022

req: test/023
This commit is contained in:
slhx agent
2026-07-17 14:40:56 +02:00
parent 31c52698e3
commit d1af300bd4
3 changed files with 94 additions and 6 deletions
+7
View File
@@ -24,6 +24,9 @@
# - Rust-fact named fields always carry identifiers by syn's type contract. Per-entry # - Rust-fact named fields always carry identifiers by syn's type contract. Per-entry
# and recursive read_dir errors cannot be injected portably after the parent opens; # and recursive read_dir errors cannot be injected portably after the parent opens;
# parent-open, source-read, and parse failures remain explicitly proven. # parent-open, source-read, and parse failures remain explicitly proven.
# - Registry-helper syntax is emitted entirely from quote-owned static tokens. Its
# parse succeeds by construction; expect/unwrap and expect-message mutations are
# equivalent, while exact generated registration and public diagnostics are proven.
exclude_re = [ exclude_re = [
"test_process_try_wait", "test_process_try_wait",
"test_process_poll_delay", "test_process_poll_delay",
@@ -48,4 +51,8 @@ exclude_re = [
"replace chars.next\\(\\)\\? with chars.next\\(\\).unwrap\\(\\) in context_type_for_heml_path", "replace chars.next\\(\\)\\? with chars.next\\(\\).unwrap\\(\\) in context_type_for_heml_path",
"replace entry\\? with entry.unwrap\\(\\) in collect_rust_struct_facts", "replace entry\\? with entry.unwrap\\(\\) in collect_rust_struct_facts",
"replace collect_rust_struct_facts.*\\? with collect_rust_struct_facts.*unwrap\\(\\) in collect_rust_struct_facts", "replace collect_rust_struct_facts.*\\? with collect_rust_struct_facts.*unwrap\\(\\) in collect_rust_struct_facts",
'replace syn::parse2.* with syn::parse2.*unwrap\(\) in add_app_registry_helper',
'replace "generated app registry helper parses" with "" in add_app_registry_helper',
'replace "generated component register helper parses" with "" in add_component_register_helper',
'replace "generated component state register helper parses" with "" in add_component_register_helper',
] ]
+1 -1
View File
@@ -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. - [ ] **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. - **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-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 `5/8` are clean. Shard `5/8` passed with 61 mutants (57 caught, 4 unviable) after fail-closed generated field-name validation and deletion of a redundant empty-parameter branch. Shards `6/8` through `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 `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.
- **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
+86 -5
View File
@@ -765,9 +765,6 @@ fn add_component_register_helper(mut module: ItemMod, component: &str) -> ItemMo
let Some(state_ty) = component_state_type(items) else { let Some(state_ty) = component_state_type(items) else {
return module; return module;
}; };
if handlers.is_empty() {
return module;
}
let calls = handlers let calls = handlers
.iter() .iter()
.map(|handler| component_registration_call(handler, &component_ident)); .map(|handler| component_registration_call(handler, &component_ident));
@@ -1021,8 +1018,8 @@ mod tests {
add_app_registry_helper, add_component_register_helper, client_handler_has_inputs, add_app_registry_helper, add_component_register_helper, client_handler_has_inputs,
component_handler_names, expand_handler_function, form_contract_errors, form_decode_fields, 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, form_fields, form_impl_generics, form_model_type, form_resource_id, generic_inner_type,
handle_params, handle_requires_form, handler_form_model_type, handler_placement, handle_params, handle_requires_form, handler_arg_names, handler_form_model_type,
handler_syms_path, has_form_param, has_non_unit_return, is_type_named, 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, join_contract_errors, missing_component_handlers, missing_form_generated_files_message,
missing_handle_params, missing_handler_generated_files_message, parser_type, 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, HandlerPlacement,
@@ -1461,6 +1458,14 @@ mod tests {
assert_eq!(missing_handle_params(&path, "show", &missing), vec!["mode"]); assert_eq!(missing_handle_params(&path, "show", &missing), vec!["mode"]);
assert!(missing_handle_params(&path, "unknown", &missing).is_empty()); assert!(missing_handle_params(&path, "unknown", &missing).is_empty());
std::fs::write(&path, "handle_param\nhandle_param\tshow\tmode\n").unwrap();
assert_eq!(handle_params(&path, "show"), vec!["mode"]);
let patterns: ItemFn = parse_quote! {
fn patterns(self, named: String, (left, right): (u8, u8), _: bool) {}
};
assert_eq!(handler_arg_names(&patterns), vec!["named"]);
let _ = std::fs::remove_file(path); let _ = std::fs::remove_file(path);
} }
@@ -1518,6 +1523,80 @@ mod tests {
); );
assert!(generated.contains("hemx_axum :: State"), "{generated}"); assert!(generated.contains("hemx_axum :: State"), "{generated}");
assert!(generated.contains("state . clone"), "{generated}"); assert!(generated.contains("state . clone"), "{generated}");
let no_components: ItemFn = parse_quote!(
fn registry(state: App) {}
);
assert_eq!(
add_app_registry_helper(no_components, vec![]).err().unwrap(),
"#[hemx::app] requires component registry module(s), for example #[hemx::app(todo_handlers, auth_handlers)]"
);
let unnamed_state: ItemFn = parse_quote!(
fn registry((state,): (App,)) {}
);
assert_eq!(
add_app_registry_helper(unnamed_state, vec![parse_quote!(handlers)])
.err()
.unwrap(),
"#[hemx::app] must be used on a registry function with a named app state argument"
);
}
#[test]
fn component_registration_generation_respects_existing_and_incomplete_modules() {
let external: syn::ItemMod = parse_quote!(
mod handlers;
);
let unchanged = add_component_register_helper(external.clone(), "todos");
assert_eq!(
quote!(#external).to_string(),
quote!(#unchanged).to_string()
);
for existing in ["register", "register_with_state"] {
let existing_ident = syn::Ident::new(existing, proc_macro2::Span::call_site());
let module: syn::ItemMod = parse_quote! {
mod handlers {
struct App;
#[hemx::handler]
fn create(app: App) {}
fn #existing_ident() {}
}
};
let generated = add_component_register_helper(module, "todos");
let items = generated.content.unwrap().1;
assert_eq!(items.len(), 3, "must preserve existing {existing}");
}
let no_state: syn::ItemMod = parse_quote! {
mod handlers {
#[hemx::handler]
fn create() {}
}
};
assert_eq!(
add_component_register_helper(no_state, "todos")
.content
.unwrap()
.1
.len(),
1
);
let no_handlers: syn::ItemMod = parse_quote! {
mod handlers {
struct App;
fn helper(app: App) {}
}
};
assert_eq!(
add_component_register_helper(no_handlers, "todos")
.content
.unwrap()
.1
.len(),
2
);
// test req: component/003 req: derive_handler/003
} }
#[test] #[test]
@@ -1525,6 +1604,8 @@ mod tests {
// req: component/003 req: derive_handler/003 // req: component/003 req: derive_handler/003
let module = parse_quote! { let module = parse_quote! {
mod handlers { mod handlers {
const COMPONENT_KIND: &str = "todos";
#[hemx::handler] #[hemx::handler]
fn sync_form(app: super::App, form: super::NewTodo) -> impl hemx::IntoEffect { fn sync_form(app: super::App, form: super::NewTodo) -> impl hemx::IntoEffect {
hemx::EventName::new("sync-form").emit("") hemx::EventName::new("sync-form").emit("")