use super::*;
use crate::{
model::artifacts::ArtifactChecksumRecord,
tests::support::{
execution_workflow::{assert_pending, child, prepare_stage, workflow},
response_fault::ReceiptFault,
},
};
use std::{cell::Cell, fs};
#[derive(Debug, thiserror::Error)]
enum Failure {
#[error(transparent)]
Journal(#[from] AttemptJournalError),
#[error("receipt does not match the original Applied operation")]
Receipt,
}
#[test]
fn changed_outcome_attempt_or_request_never_prepares_or_changes_original_spending() {
for fault in [
ReceiptFault::NotApplied,
ReceiptFault::Attempt,
ReceiptFault::Request,
] {
let prepared = prepare_stage("ic-backup-receipt-refusal", &workflow(), 0, child(0));
let stage = prepared.open();
let sequence = 42;
let authority = stage.plan().attempt_authority(sequence).unwrap();
let mut journal = AttemptJournalGuard::open(stage.layout().unwrap(), &authority).unwrap();
let attempt = journal
.reserve_planned_mutation(&stage.plan().digest())
.unwrap();
let original = fs::read(journal.path()).unwrap();
let request = ArtifactChecksumRecord::from_hash(authority.binding().request()).unwrap();
let mut receipt = MutationReceiptRequest {
attempt,
request: request.clone(),
outcome: MutationOutcomeRecord::Applied,
evidence: ArtifactChecksumRecord::from_bytes(b"qualified original"),
};
fault.apply(&mut receipt, &"00".repeat(32));
let prepared_calls = Cell::new(0);
assert!(matches!(
record_applied(
&mut journal,
receipt,
attempt,
request.hash(),
Failure::Receipt,
|| {
prepared_calls.set(1);
Ok(())
}
),
Err(Failure::Receipt)
));
assert_eq!(prepared_calls.get(), 0);
assert_eq!(fs::read(journal.path()).unwrap(), original);
drop(journal);
assert_pending(&stage, sequence);
}
}