#![forbid(unsafe_code)]
#![warn(missing_docs)]
use std::fmt::Debug;
use pacta_contract::{Pact, Registry, Retainer, Timestamp};
use uuid::Uuid;
pub const LEASE_MILLIS: u64 = 1000;
const DOCKET: &str = "conformance";
fn at(millis: u64) -> Timestamp {
Timestamp::from_millis(millis)
}
fn a_pact_on(docket: &str) -> Pact {
Pact::new(
Uuid::new_v4(),
docket.to_string(),
"conformance".to_string(),
Vec::new(),
)
}
fn a_pact() -> Pact {
a_pact_on(DOCKET)
}
pub fn run<R, F>(make: F)
where
R: Registry,
R::Error: Debug,
F: Fn(Vec<Pact>, u64) -> R,
{
no_available_pact_returns_none(&make);
unrequested_docket_is_not_claimed(&make);
claim_returns_claim_with_lease(&make);
held_pact_not_reclaimable_before_expiry(&make);
expired_lease_lapses_and_reclaims_with_rotated_retainer(&make);
stale_retainer_settle_rejected_after_reclaim(&make);
late_fulfill_before_reclaim_succeeds(&make);
fulfill_settles_and_pact_not_claimable(&make);
breach_settles_terminally(&make);
released_pact_withheld_until_reclaimable(&make);
released_pact_reclaimable_at_its_instant(&make);
immediate_reclaim_reclaims_like_lapse(&make);
release_rotates_authority_from_prior_holder(&make);
heartbeat_extends_lease_preventing_lapse(&make);
heartbeat_on_lapsed_lease_rejected(&make);
heartbeat_unknown_retainer_rejected(&make);
}
fn no_available_pact_returns_none<R, F>(make: &F)
where
R: Registry,
R::Error: Debug,
F: Fn(Vec<Pact>, u64) -> R,
{
let registry = make(Vec::new(), LEASE_MILLIS);
assert!(
registry
.claim(&[DOCKET], at(0))
.expect("claim should not error")
.is_none(),
"an empty registry must yield no claim"
);
}
fn unrequested_docket_is_not_claimed<R, F>(make: &F)
where
R: Registry,
R::Error: Debug,
F: Fn(Vec<Pact>, u64) -> R,
{
let registry = make(vec![a_pact_on("other")], LEASE_MILLIS);
assert!(
registry
.claim(&[DOCKET], at(0))
.expect("claim should not error")
.is_none(),
"a pact on an unrequested docket must not be claimed"
);
assert!(
registry
.claim(&["other"], at(0))
.expect("claim should not error")
.is_some(),
"the same pact must be claimable from its own docket"
);
}
fn claim_returns_claim_with_lease<R, F>(make: &F)
where
R: Registry,
R::Error: Debug,
F: Fn(Vec<Pact>, u64) -> R,
{
let registry = make(vec![a_pact()], LEASE_MILLIS);
let claim = registry
.claim(&[DOCKET], at(100))
.expect("claim should not error")
.expect("a pact should be claimable");
assert_eq!(
claim.lease_expiry,
at(100 + LEASE_MILLIS),
"lease expiry must be now plus the lease duration"
);
}
fn held_pact_not_reclaimable_before_expiry<R, F>(make: &F)
where
R: Registry,
R::Error: Debug,
F: Fn(Vec<Pact>, u64) -> R,
{
let registry = make(vec![a_pact()], LEASE_MILLIS);
let _first = registry
.claim(&[DOCKET], at(0))
.expect("claim should not error")
.expect("a pact should be claimable");
assert!(
registry
.claim(&[DOCKET], at(500))
.expect("claim should not error")
.is_none(),
"a held pact must not be reclaimable before its lease expires"
);
}
fn expired_lease_lapses_and_reclaims_with_rotated_retainer<R, F>(make: &F)
where
R: Registry,
R::Error: Debug,
F: Fn(Vec<Pact>, u64) -> R,
{
let registry = make(vec![a_pact()], LEASE_MILLIS);
let first = registry
.claim(&[DOCKET], at(0))
.expect("claim should not error")
.expect("a pact should be claimable");
let second = registry
.claim(&[DOCKET], at(1500))
.expect("claim should not error")
.expect("an expired pact should be reclaimable through the claim path");
assert_ne!(
first.retainer.id(),
second.retainer.id(),
"reclaiming a lapsed pact must rotate the retainer"
);
assert_eq!(
second.lease_expiry,
at(1500 + LEASE_MILLIS),
"the reclaim must set a fresh lease"
);
}
fn stale_retainer_settle_rejected_after_reclaim<R, F>(make: &F)
where
R: Registry,
R::Error: Debug,
F: Fn(Vec<Pact>, u64) -> R,
{
let registry = make(vec![a_pact()], LEASE_MILLIS);
let first = registry
.claim(&[DOCKET], at(0))
.expect("claim should not error")
.expect("a pact should be claimable");
let _second = registry
.claim(&[DOCKET], at(1500))
.expect("claim should not error")
.expect("an expired pact should be reclaimable");
assert!(
registry.fulfill(&first.retainer).is_err(),
"the prior holder must not settle after a reclaim (at-least-once safety)"
);
}
fn late_fulfill_before_reclaim_succeeds<R, F>(make: &F)
where
R: Registry,
R::Error: Debug,
F: Fn(Vec<Pact>, u64) -> R,
{
let registry = make(vec![a_pact()], LEASE_MILLIS);
let claim = registry
.claim(&[DOCKET], at(0))
.expect("claim should not error")
.expect("a pact should be claimable");
assert!(
registry.fulfill(&claim.retainer).is_ok(),
"a late fulfill before any reclaim must settle"
);
assert!(
registry
.claim(&[DOCKET], at(9999))
.expect("claim should not error")
.is_none(),
"a settled pact must not be claimable"
);
}
fn fulfill_settles_and_pact_not_claimable<R, F>(make: &F)
where
R: Registry,
R::Error: Debug,
F: Fn(Vec<Pact>, u64) -> R,
{
let registry = make(vec![a_pact()], LEASE_MILLIS);
let claim = registry
.claim(&[DOCKET], at(0))
.expect("claim should not error")
.expect("a pact should be claimable");
registry
.fulfill(&claim.retainer)
.expect("fulfill should settle");
assert!(
registry
.claim(&[DOCKET], at(0))
.expect("claim should not error")
.is_none(),
"a fulfilled pact must not be claimable"
);
}
fn breach_settles_terminally<R, F>(make: &F)
where
R: Registry,
R::Error: Debug,
F: Fn(Vec<Pact>, u64) -> R,
{
let registry = make(vec![a_pact()], LEASE_MILLIS);
let claim = registry
.claim(&[DOCKET], at(0))
.expect("claim should not error")
.expect("a pact should be claimable");
registry
.breach(&claim.retainer)
.expect("breach should settle");
assert!(
registry
.claim(&[DOCKET], at(5000))
.expect("claim should not error")
.is_none(),
"a breached pact must not be claimable, even after its lease would have expired"
);
}
fn released_pact_withheld_until_reclaimable<R, F>(make: &F)
where
R: Registry,
R::Error: Debug,
F: Fn(Vec<Pact>, u64) -> R,
{
let registry = make(vec![a_pact()], LEASE_MILLIS);
let claim = registry
.claim(&[DOCKET], at(0))
.expect("claim should not error")
.expect("a pact should be claimable");
registry
.release(&claim.retainer, at(5000))
.expect("release should succeed for the current holder");
assert!(
registry
.claim(&[DOCKET], at(3000))
.expect("claim should not error")
.is_none(),
"a released pact must not be claimable before its reclaimable instant"
);
}
fn released_pact_reclaimable_at_its_instant<R, F>(make: &F)
where
R: Registry,
R::Error: Debug,
F: Fn(Vec<Pact>, u64) -> R,
{
let registry = make(vec![a_pact()], LEASE_MILLIS);
let first = registry
.claim(&[DOCKET], at(0))
.expect("claim should not error")
.expect("a pact should be claimable");
registry
.release(&first.retainer, at(5000))
.expect("release should succeed");
let second = registry
.claim(&[DOCKET], at(5000))
.expect("claim should not error")
.expect("a released pact must be claimable at its reclaimable instant");
assert_ne!(
first.retainer.id(),
second.retainer.id(),
"reclaiming a released pact must rotate the retainer"
);
}
fn immediate_reclaim_reclaims_like_lapse<R, F>(make: &F)
where
R: Registry,
R::Error: Debug,
F: Fn(Vec<Pact>, u64) -> R,
{
let registry = make(vec![a_pact()], LEASE_MILLIS);
let claim = registry
.claim(&[DOCKET], at(0))
.expect("claim should not error")
.expect("a pact should be claimable");
registry
.release(&claim.retainer, at(0))
.expect("release with an immediate reclaim should succeed");
assert!(
registry
.claim(&[DOCKET], at(0))
.expect("claim should not error")
.is_some(),
"an immediate reclaim must make the pact claimable at once, as a voluntary lapse"
);
}
fn release_rotates_authority_from_prior_holder<R, F>(make: &F)
where
R: Registry,
R::Error: Debug,
F: Fn(Vec<Pact>, u64) -> R,
{
let registry = make(vec![a_pact()], LEASE_MILLIS);
let claim = registry
.claim(&[DOCKET], at(0))
.expect("claim should not error")
.expect("a pact should be claimable");
registry
.release(&claim.retainer, at(0))
.expect("release should succeed");
assert!(
registry.fulfill(&claim.retainer).is_err(),
"the prior holder must not settle after releasing (release rotates authority)"
);
}
fn heartbeat_extends_lease_preventing_lapse<R, F>(make: &F)
where
R: Registry,
R::Error: Debug,
F: Fn(Vec<Pact>, u64) -> R,
{
let registry = make(vec![a_pact()], LEASE_MILLIS);
let claim = registry
.claim(&[DOCKET], at(0))
.expect("claim should not error")
.expect("a pact should be claimable");
registry
.heartbeat(&claim.retainer, at(800))
.expect("an in-window heartbeat should extend the lease");
assert!(
registry
.claim(&[DOCKET], at(1500))
.expect("claim should not error")
.is_none(),
"a heartbeat within the window must prevent a lapse"
);
}
fn heartbeat_on_lapsed_lease_rejected<R, F>(make: &F)
where
R: Registry,
R::Error: Debug,
F: Fn(Vec<Pact>, u64) -> R,
{
let registry = make(vec![a_pact()], LEASE_MILLIS);
let claim = registry
.claim(&[DOCKET], at(0))
.expect("claim should not error")
.expect("a pact should be claimable");
assert!(
registry.heartbeat(&claim.retainer, at(1200)).is_err(),
"a heartbeat after the lease expired must be rejected, forcing a re-claim"
);
}
fn heartbeat_unknown_retainer_rejected<R, F>(make: &F)
where
R: Registry,
R::Error: Debug,
F: Fn(Vec<Pact>, u64) -> R,
{
let registry = make(vec![a_pact()], LEASE_MILLIS);
let _claim = registry
.claim(&[DOCKET], at(0))
.expect("claim should not error")
.expect("a pact should be claimable");
let unknown = Retainer::new(Uuid::new_v4());
assert!(
registry.heartbeat(&unknown, at(100)).is_err(),
"a heartbeat with an unissued retainer must be rejected"
);
}