use std::sync::Arc;
use super::{ClaimEvidence, DedupCache};
use crate::durability::bridge::block_on;
use crate::durability::store::{EngineReadAccounting, EngineReadAccountingGuard};
use crate::durability::{DurableStore, ProcessingReceipt, open_ephemeral};
type TestResult = Result<(), Box<dyn std::error::Error>>;
const NAMESPACE: &str = "liminal:in-flight-test";
fn evidence() -> ClaimEvidence {
ClaimEvidence::new("orders", vec![0])
}
#[test]
fn a_store_without_an_index_is_walked_once_and_then_never_again() -> TestResult {
let store: Arc<dyn DurableStore> = Arc::new(open_ephemeral(1)?);
let legacy = DedupCache::new(Arc::clone(&store), NAMESPACE);
for index in 0..16 {
let key = format!("done-{index}");
block_on(legacy.claim_or_get(&key, 1))??;
block_on(legacy.complete_receipt(&key, ProcessingReceipt::new(Vec::new())))??;
}
block_on(legacy.claim_or_get("stranded", 1))??;
let first = DedupCache::new(Arc::clone(&store), NAMESPACE);
let accounting = EngineReadAccountingGuard::start();
let found = block_on(first.in_flight_claims())??;
let migration = accounting.snapshot();
drop(accounting);
assert_eq!(
found,
[super::InFlightClaim {
idempotency_key: "stranded".to_owned(),
claimed_at_millis: 1,
evidence: None,
}]
);
assert!(
migration.streams_enumerated > 0,
"the first start walks once"
);
let second = DedupCache::new(Arc::clone(&store), NAMESPACE);
let accounting = EngineReadAccountingGuard::start();
let found = block_on(second.in_flight_claims())??;
let restart = accounting.snapshot();
drop(accounting);
assert_eq!(
found,
[super::InFlightClaim {
idempotency_key: "stranded".to_owned(),
claimed_at_millis: 1,
evidence: None,
}]
);
assert_eq!(
restart.streams_enumerated, 0,
"the walk is recorded and not repeated"
);
Ok(())
}
#[test]
fn a_namespace_begun_empty_is_read_by_no_start_and_walked_by_none() -> TestResult {
let store: Arc<dyn DurableStore> = Arc::new(open_ephemeral(1)?);
let created = DedupCache::new_empty(Arc::clone(&store), NAMESPACE);
let accounting = EngineReadAccountingGuard::start();
let found = block_on(created.in_flight_claims())??;
let start = accounting.snapshot();
drop(accounting);
assert!(found.is_empty());
assert_eq!(
start,
EngineReadAccounting::default(),
"a namespace begun empty is not read at start"
);
assert_eq!(
block_on(store.read_value(&format!("{NAMESPACE}/in-flight-start")))??,
None,
"nothing is recorded until a claim needs the index"
);
block_on(created.claim_with_evidence("held", evidence(), 1))??;
let restarted = DedupCache::new(Arc::clone(&store), NAMESPACE);
let accounting = EngineReadAccountingGuard::start();
let found = block_on(restarted.in_flight_claims())??;
let restart = accounting.snapshot();
drop(accounting);
assert_eq!(by_key(&found), [("held".to_owned(), Some(evidence()))]);
assert_eq!(
restart.streams_enumerated, 0,
"a start after the first join reads the index, never the namespace"
);
Ok(())
}
fn by_key(claims: &[super::InFlightClaim]) -> Vec<(String, Option<super::ClaimEvidence>)> {
claims
.iter()
.map(|claim| (claim.idempotency_key.clone(), claim.evidence.clone()))
.collect()
}