From 8aeab5fca1265e33a82606c1355f3766504f6a4e Mon Sep 17 00:00:00 2001 From: slhx agent Date: Fri, 17 Jul 2026 01:05:48 +0200 Subject: [PATCH] test(axum): close page edge-case mutants Prove partial constructor defaults, missing multipart-boundary rejection, first-root fingerprint replacement, and unchanged non-root HTML through public and nearest-boundary paths. req: page_swap/001 req: multipart/003 req: abi/005 --- PLAN.md | 2 +- hemx-axum/src/lib.rs | 12 ++++++++++++ hemx-axum/tests/response.rs | 15 ++++++++++++++- 3 files changed, 27 insertions(+), 2 deletions(-) diff --git a/PLAN.md b/PLAN.md index 281b91a..c723c0d 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, propagates mutest failure, and mutation-tests `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-axum`, `hemx-build`, `hemx-derive`, and `hemx-lsp` outside already-clean focused contracts; the current `hemx-axum` frontier now proves page-mode, response constructors, form accessors/rejections, media-type limits, multipart success/error semantics, sync/async registry dispatch, effect/rejection responses, and embedded runtime delivery mutation-clean; page extraction/response and media-type integration paths are now mutation-clean; remaining app/transport integration paths remain. +- **Blocked by:** none; broad survivors currently remain in `hemx-axum`, `hemx-build`, `hemx-derive`, and `hemx-lsp` outside already-clean focused contracts; the current `hemx-axum` frontier now proves page-mode, response constructors, form accessors/rejections, media-type limits, multipart success/error semantics, sync/async registry dispatch, effect/rejection responses, and embedded runtime delivery mutation-clean; page extraction/response, media-type integration, partial-constructor defaults, missing multipart-boundary rejection, and root-fingerprint injection are now mutation-clean; handler-registration and infallible runtime-header construction remain before the full package gate can pass. - **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-axum/src/lib.rs b/hemx-axum/src/lib.rs index 6e04fb4..371efba 100644 --- a/hemx-axum/src/lib.rs +++ b/hemx-axum/src/lib.rs @@ -1609,6 +1609,18 @@ mod tests { .expect("root element is rendered"); assert_eq!(root.value().attr("data-hemx-fp"), Some("1")); assert_eq!(root.text().collect::(), "Docs"); + assert_eq!(html.matches("data-hemx-fp=").count(), 1); + assert!(!html.contains("data-hemx-fp=\"99\"")); + } + + #[test] + fn fingerprint_injection_leaves_html_without_a_hemx_root_unchanged() { + let html = "
Docs
".to_owned(); + assert_eq!( + html_with_root_fingerprint(html.clone(), BuildFingerprint(99)), + html + ); + // test req: abi/005 } #[test] diff --git a/hemx-axum/tests/response.rs b/hemx-axum/tests/response.rs index 11c057e..ecb557b 100644 --- a/hemx-axum/tests/response.rs +++ b/hemx-axum/tests/response.rs @@ -144,6 +144,19 @@ async fn interaction_boundary_skips_unnamed_parts_and_rejects_invalid_multipart( let _guard = BOUNDARY_TEST_LOCK .lock() .unwrap_or_else(|error| error.into_inner()); + let app = Router::new().route("/mutate", post(bounded_mutation)); + let missing_boundary = app + .clone() + .oneshot( + Request::post("/mutate") + .header(header::CONTENT_TYPE, "multipart/form-data") + .body(Body::from("invalid")) + .unwrap(), + ) + .await + .unwrap(); + assert_eq!(missing_boundary.status(), StatusCode::BAD_REQUEST); + let accepted = concat!( "--b\r\n", "Content-Disposition: form-data; filename=\"ignored.txt\"\r\n\r\n", @@ -153,7 +166,6 @@ async fn interaction_boundary_skips_unnamed_parts_and_rejects_invalid_multipart( "1\r\n", "--b--\r\n" ); - let app = Router::new().route("/mutate", post(bounded_mutation)); let response = app .clone() .oneshot( @@ -369,6 +381,7 @@ fn page_response_constructors_preserve_mode_and_optional_fingerprint() { let partial = PageResponse::partial("
Partial
"); assert_eq!(partial.mode, PageMode::Partial); assert_eq!(partial.html, "
Partial
"); + assert_eq!(partial.title, None); assert_eq!(partial.fingerprint, None); let fingerprint = BuildFingerprint(42);