test(peer): cover update commit rollback
Add a focused transaction test for the branch where update extraction succeeds but promoting `.local.installing` to `local` fails. The fake unpacker creates a non-empty `local/` conflict after extraction, so the commit rename fails without adding production hooks or brittle platform-specific permission tricks. The assertion verifies the old install is restored from `.local.backup`, the conflict and staging directories are removed, the backup is consumed, and the intent is cleared back to None. Test Plan: - git diff --check - just fmt - just clippy - just test Follow-up-Plan: FOLLOW_UP_2.md
This commit is contained in:
@@ -477,6 +477,7 @@ mod tests {
|
||||
#[derive(Default)]
|
||||
struct FakeUnpacker {
|
||||
fail: bool,
|
||||
create_commit_conflict: bool,
|
||||
archives: Mutex<Vec<PathBuf>>,
|
||||
}
|
||||
|
||||
@@ -484,6 +485,15 @@ mod tests {
|
||||
fn failing() -> Self {
|
||||
Self {
|
||||
fail: true,
|
||||
create_commit_conflict: false,
|
||||
archives: Mutex::new(Vec::new()),
|
||||
}
|
||||
}
|
||||
|
||||
fn commit_conflict() -> Self {
|
||||
Self {
|
||||
fail: false,
|
||||
create_commit_conflict: true,
|
||||
archives: Mutex::new(Vec::new()),
|
||||
}
|
||||
}
|
||||
@@ -500,6 +510,14 @@ mod tests {
|
||||
eyre::bail!("forced unpack failure");
|
||||
}
|
||||
tokio::fs::write(dest.join("payload.txt"), b"installed").await?;
|
||||
if self.create_commit_conflict {
|
||||
let game_root = dest
|
||||
.parent()
|
||||
.ok_or_else(|| eyre::eyre!("staging dir should have parent"))?;
|
||||
let local_conflict = game_root.join(LOCAL_DIR);
|
||||
tokio::fs::create_dir_all(&local_conflict).await?;
|
||||
tokio::fs::write(local_conflict.join("conflict.txt"), b"conflict").await?;
|
||||
}
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
@@ -608,6 +626,34 @@ mod tests {
|
||||
assert_eq!(intent.state, InstallIntentState::None);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn update_commit_rename_failure_restores_previous_local() {
|
||||
let temp = TempDir::new();
|
||||
let root = temp.game_root();
|
||||
write_file(&root.join("game.eti"), b"archive");
|
||||
write_file(&root.join("version.ini"), b"20250101");
|
||||
write_file(&root.join("local").join("old.txt"), b"old");
|
||||
|
||||
let err = update(&root, "game", Arc::new(FakeUnpacker::commit_conflict()))
|
||||
.await
|
||||
.expect_err("update should fail at commit rename");
|
||||
|
||||
assert!(
|
||||
err.to_string().contains("failed to promote update"),
|
||||
"{err:?}"
|
||||
);
|
||||
assert_eq!(
|
||||
std::fs::read(root.join("local").join("old.txt"))
|
||||
.expect("old install should be restored"),
|
||||
b"old"
|
||||
);
|
||||
assert!(!root.join("local").join("conflict.txt").exists());
|
||||
assert!(!root.join(".local.installing").exists());
|
||||
assert!(!root.join(".local.backup").exists());
|
||||
let intent = read_intent(&root, "game").await;
|
||||
assert_eq!(intent.state, InstallIntentState::None);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn update_success_promotes_new_local_and_removes_backup() {
|
||||
let temp = TempDir::new();
|
||||
|
||||
Reference in New Issue
Block a user