test(derive): close fourth mutation shard
Prove generated form decoding for required, optional, and repeated controls plus bare, qualified, mismatched, and empty generic container boundaries. req: form/004 req: diagnostics/003 req: test/022 req: test/023
This commit is contained in:
@@ -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 `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.
|
||||
- **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 `4/8` are clean. Shard `4/8` passed with 61 mutants (49 caught, 12 unviable) after exact required/optional/multiple form-decoding code generation plus bare/qualified/mismatched/empty generic container boundaries. Shards `5/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
|
||||
|
||||
+52
-7
@@ -1023,13 +1023,13 @@ 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_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,
|
||||
missing_handler_generated_files_message, parser_type, returns_result, syms_contains_handle,
|
||||
HandlerPlacement,
|
||||
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_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, syms_contains_handle, HandlerPlacement,
|
||||
};
|
||||
use quote::{quote, ToTokens};
|
||||
use syn::{parse_quote, ItemFn, Type};
|
||||
@@ -1297,6 +1297,51 @@ mod tests {
|
||||
"hemx form `profile` field `required_name` is required in HTML and must not be Option<_>",
|
||||
]
|
||||
);
|
||||
|
||||
let decode_struct: syn::ItemStruct = parse_quote!(
|
||||
struct Profile {
|
||||
first: Option<String>,
|
||||
tags: Vec<String>,
|
||||
required_name: String,
|
||||
}
|
||||
);
|
||||
let decoded = form_decode_fields(&path, "profile", &decode_struct)
|
||||
.into_iter()
|
||||
.map(|tokens| tokens.to_string())
|
||||
.collect::<Vec<_>>();
|
||||
assert_eq!(decoded.len(), 3);
|
||||
assert!(decoded[0].contains("Some (__hemx_value) => Some"));
|
||||
assert!(decoded[0].contains("None => None"));
|
||||
assert!(decoded[1].contains("collect :: < Result < Vec < _ > , _ >> () ?"));
|
||||
assert!(decoded[2].contains("missing form field"));
|
||||
|
||||
let option: Type = parse_quote!(Option<String>);
|
||||
let qualified_vec: Type = parse_quote!(std::vec::Vec<u64>);
|
||||
let plain: Type = parse_quote!(String);
|
||||
assert_eq!(
|
||||
generic_inner_type(&option, "Option")
|
||||
.unwrap()
|
||||
.to_token_stream()
|
||||
.to_string(),
|
||||
"String"
|
||||
);
|
||||
assert_eq!(
|
||||
generic_inner_type(&qualified_vec, "Vec")
|
||||
.unwrap()
|
||||
.to_token_stream()
|
||||
.to_string(),
|
||||
"u64"
|
||||
);
|
||||
assert!(generic_inner_type(&option, "Vec").is_none());
|
||||
assert!(generic_inner_type(&plain, "String").is_none());
|
||||
let empty_path = Type::Path(syn::TypePath {
|
||||
qself: None,
|
||||
path: syn::Path {
|
||||
leading_colon: None,
|
||||
segments: Default::default(),
|
||||
},
|
||||
});
|
||||
assert!(generic_inner_type(&empty_path, "Vec").is_none());
|
||||
std::fs::remove_file(path).unwrap();
|
||||
// test req: form/004 req: diagnostics/003
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user