use crate::Token;
use crate::accounting::Accounting;
use crate::contract::Violation;
use super::Pending;
#[derive(Debug, PartialEq, Eq)]
struct Payload(u32);
fn token(ledger: &mut Accounting, value: u32) -> Token<Payload> {
Token::new(ledger, Payload(value)).expect("mint a token")
}
#[test]
fn pushing_makes_an_operation_outstanding() {
let mut ledger = Accounting::new();
let mut pending: Pending<Payload> = Pending::new();
assert!(pending.is_empty());
let first = token(&mut ledger, 1);
let first_id = first.id();
pending.push(first, ());
let second = token(&mut ledger, 2);
let second_id = second.id();
pending.push(second, ());
assert_eq!(pending.len(), 2);
assert!(pending.abandon(first_id));
assert!(pending.abandon(second_id));
}
#[test]
#[should_panic(expected = "dropped unclaimed")]
fn dropping_a_map_that_still_holds_tokens_panics() {
let mut ledger = Accounting::new();
let mut pending: Pending<Payload> = Pending::new();
pending.push(token(&mut ledger, 1), ());
}
#[test]
fn abandoning_every_token_leaves_the_drop_silent() {
let mut ledger = Accounting::new();
let mut pending: Pending<Payload> = Pending::new();
let held = token(&mut ledger, 1);
let held_id = held.id();
pending.push(held, ());
assert!(pending.abandon(held_id));
}
#[test]
fn finish_reports_unclaimed_tokens_instead_of_panicking() {
let mut ledger = Accounting::new();
let mut pending: Pending<Payload> = Pending::new();
let held = token(&mut ledger, 7);
let held_id = held.id();
pending.push(held, ());
let violations = pending.finish();
assert_eq!(
violations,
vec![Violation::LeakedToken { user_data: held_id }]
);
}
#[test]
fn an_empty_map_finishes_with_no_violations() {
let pending: Pending<Payload> = Pending::new();
assert!(pending.finish().is_empty());
}
#[test]
fn a_checked_map_drives_its_own_contract() {
let mut ledger = Accounting::new();
let mut pending: Pending<Payload> = Pending::checked();
let held = token(&mut ledger, 3);
let held_id = held.id();
pending.push(held, ());
let contract = pending.contract().expect("a checked map has one");
assert!(
contract
.check_quiescent()
.contains(&Violation::Outstanding { user_data: held_id }),
"the oracle should know about a push nobody told it about by hand"
);
let violations = pending.finish();
assert!(violations.contains(&Violation::Outstanding { user_data: held_id }));
}
#[test]
fn an_unchecked_map_has_no_contract() {
let pending: Pending<Payload> = Pending::new();
assert!(pending.contract().is_none());
let _ = pending.finish();
}
#[test]
fn a_sidecar_is_stored_alongside_the_token() {
let mut ledger = Accounting::new();
let mut pending: Pending<Payload, (u32, &'static str)> = Pending::new();
let held = token(&mut ledger, 1);
let held_id = held.id();
pending.push(held, (17, "slot"));
assert_eq!(pending.len(), 1);
assert!(pending.abandon(held_id));
}
#[test]
#[should_panic(expected = "already pending")]
fn pushing_a_duplicate_identity_panics() {
let mut ledger = Accounting::new();
let mut pending: Pending<Payload> = Pending::new();
let held = token(&mut ledger, 1);
let id = held.id();
pending.push(held, ());
let mut other_ledger = Accounting::new();
let clash = token(&mut other_ledger, 2);
assert_eq!(clash.id(), id, "a fresh ledger restarts identities");
pending.push(clash, ());
}
#[test]
fn debug_does_not_print_payloads() {
let mut ledger = Accounting::new();
let mut pending: Pending<Payload> = Pending::new();
let held = token(&mut ledger, 0xBEEF);
let held_id = held.id();
pending.push(held, ());
let rendered = format!("{pending:?}");
assert!(rendered.contains("outstanding"), "{rendered}");
assert!(!rendered.contains("BEEF"), "payload leaked into Debug");
assert!(!rendered.contains("beef"), "payload leaked into Debug");
assert!(pending.abandon(held_id));
}