From 17cf5b4e91ebf0db0314e90fc4c4d8b4d7826c3d Mon Sep 17 00:00:00 2001 From: skylar taylor-barrick Date: Mon, 12 Oct 2026 13:57:37 -0600 Subject: [PATCH] test(recovery): make failed republishing fixtures deterministic Force actual failed recovery publication at the reserved destination so privileged runners exercise the intended retention path. Keep the normal writer, publication order and existing assertions; enable these fixtures across platforms. Validation: all six local gates and the FreeBSD root VM workspace build/tests. PR #627. --- crates/engine/src/recovery.rs | 48 ++++++++++++++++++++--------------- 1 file changed, 27 insertions(+), 21 deletions(-) diff --git a/crates/engine/src/recovery.rs b/crates/engine/src/recovery.rs index 85308be..33b9a2a 100644 --- a/crates/engine/src/recovery.rs +++ b/crates/engine/src/recovery.rs @@ -656,6 +656,10 @@ pub fn list(dir: &Path) -> Vec<(u64, Value)> { /// Entries belonging to documents already open in this session's recovery folder are left alone. /// Returns the opened documents' indices. pub fn open(s: &mut Session, dir: &Path) -> Result> { + open_with_publisher(s, dir, |path, bytes| publish_recovery_file(path, bytes, false)) +} + +fn open_with_publisher(s: &mut Session, dir: &Path, mut publish: impl FnMut(&Path, &[u8]) -> Result<()>) -> Result> { #[cfg(not(target_arch = "wasm32"))] { validate_recovery_dir(dir)?; @@ -712,14 +716,14 @@ pub fn open(s: &mut Session, dir: &Path) -> Result> { // clean up every snapshot this document actually owns rather than an unrelated same uid. if let Some(state) = s.docs.get_mut(index) { let (document, metadata) = files(&destination, state.uid); - let publish = designcraft_format::save(state.doc.as_ref()) + let published = designcraft_format::save(state.doc.as_ref()) .map_err(|e| EngineError::Other(e.to_string())) - .and_then(|bytes| publish_recovery_file(&document, &bytes, false)); - match publish { + .and_then(|bytes| publish(&document, &bytes)); + match published { Ok(()) => { state.recovery_entries.push((destination.clone(), state.uid)); let meta = json!({"path": state.path, "title": state.title(), "saved": designcraft_doc::vars::now()}); - match publish_recovery_file(&metadata, meta.to_string().as_bytes(), false) { + match publish(&metadata, meta.to_string().as_bytes()) { Ok(()) => { discard(dir, uid); state.recovery_entries = vec![(destination.clone(), state.uid)]; @@ -801,20 +805,32 @@ mod tests { assert_eq!(list(dir.path()).len(), 1); } - #[cfg(unix)] + /// Directory collisions fail even for privileged runners. Creating the blocker in the + /// publisher occurs after UID reservation, which would otherwise skip an occupied path. + fn open_with_failed_republication(s: &mut Session, dir: &Path) -> Result> { + let mut attempts = 0; + let opened = open_with_publisher(s, dir, |path, bytes| { + assert_eq!(path.extension().and_then(|extension| extension.to_str()), Some("designcraft")); + std::fs::create_dir(path).unwrap(); + let result = publish_recovery_file(path, bytes, false); + std::fs::remove_dir(path).unwrap(); + assert!(result.is_err(), "a directory must obstruct actual recovery publication"); + attempts += 1; + result + }); + assert_eq!(attempts, 1, "the fixture must exercise the actual failed publication"); + opened + } + #[test] fn a_failed_recovery_rewrite_preserves_the_original() { - use std::os::unix::fs::PermissionsExt; let dir = tempfile::tempdir().unwrap(); assert_eq!(save(&unsaved("keep the original"), dir.path()).unwrap(), 1); let entries = list(dir.path()); let original = files(dir.path(), entries[0].0).0; let bytes = std::fs::read(&original).unwrap(); - std::fs::set_permissions(dir.path(), std::fs::Permissions::from_mode(0o500)).unwrap(); let mut reopened = Session::new(); - let result = open(&mut reopened, dir.path()); - // Restore before assertions so a failing test does not leave an unremovable fixture. - std::fs::set_permissions(dir.path(), std::fs::Permissions::from_mode(0o700)).unwrap(); + let result = open_with_failed_republication(&mut reopened, dir.path()); assert_eq!(result.unwrap().len(), 1); assert_eq!(texts(&reopened), ["keep the original"]); assert_eq!(std::fs::read(original).unwrap(), bytes); @@ -918,9 +934,7 @@ mod tests { } } - #[cfg(unix)] fn failed_republication_session() -> (Session, tempfile::TempDir, u64) { - use std::os::unix::fs::PermissionsExt; let root = tempfile::tempdir().unwrap(); let mut session = Session::new(); session.execute("file.new", &json!({})).unwrap(); @@ -929,15 +943,12 @@ mod tests { original.docs[0].uid = live_uid; save(&original, root.path()).unwrap(); session.recovery_dir = Some(root.path().to_path_buf()); - std::fs::set_permissions(root.path(), std::fs::Permissions::from_mode(0o500)).unwrap(); - let recovered = open(&mut session, root.path()); - std::fs::set_permissions(root.path(), std::fs::Permissions::from_mode(0o700)).unwrap(); + let recovered = open_with_failed_republication(&mut session, root.path()); assert_eq!(recovered.unwrap().len(), 1); assert_ne!(session.docs[1].uid, live_uid); (session, root, live_uid) } - #[cfg(unix)] #[test] fn retained_source_survives_a_coincident_live_uid_checkpoint_failure() { let (mut session, root, live_uid) = failed_republication_session(); @@ -950,7 +961,6 @@ mod tests { assert_eq!(std::fs::read(files(root.path(), live_uid).0).unwrap(), original); } - #[cfg(unix)] #[test] fn clean_autosave_preserves_another_documents_retained_source() { let (mut session, root, live_uid) = failed_republication_session(); @@ -962,7 +972,6 @@ mod tests { assert_eq!(std::fs::read(checkpoint).unwrap(), original); } - #[cfg(unix)] #[test] fn closing_a_coincident_document_preserves_another_documents_retained_source() { let (mut session, root, live_uid) = failed_republication_session(); @@ -972,7 +981,6 @@ mod tests { assert_eq!(std::fs::read(checkpoint).unwrap(), original); } - #[cfg(unix)] #[test] fn saving_a_coincident_document_preserves_another_documents_retained_source() { let (mut session, root, live_uid) = failed_republication_session(); @@ -983,7 +991,6 @@ mod tests { assert_eq!(std::fs::read(checkpoint).unwrap(), original); } - #[cfg(unix)] #[test] fn reverting_a_coincident_document_preserves_another_documents_retained_source() { let (mut session, root, live_uid) = failed_republication_session(); @@ -998,7 +1005,6 @@ mod tests { assert_eq!(std::fs::read(checkpoint).unwrap(), original); } - #[cfg(unix)] #[test] fn retained_source_provenance_is_consumed_before_its_uid_is_reused() { let (mut session, root, live_uid) = failed_republication_session();