test(build): close placement diagnostic mutants
Assert exact InvalidData diagnostics for navigation, boost, client-module, and SSE misplacement and prove each accepted placement through AppBuilder::run. req: page_swap/001 req: page_swap/007 req: push/006 req: diagnostics/002 req: test/021
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-axum`, `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-build`, `hemx-derive`, and `hemx-lsp` outside already-clean focused contracts. The current `hemx-build` frontier now also mutation-proves all 31 loop-local inference mutants: only one Rust identifier bound directly to a `self` vector field yields facts, malformed/non-vector bindings fail closed, unknown element types preserve an empty local, and repeated locals deduplicate. The remaining package frontier starts at placement/attribute diagnostics. The complete 470-mutant `hemx-axum` package gate now 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-build`, `hemx-derive`, and `hemx-lsp` outside already-clean focused contracts. The current `hemx-build` frontier now also mutation-proves all 59 placement-diagnostic mutants with exact `InvalidData` paths, attributes, and guidance for navigation, boost containers, client modules, and SSE roots, plus their valid placements. The remaining package frontier starts at static attribute-value and keyed-scope diagnostics. The complete 470-mutant `hemx-axum` package gate now 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
|
||||
|
||||
+50
-22
@@ -3920,73 +3920,101 @@ fn main() {{
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rejects_invalid_hemx_attr_placement() {
|
||||
fn placement_contracts_fail_closed_with_exact_diagnostics() {
|
||||
// req: page_swap/001 req: page_swap/007 req: push/006 req: diagnostics/002
|
||||
for (case, template, attr, guidance) in [
|
||||
for (case, template, attr, expectation) in [
|
||||
(
|
||||
"nav-button",
|
||||
r#"<button data-hemx-nav="" data-hemx-handle="go">Go</button>"#,
|
||||
"data-hemx-nav",
|
||||
"<a href=...>",
|
||||
"expected a real `<a href=...>` link so navigation works without JavaScript",
|
||||
),
|
||||
(
|
||||
"nav-missing-href",
|
||||
r#"<a data-hemx-nav="">Docs</a>"#,
|
||||
"data-hemx-nav",
|
||||
"<a href=...>",
|
||||
"expected a real `<a href=...>` link so navigation works without JavaScript",
|
||||
),
|
||||
(
|
||||
"nav-empty-href",
|
||||
r#"<a href="" data-hemx-nav="">Docs</a>"#,
|
||||
r#"<a href=" " data-hemx-nav="">Docs</a>"#,
|
||||
"data-hemx-nav",
|
||||
"<a href=...>",
|
||||
"expected a real `<a href=...>` link so navigation works without JavaScript",
|
||||
),
|
||||
(
|
||||
"boost-anchor",
|
||||
r#"<a href="/docs" data-hemx-boost="">Docs</a>"#,
|
||||
"data-hemx-boost",
|
||||
"data-hemx-nav",
|
||||
"expected a container around descendant links/forms; use `data-hemx-nav` on anchors or `data-hemx-handle` on forms",
|
||||
),
|
||||
(
|
||||
"boost-form",
|
||||
r#"<form data-hemx-boost=""><input name="q"></form>"#,
|
||||
"data-hemx-boost",
|
||||
"data-hemx-handle",
|
||||
"expected a container around descendant links/forms; use `data-hemx-nav` on anchors or `data-hemx-handle` on forms",
|
||||
),
|
||||
(
|
||||
"client-module-child",
|
||||
r#"<section data-hemx-root="app"><div data-hemx-client-module="/app.js"></div></section>"#,
|
||||
"data-hemx-client-module",
|
||||
"expected placement on the same element as `data-hemx-root`",
|
||||
),
|
||||
(
|
||||
"sse-child",
|
||||
r#"<section data-hemx-root="feed"><div data-hemx-sse="/events"></div></section>"#,
|
||||
"data-hemx-sse",
|
||||
"data-hemx-root",
|
||||
"expected placement on the same element as `data-hemx-root`",
|
||||
),
|
||||
] {
|
||||
let base = test_dir(&format!("hemx-build-invalid-placement-{case}-test"));
|
||||
let templates = base.join("templates");
|
||||
let out = base.join("out");
|
||||
let template_path = templates.join("placement.heml");
|
||||
let _ = std::fs::remove_dir_all(&base);
|
||||
std::fs::create_dir_all(&templates).unwrap();
|
||||
std::fs::write(templates.join("placement.heml"), template).unwrap();
|
||||
std::fs::write(&template_path, template).unwrap();
|
||||
|
||||
let err = app()
|
||||
let error = app()
|
||||
.template_dir(&templates)
|
||||
.out_dir(&out)
|
||||
.run()
|
||||
.unwrap_err();
|
||||
assert!(
|
||||
err.to_string().contains(attr),
|
||||
"missing attr in diagnostic: {err}"
|
||||
);
|
||||
assert!(
|
||||
err.to_string().contains("invalid"),
|
||||
"missing invalid-placement diagnostic: {err}"
|
||||
);
|
||||
assert!(
|
||||
err.to_string().contains(guidance),
|
||||
"missing placement guidance {guidance:?}: {err}"
|
||||
assert_eq!(error.kind(), io::ErrorKind::InvalidData);
|
||||
assert_eq!(
|
||||
error.to_string(),
|
||||
format!(
|
||||
"{}: invalid {attr} placement; {expectation}",
|
||||
template_path.display()
|
||||
)
|
||||
);
|
||||
|
||||
let _ = std::fs::remove_dir_all(&base);
|
||||
}
|
||||
|
||||
for (case, template) in [
|
||||
("nav-link", r#"<a href="/docs" data-hemx-nav="">Docs</a>"#),
|
||||
(
|
||||
"boost-container",
|
||||
r#"<nav data-hemx-boost=""><a href="/docs">Docs</a></nav>"#,
|
||||
),
|
||||
(
|
||||
"client-module-root",
|
||||
r#"<main data-hemx-root="app" data-hemx-client-module="/app.js"></main>"#,
|
||||
),
|
||||
(
|
||||
"sse-root",
|
||||
r#"<main data-hemx-root="feed" data-hemx-sse="/events"></main>"#,
|
||||
),
|
||||
] {
|
||||
let base = test_dir(&format!("hemx-build-valid-placement-{case}-test"));
|
||||
let templates = base.join("templates");
|
||||
let out = base.join("out");
|
||||
let _ = std::fs::remove_dir_all(&base);
|
||||
std::fs::create_dir_all(&templates).unwrap();
|
||||
std::fs::write(templates.join("placement.heml"), template).unwrap();
|
||||
app().template_dir(&templates).out_dir(&out).run().unwrap();
|
||||
let _ = std::fs::remove_dir_all(base);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
Reference in New Issue
Block a user