test(derive): close fifth mutation shard
Fail closed when generated form field names cannot map to Rust identifiers and delete the redundant empty generated-parameter branch. 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.
|
- [ ] **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 `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.
|
- **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.
|
||||||
- **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
|
||||||
|
|||||||
+13
-6
@@ -670,12 +670,8 @@ fn rust_ident(name: &str) -> Option<String> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn missing_handle_params(path: &PathBuf, ident: &str, function: &ItemFn) -> Vec<String> {
|
fn missing_handle_params(path: &PathBuf, ident: &str, function: &ItemFn) -> Vec<String> {
|
||||||
let required = handle_params(path, ident);
|
|
||||||
if required.is_empty() {
|
|
||||||
return Vec::new();
|
|
||||||
}
|
|
||||||
let args = handler_arg_names(function);
|
let args = handler_arg_names(function);
|
||||||
required
|
handle_params(path, ident)
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.filter(|param| !args.contains(param))
|
.filter(|param| !args.contains(param))
|
||||||
.collect()
|
.collect()
|
||||||
@@ -1029,7 +1025,7 @@ mod tests {
|
|||||||
handler_syms_path, has_form_param, has_non_unit_return, is_type_named,
|
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, syms_contains_handle, HandlerPlacement,
|
returns_result, rust_ident, syms_contains_handle, HandlerPlacement,
|
||||||
};
|
};
|
||||||
use quote::{quote, ToTokens};
|
use quote::{quote, ToTokens};
|
||||||
use syn::{parse_quote, ItemFn, Type};
|
use syn::{parse_quote, ItemFn, Type};
|
||||||
@@ -1432,6 +1428,16 @@ mod tests {
|
|||||||
assert!(form_model_type(&foreign_form).is_none());
|
assert!(form_model_type(&foreign_form).is_none());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn generated_form_field_names_map_only_to_valid_rust_identifiers() {
|
||||||
|
assert_eq!(rust_ident("first-name").as_deref(), Some("first_name"));
|
||||||
|
assert_eq!(rust_ident("_private2").as_deref(), Some("_private2"));
|
||||||
|
for invalid in ["", "2fast", "with space", "naïve"] {
|
||||||
|
assert_eq!(rust_ident(invalid), None, "{invalid:?} must fail closed");
|
||||||
|
}
|
||||||
|
// test req: form/004 req: diagnostics/003
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn handler_params_match_generated_param_names() {
|
fn handler_params_match_generated_param_names() {
|
||||||
let path = std::env::temp_dir().join("hemx-derive-param-test.syms");
|
let path = std::env::temp_dir().join("hemx-derive-param-test.syms");
|
||||||
@@ -1453,6 +1459,7 @@ mod tests {
|
|||||||
|
|
||||||
assert!(missing_handle_params(&path, "show", &complete).is_empty());
|
assert!(missing_handle_params(&path, "show", &complete).is_empty());
|
||||||
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());
|
||||||
|
|
||||||
let _ = std::fs::remove_file(path);
|
let _ = std::fs::remove_file(path);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user