use spate_core::feed::memory::MemoryFeed;
use spate_core::feed::{Feed, FeedError, FeedErrorKind, Receipt, Unit, wake_channel};
use spate_test::scripted_feed;
use std::collections::BTreeMap;
use std::sync::Arc;
use std::time::Duration;
#[test]
fn a_reader_holds_a_redelivered_unit_by_its_new_receipt() -> Result<(), FeedError> {
let (feed, script) = scripted_feed();
let feed: Arc<dyn Feed> = Arc::new(feed);
let (waker, park) = wake_channel();
feed.set_waker(waker);
let mut held: BTreeMap<Receipt, Unit> = BTreeMap::new();
let first = script.deliver(Unit::new("day-1", b"2026-10-01".to_vec()));
assert!(park.park(Duration::from_secs(5)));
for delivery in feed.receive(8)? {
held.insert(delivery.receipt, delivery.unit);
}
let second = script.redeliver(first);
assert!(park.park(Duration::from_secs(5)));
for delivery in feed.receive(8)? {
assert_eq!(delivery.deliveries, 2);
held.insert(delivery.receipt, delivery.unit);
}
held.retain(|receipt, _| match feed.renew(*receipt) {
Ok(()) => true,
Err(e) => e.kind != FeedErrorKind::Gone,
});
assert_eq!(held.keys().copied().collect::<Vec<_>>(), vec![second]);
feed.acknowledge(second)?;
assert_eq!(script.acknowledged(), vec![second]);
assert_eq!(script.gone(), vec![first]);
Ok(())
}
#[test]
fn a_refused_publish_is_reported_and_never_delivered() {
let (feed, script) = scripted_feed();
let feed: Arc<dyn Feed> = Arc::new(feed);
script.refuse_next_publish(FeedErrorKind::Refused);
let mut confirmation = feed.publish(Unit::new("too-big", vec![0; 16]));
let refused = confirmation.try_outcome().unwrap().unwrap_err();
assert_eq!(refused.kind, FeedErrorKind::Refused);
assert!(feed.receive(8).unwrap().is_empty());
assert_eq!(script.published().len(), 1);
}
fn counts_for_a_unit_published_twice(feed: Arc<dyn Feed>) -> Vec<u32> {
drop(feed.publish(Unit::new("twice", b"x".to_vec())));
drop(feed.publish(Unit::new("twice", b"x".to_vec())));
feed.receive(8)
.unwrap()
.into_iter()
.map(|d| d.deliveries)
.collect()
}
#[test]
fn both_feeds_count_a_unit_published_twice_alike() {
let memory = counts_for_a_unit_published_twice(Arc::new(MemoryFeed::new()));
let (scripted, _script) = scripted_feed();
let scripted = counts_for_a_unit_published_twice(Arc::new(scripted));
assert_eq!(memory, vec![1, 1]);
assert_eq!(scripted, memory, "scripted feed counts by unit id");
}
#[test]
fn a_unit_published_again_after_its_ack_is_a_first_delivery() {
let (feed, _script) = scripted_feed();
drop(feed.publish(Unit::new("k", b"x".to_vec())));
let first = feed.receive(8).unwrap().remove(0);
feed.acknowledge(first.receipt).unwrap();
drop(feed.publish(Unit::new("k", b"x".to_vec())));
assert_eq!(feed.receive(8).unwrap().remove(0).deliveries, 1);
}
#[test]
fn a_duplicate_after_a_redelivery_counts_three() {
let (feed, script) = scripted_feed();
let first = script.deliver(Unit::new("a", b"x".to_vec()));
assert_eq!(feed.receive(8).unwrap()[0].deliveries, 1);
script.redeliver(first);
assert_eq!(feed.receive(8).unwrap()[0].deliveries, 2);
script.deliver(Unit::new("a", b"x".to_vec()));
assert_eq!(feed.receive(8).unwrap()[0].deliveries, 3);
}
#[test]
fn a_redelivery_after_a_duplicate_counts_three() {
let (feed, script) = scripted_feed();
let first = script.deliver(Unit::new("a", b"x".to_vec()));
script.deliver(Unit::new("a", b"x".to_vec()));
let got: Vec<u32> = feed
.receive(8)
.unwrap()
.iter()
.map(|d| d.deliveries)
.collect();
assert_eq!(got, vec![1, 2]);
script.redeliver(first);
assert_eq!(feed.receive(8).unwrap()[0].deliveries, 3);
}
#[test]
fn a_second_redelivery_counts_three() {
let (feed, script) = scripted_feed();
let first = script.deliver(Unit::new("a", b"x".to_vec()));
let _ = feed.receive(8).unwrap();
let second = script.redeliver(first);
let _ = feed.receive(8).unwrap();
script.redeliver(second);
assert_eq!(feed.receive(8).unwrap()[0].deliveries, 3);
}
#[test]
fn a_published_unit_keeps_its_count_beside_a_scripted_id() {
let (feed, script) = scripted_feed();
let scripted = script.deliver(Unit::new("a", b"x".to_vec()));
drop(feed.publish(Unit::new("a", b"y".to_vec())));
let published = feed.receive(8).unwrap()[1].receipt;
script.redeliver(scripted);
assert_eq!(feed.receive(8).unwrap()[0].deliveries, 2);
script.redeliver(published);
assert_eq!(feed.receive(8).unwrap()[0].deliveries, 2);
}
#[test]
fn a_published_unit_redelivered_twice_counts_three() {
let (feed, script) = scripted_feed();
drop(feed.publish(Unit::new("a", b"x".to_vec())));
let first = feed.receive(8).unwrap()[0].receipt;
let second = script.redeliver(first);
assert_eq!(feed.receive(8).unwrap()[0].deliveries, 2);
script.redeliver(second);
assert_eq!(feed.receive(8).unwrap()[0].deliveries, 3);
}
#[test]
fn a_scripted_feed_records_hand_backs_and_closes() {
let (feed, script) = scripted_feed();
let feed: Arc<dyn Feed> = Arc::new(feed);
assert!(!feed.hands_back());
script.hand_back_on_stop();
assert!(feed.hands_back());
let first = script.deliver(Unit::new("a", b"a".to_vec()));
let second = script.deliver(Unit::new("b", b"b".to_vec()));
let kept = script.deliver(Unit::new("c", b"c".to_vec()));
assert_eq!(feed.receive(8).unwrap().len(), 3);
feed.stop_receiving();
feed.stop_receiving();
feed.hand_back(first).unwrap();
feed.hand_back(second).unwrap();
assert_eq!(feed.hand_back(first).unwrap_err().kind, FeedErrorKind::Gone);
let mut closed = feed.close(Duration::from_secs(2)).expect("the feed closes");
assert!(matches!(closed.try_outcome(), Some(Ok(()))));
assert_eq!(script.stopped_receiving(), 2);
assert_eq!(script.handed_back(), vec![first, second]);
assert_eq!(script.held(), vec![kept]);
assert_eq!(script.closes(), vec![Duration::from_secs(2)]);
assert_eq!(script.gone(), vec![first]);
}