use std::collections::BTreeMap;
use std::error::Error;
use super::fenced_attach_terminal::{ComposedTerminalRestoreParts, ComposedTerminalValidation};
#[test]
fn checkpoint_audit_restores_exact_reservations_through_served_codec() -> Result<(), Box<dyn Error>>
{
let pairs = BTreeMap::from([(2, 5), (3, 8)]);
let parts = ComposedTerminalRestoreParts::from_reservations(pairs);
let owner = ComposedTerminalValidation::from_restore_parts(parts, 2, 8)?;
assert_eq!(
owner.encode_checkpoint()?,
reservation_bytes(&[(2, 5), (3, 8)])
);
let restored =
ComposedTerminalValidation::decode_checkpoint(&owner.encode_checkpoint()?, 2, 8)?;
assert_eq!(restored.active_reservation_count(), 2);
assert_eq!(
restored.encode_checkpoint()?,
reservation_bytes(&[(2, 5), (3, 8)])
);
Ok(())
}
#[test]
fn checkpoint_audit_refuses_uncovered_reversed_and_duplicate_sources() {
for pairs in [
BTreeMap::from([(2, 9)]),
BTreeMap::from([(5, 5)]),
BTreeMap::from([(6, 5)]),
BTreeMap::from([(2, 8), (3, 8)]),
] {
let parts = ComposedTerminalRestoreParts::from_reservations(pairs);
assert!(ComposedTerminalValidation::from_restore_parts(parts, 2, 8).is_err());
}
}
#[test]
fn checkpoint_audit_uses_current_capacity_instead_of_the_saved_configuration() {
let parts = ComposedTerminalRestoreParts::from_reservations(BTreeMap::from([(2, 5), (3, 8)]));
assert!(ComposedTerminalValidation::from_restore_parts(parts, 1, 8).is_err());
}
#[test]
fn borrowed_audit_checkpoint_keeps_owner_and_restores_the_same_pairs() -> Result<(), Box<dyn Error>>
{
let owner = ComposedTerminalValidation::from_restore_parts(
ComposedTerminalRestoreParts::from_reservations(BTreeMap::from([(2, 5), (3, 8)])),
2,
8,
)?;
let borrowed = owner.encode_checkpoint()?;
let restored = ComposedTerminalValidation::decode_checkpoint(&borrowed, 2, 8)?;
assert_eq!(borrowed, restored.encode_checkpoint()?);
assert_eq!(owner.active_reservation_count(), 2);
Ok(())
}
#[test]
fn audit_checkpoint_round_trip_retains_exact_pairs_and_refuses_trailing_bytes()
-> Result<(), Box<dyn Error>> {
let owner = ComposedTerminalValidation::from_restore_parts(
ComposedTerminalRestoreParts::from_reservations(BTreeMap::from([(2, 5), (3, 8)])),
2,
8,
)?;
let bytes = owner.encode_checkpoint()?;
let restored = ComposedTerminalValidation::decode_checkpoint(&bytes, 2, 8)?;
assert_eq!(restored.encode_checkpoint()?, owner.encode_checkpoint()?);
assert_eq!(restored.encode_checkpoint()?, bytes);
assert!(ComposedTerminalValidation::decode_checkpoint(&bytes, 1, 8).is_err());
assert!(ComposedTerminalValidation::decode_checkpoint(&bytes, 2, 7).is_err());
let mut trailing = bytes;
trailing.push(0);
assert!(ComposedTerminalValidation::decode_checkpoint(&trailing, 2, 8).is_err());
Ok(())
}
#[test]
fn audit_checkpoint_refuses_duplicate_and_reversed_pending_keys() {
for pairs in [[(2_u64, 5_u64), (2, 8)], [(3, 8), (2, 5)]] {
let mut bytes = 2_u32.to_le_bytes().to_vec();
for (pending, recovered) in pairs {
bytes.extend_from_slice(&pending.to_le_bytes());
bytes.extend_from_slice(&recovered.to_le_bytes());
}
assert!(ComposedTerminalValidation::decode_checkpoint(&bytes, 2, 8).is_err());
}
}
fn reservation_bytes(pairs: &[(u64, u64)]) -> Vec<u8> {
let mut bytes = 2_u32.to_le_bytes().to_vec();
for (pending, recovered) in pairs {
bytes.extend_from_slice(&pending.to_le_bytes());
bytes.extend_from_slice(&recovered.to_le_bytes());
}
bytes
}