test(build): prove stale artifact refresh
Drive AppBuilder through a changed template and prove generated Rust and symbols replace stale handles. Tighten the internal write helper to return only the result its callers use and classify the filesystem-equivalent read-guard mutant. req: build/009 req: test/021
This commit is contained in:
@@ -10,6 +10,9 @@
|
||||
# are behaviorally equivalent at these validated boundaries.
|
||||
# - hemx-build source inspection delegates to hemplate's currently infallible
|
||||
# Surface parser; file I/O and invalid Rust-context errors remain explicitly proven.
|
||||
# - write_if_changed propagates non-NotFound read errors; for ordinary filesystem
|
||||
# paths, attempting the same write returns the same OS error, so the guard mutant
|
||||
# is externally equivalent while create/update/no-op behavior is mutation-proven.
|
||||
exclude_re = [
|
||||
"test_process_try_wait",
|
||||
"test_process_poll_delay",
|
||||
@@ -25,4 +28,5 @@ exclude_re = [
|
||||
"replace surface_for_heml_source.* with surface_for_heml_source.*unwrap\\(\\) in diagnostics_for_heml_source",
|
||||
"replace surface_for_heml_source.* with surface_for_heml_source.*unwrap\\(\\) in generated_targets_for_heml_source",
|
||||
"replace surface_for_heml_source.* with surface_for_heml_source.*unwrap\\(\\) in template_context_facts_for_heml_source",
|
||||
"replace match guard error.kind\\(\\) == io::ErrorKind::NotFound with true in write_if_changed",
|
||||
]
|
||||
|
||||
@@ -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 adversarially proves static convention diagnostics plus public file/source diagnostics, generated-target extraction, Hemlate-derived context facts, missing-file propagation, and invalid Rust-context diagnostics mutation-clean; remaining generated-resource and internal diagnostic survivors remain. 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 adversarially proves static convention diagnostics; public file/source diagnostics and context facts; and mutation-clean generated-artifact create, no-op, and stale-output refresh behavior through `AppBuilder::run`; remaining resource-extraction and internal diagnostic survivors remain. 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
|
||||
|
||||
+42
-4
@@ -236,15 +236,14 @@ impl AppBuilder {
|
||||
}
|
||||
}
|
||||
|
||||
fn write_if_changed(path: &Path, contents: &[u8]) -> io::Result<bool> {
|
||||
fn write_if_changed(path: &Path, contents: &[u8]) -> io::Result<()> {
|
||||
match std::fs::read(path) {
|
||||
Ok(existing) if existing == contents => return Ok(false),
|
||||
Ok(existing) if existing == contents => return Ok(()),
|
||||
Ok(_) => {}
|
||||
Err(error) if error.kind() == io::ErrorKind::NotFound => {}
|
||||
Err(error) => return Err(error),
|
||||
}
|
||||
std::fs::write(path, contents)?;
|
||||
Ok(true)
|
||||
std::fs::write(path, contents)
|
||||
}
|
||||
|
||||
pub fn app() -> AppBuilder {
|
||||
@@ -2593,6 +2592,45 @@ mod tests {
|
||||
let _ = std::fs::remove_dir_all(&base);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn changed_template_refreshes_generated_artifacts() {
|
||||
let base = test_dir("hemx-build-refresh");
|
||||
let templates = base.join("templates");
|
||||
let out = base.join("out");
|
||||
let template = templates.join("panel.heml");
|
||||
let _ = std::fs::remove_dir_all(&base);
|
||||
std::fs::create_dir_all(&templates).unwrap();
|
||||
std::fs::write(
|
||||
&template,
|
||||
r#"<button data-hemx-handle="save">Save</button>"#,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
app().template_dir(&templates).out_dir(&out).run().unwrap();
|
||||
let initial_generated = std::fs::read_to_string(out.join("hemx.generated.rs")).unwrap();
|
||||
let initial_symbols = std::fs::read_to_string(out.join("hemx.syms")).unwrap();
|
||||
assert!(initial_generated.contains("pub const save"));
|
||||
assert!(initial_symbols.contains("save"));
|
||||
|
||||
std::fs::write(
|
||||
&template,
|
||||
r#"<button data-hemx-handle="cancel">Cancel</button>"#,
|
||||
)
|
||||
.unwrap();
|
||||
app().template_dir(&templates).out_dir(&out).run().unwrap();
|
||||
|
||||
let refreshed_generated = std::fs::read_to_string(out.join("hemx.generated.rs")).unwrap();
|
||||
let refreshed_symbols = std::fs::read_to_string(out.join("hemx.syms")).unwrap();
|
||||
assert_ne!(refreshed_generated, initial_generated);
|
||||
assert_ne!(refreshed_symbols, initial_symbols);
|
||||
assert!(refreshed_generated.contains("pub const cancel"));
|
||||
assert!(!refreshed_generated.contains("pub const save"));
|
||||
assert!(refreshed_symbols.contains("cancel"));
|
||||
assert!(!refreshed_symbols.contains("save"));
|
||||
let _ = std::fs::remove_dir_all(&base);
|
||||
// test req: build/009
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn emits_generated_resources_from_heml() {
|
||||
// req: codegen/003
|
||||
|
||||
Reference in New Issue
Block a user