#![expect(clippy::unwrap_used, reason = "test asserts on known-present values")]
use super::{AttestResult, attest_path, attests, attests_post, remove, write, write_in_progress};
use crate::Checksum;
use crate::fs::{Fs, StdFs};
use std::sync::Arc;
fn ck(v: u128) -> Checksum {
Checksum::from_raw(v)
}
#[test]
fn attests_roundtrips_a_plaintext_attestation() {
let dir = tempfile::tempdir().unwrap();
let path = dir.path().join("7");
let fs: Arc<dyn Fs> = Arc::new(StdFs);
write(&*fs, &path, None, 7, ck(100), ck(200)).unwrap();
assert!(
matches!(
attests(&*fs, &path, None, 7, ck(200), ck(100)),
AttestResult::Attests
),
"a matching attestation is trusted",
);
}
#[test]
fn attests_rejects_a_mismatched_digest_or_id() {
let dir = tempfile::tempdir().unwrap();
let path = dir.path().join("7");
let fs: Arc<dyn Fs> = Arc::new(StdFs);
write(&*fs, &path, None, 7, ck(100), ck(200)).unwrap();
assert!(matches!(
attests(&*fs, &path, None, 7, ck(999), ck(100)),
AttestResult::Absent
));
assert!(matches!(
attests(&*fs, &path, None, 7, ck(200), ck(999)),
AttestResult::Absent
));
assert!(matches!(
attests(&*fs, &path, None, 8, ck(200), ck(100)),
AttestResult::Absent
));
}
#[test]
fn attests_returns_absent_without_a_sidecar() {
let dir = tempfile::tempdir().unwrap();
let path = dir.path().join("7");
let fs: Arc<dyn Fs> = Arc::new(StdFs);
assert!(matches!(
attests(&*fs, &path, None, 7, ck(200), ck(100)),
AttestResult::Absent
));
}
#[test]
fn attests_rejects_a_truncated_sidecar() {
let dir = tempfile::tempdir().unwrap();
let path = dir.path().join("7");
let fs: Arc<dyn Fs> = Arc::new(StdFs);
write(&*fs, &path, None, 7, ck(100), ck(200)).unwrap();
let ap = attest_path(&path);
let bytes = std::fs::read(&ap).unwrap();
let Some(short) = bytes.get(..bytes.len() - 1) else {
panic!("the written sidecar is non-empty");
};
std::fs::write(&ap, short).unwrap();
assert!(
matches!(
attests(&*fs, &path, None, 7, ck(200), ck(100)),
AttestResult::Inconclusive
),
"a short attestation is inconclusive, not attesting",
);
}
#[test]
fn attests_rejects_an_oversized_sidecar() {
let dir = tempfile::tempdir().unwrap();
let path = dir.path().join("7");
let fs: Arc<dyn Fs> = Arc::new(StdFs);
write(&*fs, &path, None, 7, ck(100), ck(200)).unwrap();
let ap = attest_path(&path);
let mut bytes = std::fs::read(&ap).unwrap();
bytes.extend(std::iter::repeat_n(0u8, 1 << 20));
std::fs::write(&ap, &bytes).unwrap();
assert!(
matches!(
attests(&*fs, &path, None, 7, ck(200), ck(100)),
AttestResult::Inconclusive
),
"an oversized attestation is inconclusive, not attesting",
);
}
#[test]
fn attests_is_inconclusive_when_the_sidecar_metadata_read_fails() {
use crate::fs::{Fault, FaultFs, FaultOp, FaultRule};
use crate::io::ErrorKind;
let dir = tempfile::tempdir().unwrap();
let path = dir.path().join("7");
let fault = FaultFs::new(StdFs);
let injector = fault.injector();
let fs: Arc<dyn Fs> = Arc::new(fault);
write(&*fs, &path, None, 7, ck(100), ck(200)).unwrap();
injector.arm(
FaultRule::new(FaultOp::Metadata, Fault::Error(ErrorKind::Other)).on_path(".heal-attest"),
);
assert!(
matches!(
attests(&*fs, &path, None, 7, ck(200), ck(100)),
AttestResult::Inconclusive
),
"a metadata probe failure is inconclusive, not attesting",
);
}
#[test]
fn attests_post_attests_on_a_matching_post_regardless_of_pre() {
let dir = tempfile::tempdir().unwrap();
let path = dir.path().join("7");
let fs: Arc<dyn Fs> = Arc::new(StdFs);
write(&*fs, &path, None, 7, ck(100), ck(200)).unwrap();
assert!(
matches!(
attests_post(&*fs, &path, None, 7, ck(200)),
AttestResult::Attests
),
"a matching post attests independent of the recorded pre",
);
}
#[test]
fn attests_post_returns_absent_on_a_nonmatching_post_or_id() {
let dir = tempfile::tempdir().unwrap();
let path = dir.path().join("7");
let fs: Arc<dyn Fs> = Arc::new(StdFs);
write(&*fs, &path, None, 7, ck(100), ck(200)).unwrap();
assert!(matches!(
attests_post(&*fs, &path, None, 7, ck(999)),
AttestResult::Absent
));
assert!(matches!(
attests_post(&*fs, &path, None, 8, ck(200)),
AttestResult::Absent
));
remove(&*fs, &path);
assert!(matches!(
attests_post(&*fs, &path, None, 7, ck(200)),
AttestResult::Absent
));
}
#[test]
fn attests_post_is_inconclusive_when_the_metadata_read_fails() {
use crate::fs::{Fault, FaultFs, FaultOp, FaultRule};
use crate::io::ErrorKind;
let dir = tempfile::tempdir().unwrap();
let path = dir.path().join("7");
let fault = FaultFs::new(StdFs);
let injector = fault.injector();
let fs: Arc<dyn Fs> = Arc::new(fault);
write(&*fs, &path, None, 7, ck(100), ck(200)).unwrap();
injector.arm(
FaultRule::new(FaultOp::Metadata, Fault::Error(ErrorKind::Other)).on_path(".heal-attest"),
);
assert!(
matches!(
attests_post(&*fs, &path, None, 7, ck(200)),
AttestResult::Inconclusive
),
"a metadata probe failure is inconclusive, not non-attesting",
);
}
#[test]
fn attests_rejects_a_legacy_in_progress_marker() {
let dir = tempfile::tempdir().unwrap();
let path = dir.path().join("7");
let fs: Arc<dyn Fs> = Arc::new(StdFs);
write_in_progress(&*fs, &path, None, 7, ck(100)).unwrap();
assert!(
matches!(
attests(&*fs, &path, None, 7, ck(200), ck(100)),
AttestResult::Absent
),
"a legacy in-progress marker is non-attesting (Absent)",
);
}
#[test]
fn remove_deletes_the_sidecar() {
let dir = tempfile::tempdir().unwrap();
let path = dir.path().join("7");
let fs: Arc<dyn Fs> = Arc::new(StdFs);
write(&*fs, &path, None, 7, ck(100), ck(200)).unwrap();
assert!(attest_path(&path).exists());
remove(&*fs, &path);
assert!(!attest_path(&path).exists());
}
#[test]
fn a_failed_completed_write_preserves_the_in_progress_marker() {
use super::{attests_in_progress, write_in_progress};
use crate::fs::{Fault, FaultFs, FaultOp, FaultRule};
use crate::io::ErrorKind;
let dir = tempfile::tempdir().unwrap();
let path = dir.path().join("7");
let fault = FaultFs::new(StdFs);
let injector = fault.injector();
let fs: Arc<dyn Fs> = Arc::new(fault);
write_in_progress(&*fs, &path, None, 7, ck(100)).unwrap();
assert!(
attests_in_progress(&*fs, &path, None, 7, ck(100)),
"the in-progress marker is written and durable",
);
injector.arm(
FaultRule::new(FaultOp::Write, Fault::Error(ErrorKind::Other)).on_path(".heal-attest"),
);
assert!(
write(&*fs, &path, None, 7, ck(100), ck(200)).is_err(),
"the faulted completed-attestation write must fail",
);
assert!(
attests_in_progress(&*fs, &path, None, 7, ck(100)),
"a failed completed write must leave the in-progress marker intact",
);
}
#[cfg(feature = "encryption")]
#[test]
fn attests_rejects_a_tampered_or_wrong_key_encrypted_sidecar() {
use crate::encryption::Aes256GcmProvider;
let dir = tempfile::tempdir().unwrap();
let path = dir.path().join("7");
let fs: Arc<dyn Fs> = Arc::new(StdFs);
let enc = Aes256GcmProvider::new(&[7u8; 32]);
write(&*fs, &path, Some(&enc), 7, ck(100), ck(200)).unwrap();
assert!(matches!(
attests(&*fs, &path, Some(&enc), 7, ck(200), ck(100)),
AttestResult::Attests
));
let ap = attest_path(&path);
let mut bytes = std::fs::read(&ap).unwrap();
if let Some(b) = bytes.last_mut() {
*b ^= 0xFF;
}
std::fs::write(&ap, &bytes).unwrap();
assert!(
matches!(
attests(&*fs, &path, Some(&enc), 7, ck(200), ck(100)),
AttestResult::Inconclusive
),
"a tampered encrypted attestation is inconclusive, not attesting",
);
write(&*fs, &path, Some(&enc), 7, ck(100), ck(200)).unwrap();
let wrong = Aes256GcmProvider::new(&[9u8; 32]);
assert!(
matches!(
attests(&*fs, &path, Some(&wrong), 7, ck(200), ck(100)),
AttestResult::Inconclusive
),
"an attestation sealed under a different key is inconclusive",
);
}