Implement audited findings
Some checks failed
Dependency security audit / rustsec (push) Failing after 3s
Some checks failed
Dependency security audit / rustsec (push) Failing after 3s
This commit is contained in:
@@ -60,6 +60,15 @@ impl TreeCommitter for Committer {
|
||||
}
|
||||
}
|
||||
|
||||
struct SabotagingCommitter(Box<dyn FnMut()>);
|
||||
|
||||
impl TreeCommitter for SabotagingCommitter {
|
||||
fn commit(&mut self, _change: &TreeCommit) -> Result<(), TreeCommitError> {
|
||||
(self.0)();
|
||||
Err(TreeCommitError::new("simulated operation failure"))
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn remove_entry_supports_confirmation_force_and_empty_cleanup() -> TestResult {
|
||||
let fixture = FixtureSet::load()?;
|
||||
@@ -514,6 +523,97 @@ fn collisions_auxiliary_files_and_commit_failure_never_leave_partial_trees() ->
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rollback_failures_from_sources_destinations_and_cleanup_retain_the_operation() -> TestResult {
|
||||
let fixture = FixtureSet::load()?;
|
||||
|
||||
let store = fixture.materialize_store("basic")?;
|
||||
let repository = Repository::open(store.path())?;
|
||||
let keys = KeyStore::load(fixture.path("keys"))?;
|
||||
let mut provider = Secrets::all(&fixture);
|
||||
let source_collision = store.path().join("email/personal.gpg");
|
||||
let mut committer = SabotagingCommitter(Box::new(move || {
|
||||
fs::create_dir(&source_collision).expect("block source restoration");
|
||||
}));
|
||||
let error = TreeMutator::new(&repository, &keys)
|
||||
.move_tree(
|
||||
&MoveRequest {
|
||||
source: "email/personal".into(),
|
||||
destination: "archive/personal".into(),
|
||||
force: false,
|
||||
},
|
||||
OverwriteDecision::Allow,
|
||||
None,
|
||||
&mut provider,
|
||||
&mut committer,
|
||||
)
|
||||
.expect_err("source restoration must fail");
|
||||
assert_operation_and_rollback(error);
|
||||
|
||||
let store = fixture.materialize_store("basic")?;
|
||||
let repository = Repository::open(store.path())?;
|
||||
let keys = KeyStore::load(fixture.path("keys"))?;
|
||||
let mut provider = Secrets::all(&fixture);
|
||||
let destination = store.path().join("archive/personal.gpg");
|
||||
let mut committer = SabotagingCommitter(Box::new(move || {
|
||||
fs::remove_file(&destination).expect("remove destination entry");
|
||||
fs::create_dir(&destination).expect("block destination restoration");
|
||||
}));
|
||||
let error = TreeMutator::new(&repository, &keys)
|
||||
.copy(
|
||||
&CopyRequest {
|
||||
source: "email/personal".into(),
|
||||
destination: "archive/personal".into(),
|
||||
force: false,
|
||||
},
|
||||
OverwriteDecision::Allow,
|
||||
None,
|
||||
&mut provider,
|
||||
&mut committer,
|
||||
)
|
||||
.expect_err("destination restoration must fail");
|
||||
assert_operation_and_rollback(error);
|
||||
|
||||
let store = fixture.materialize_store("basic")?;
|
||||
let repository = Repository::open(store.path())?;
|
||||
let keys = KeyStore::load(fixture.path("keys"))?;
|
||||
let mut provider = Secrets::all(&fixture);
|
||||
let blocker = store.path().join("archive/blocker");
|
||||
let mut committer = SabotagingCommitter(Box::new(move || {
|
||||
fs::write(&blocker, b"concurrent file").expect("block directory cleanup");
|
||||
}));
|
||||
let error = TreeMutator::new(&repository, &keys)
|
||||
.copy(
|
||||
&CopyRequest {
|
||||
source: "email/personal".into(),
|
||||
destination: "archive/personal".into(),
|
||||
force: false,
|
||||
},
|
||||
OverwriteDecision::Allow,
|
||||
None,
|
||||
&mut provider,
|
||||
&mut committer,
|
||||
)
|
||||
.expect_err("created-directory cleanup must fail");
|
||||
assert_operation_and_rollback(error);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn assert_operation_and_rollback(error: MutationError) {
|
||||
let MutationError::RollbackFailed {
|
||||
operation,
|
||||
rollback,
|
||||
} = error
|
||||
else {
|
||||
panic!("operation and rollback errors must both be retained");
|
||||
};
|
||||
assert!(matches!(*operation, MutationError::Commit(_)));
|
||||
assert!(matches!(
|
||||
*rollback,
|
||||
MutationError::Repository(_) | MutationError::TreeChanged { .. }
|
||||
));
|
||||
}
|
||||
|
||||
fn remove(entry: &str, recursive: bool, force: bool) -> RemoveRequest {
|
||||
RemoveRequest {
|
||||
entry: entry.into(),
|
||||
|
||||
Reference in New Issue
Block a user