test(axum): harden page and media extraction
Prove explicit partial header values, full-page body and fingerprint delivery, and case-insensitive urlencoded media types through public Axum extractors and responses. req: page_swap/001 req: abi/005 req: security/003
This commit is contained in:
@@ -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; remaining 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 and media-type integration paths are now mutation-clean; remaining app/transport integration paths remain.
|
||||
- **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
|
||||
|
||||
@@ -91,7 +91,7 @@ async fn interaction_boundary_honors_media_type_and_host_body_limit() {
|
||||
Request::post("/mutate")
|
||||
.header(
|
||||
header::CONTENT_TYPE,
|
||||
"application/x-www-form-urlencoded; charset=utf-8",
|
||||
"Application/X-Www-Form-Urlencoded; charset=UTF-8",
|
||||
)
|
||||
.body(Body::from("__h=1"))
|
||||
.unwrap(),
|
||||
@@ -235,12 +235,26 @@ impl hemx_core::FromForm for OpenProject {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn page_mode_detects_partial_header() {
|
||||
let mut headers = HeaderMap::new();
|
||||
assert_eq!(PageMode::from_headers(&headers), PageMode::Full);
|
||||
|
||||
headers.insert(HEMX_PARTIAL_HEADER, "true".parse().unwrap());
|
||||
assert_eq!(PageMode::from_headers(&headers), PageMode::Partial);
|
||||
fn page_mode_detects_only_explicit_partial_header_values() {
|
||||
for value in [
|
||||
None,
|
||||
Some("false"),
|
||||
Some("0"),
|
||||
Some("TRUE"),
|
||||
Some("invalid"),
|
||||
] {
|
||||
let mut headers = HeaderMap::new();
|
||||
if let Some(value) = value {
|
||||
headers.insert(HEMX_PARTIAL_HEADER, value.parse().unwrap());
|
||||
}
|
||||
assert_eq!(PageMode::from_headers(&headers), PageMode::Full);
|
||||
}
|
||||
for value in ["true", "1"] {
|
||||
let mut headers = HeaderMap::new();
|
||||
headers.insert(HEMX_PARTIAL_HEADER, value.parse().unwrap());
|
||||
assert_eq!(PageMode::from_headers(&headers), PageMode::Partial);
|
||||
}
|
||||
// test req: page_swap/001
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -365,6 +379,27 @@ fn page_response_constructors_preserve_mode_and_optional_fingerprint() {
|
||||
// test req: page_swap/001 req: abi/005
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn full_page_response_preserves_html_and_fingerprint_without_partial_headers() {
|
||||
let response = PageResponse::full("<html>Full</html>")
|
||||
.fingerprint(BuildFingerprint(42))
|
||||
.into_response();
|
||||
|
||||
assert_eq!(response.status(), StatusCode::OK);
|
||||
assert_eq!(
|
||||
response.headers()[header::CONTENT_TYPE],
|
||||
"text/html; charset=utf-8"
|
||||
);
|
||||
assert_eq!(response.headers()[HEMX_FINGERPRINT_HEADER], "42");
|
||||
assert!(!response.headers().contains_key(HEMX_PARTIAL_HEADER));
|
||||
assert!(!response.headers().contains_key(HEMX_TITLE_HEADER));
|
||||
assert_eq!(
|
||||
to_bytes(response.into_body(), 1024).await.unwrap().as_ref(),
|
||||
b"<html>Full</html>"
|
||||
);
|
||||
// test req: page_swap/001 req: abi/005
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn partial_page_response_sets_partial_and_title_headers() {
|
||||
let response = PageResponse::partial("<main>Docs</main>")
|
||||
|
||||
Reference in New Issue
Block a user