test(derive): close third mutation shard

Prove missing generated-symbol fallback and exact accumulated form diagnostics for unknown forms, unnamed structs, missing fields, singular/multiple mismatches, and required Option fields.

req: form/004

req: diagnostics/003

req: derive_handler/005

req: test/022

req: test/023
This commit is contained in:
slhx agent
2026-07-17 14:20:29 +02:00
parent a59e9b0bfe
commit dd230b470e
2 changed files with 61 additions and 4 deletions
+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.
- **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` and `2/8` are clean. Shard `1/8` passed through deterministic partitions `1/16` (31 mutants: 24 caught, 7 unviable) and `9/16` (31 mutants: 28 caught, 3 unviable); shard `2/8` passed with 61 mutants (46 caught, 15 unviable) after exact client-handler mode/signature/codegen, generated-file diagnostics, form generic bounds, malformed symbol manifests, and unambiguous `Form<T>` path matching; shards `3/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 `3/8` are clean. Shard `3/8` passed with 61 mutants (52 caught, 9 unviable) after exact missing-symbol fallback plus accumulated missing-field, singular/multiple, required/optional, and malformed form-contract diagnostics. Shards `4/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.
- **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
+60 -3
View File
@@ -1023,8 +1023,8 @@ 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_fields, form_impl_generics,
form_model_type, form_resource_id, handle_params, handle_requires_form,
component_handler_names, expand_handler_function, form_contract_errors, form_fields,
form_impl_generics, form_model_type, form_resource_id, handle_params, handle_requires_form,
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,
@@ -1249,6 +1249,58 @@ mod tests {
// test req: derive_handler/001 req: derive_handler/005
}
#[test]
fn form_contract_diagnostics_accumulate_all_mismatches() {
let path = std::env::temp_dir().join(format!(
"hemx-derive-form-contract-errors-{}",
std::process::id()
));
let missing: syn::ItemStruct = parse_quote!(
struct Profile {
first: String,
}
);
assert_eq!(
form_contract_errors(&path, "profile", &missing),
vec!["#[hemx::form] could not find generated hemx symbols; add hemx_build::app().run()? to build.rs or check template generation"]
);
std::fs::write(
&path,
"hemx-syms-v1\nform\tprofile.heml::profile\tprofile\t42\nform_field\tprofile\tfirst\tfalse\tfalse\nform_field\tprofile\tsecond\ttrue\tfalse\nform_field\tprofile\ttags\tfalse\ttrue\nform_field\tprofile\trequired_name\ttrue\tfalse\n",
)
.unwrap();
assert_eq!(
form_contract_errors(&path, "unknown", &missing),
vec!["unknown hemx form `unknown`; add data-hemx-form=\"unknown\" to a template or rename this form binding"]
);
let tuple: syn::ItemStruct = parse_quote!(
struct Profile(String);
);
assert_eq!(
form_contract_errors(&path, "profile", &tuple),
vec!["hemx form `profile` must be a struct with named fields"]
);
let mismatched: syn::ItemStruct = parse_quote!(
struct Profile {
first: Vec<String>,
tags: String,
required_name: Option<String>,
}
);
assert_eq!(
form_contract_errors(&path, "profile", &mismatched),
vec![
"hemx form `profile` field `first` accepts one value and must not be Vec<_>",
"hemx form `profile` is missing field `second` for form control `second`",
"hemx form `profile` field `tags` accepts multiple values and must be Vec<_>",
"hemx form `profile` field `required_name` is required in HTML and must not be Option<_>",
]
);
std::fs::remove_file(path).unwrap();
// test req: form/004 req: diagnostics/003
}
#[test]
fn syms_lookup_matches_handle_ident() {
let path = std::env::temp_dir().join("hemx-derive-syms-test.syms");
@@ -1265,7 +1317,12 @@ mod tests {
assert_eq!(handle_params(&path, "create"), vec!["todo_id"]);
assert!(handle_params(&path, "missing").is_empty());
let _ = std::fs::remove_file(path);
let _ = std::fs::remove_file(&path);
assert!(
syms_contains_handle(&path, "create"),
"missing generated symbols defer to the dedicated generated-file diagnostic"
);
assert!(!handle_requires_form(&path, "create"));
}
#[test]