use futures_util::StreamExt as _;
use zenkey_fleet::{SeedItem, SeedPolicy, StreamItem, seed_subscribe};
mod util;
use util::{peer_pair, timestamping_pair};
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn a_responders_queries_stream_and_stay_answerable() {
let (a, b) = peer_pair().await;
let key = "v1/h-cccccccccccc/@rpc/demo/ping";
let responder = zenkey_fleet::declare_responder(&a, key, b"pong".to_vec(), None, false)
.await
.expect("declare");
let served = tokio::spawn(async move {
let query = {
let mut queries = std::pin::pin!(responder.stream());
queries.next().await.expect("one ask")
};
let view = responder.answer(query).await;
responder.undeclare().await.expect("undeclare");
view
});
let answers = loop {
let answers = zenkey_fleet::fleet_get(
&zenkey_fleet::Fleet::new(&b, ""),
key,
&zenkey_fleet::GetOpts::new(std::time::Duration::from_millis(500)),
)
.await
.expect("get");
if !answers.is_empty() {
break answers;
}
};
match &answers[0].answer {
zenkey_fleet::Answer::Value(bytes) => assert_eq!(bytes.to_bytes().as_ref(), b"pong"),
other => panic!("expected a value, got {other:?}"),
}
let view = served.await.expect("join");
assert_eq!(
view.reply_error, None,
"the query taken from the stream was answered, not dropped"
);
}
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn matching_changes_stream_as_bools() {
let (a, b) = peer_pair().await;
let key = "v1/h-cccccccccccc/state/demo/health";
let publication =
zenkey_fleet::declare_publication(&a, key, zenkey::qos::QosProfile::Transition, None)
.await
.expect("declare");
let events = publication.matching_events().await.expect("events");
let _sub = b.declare_subscriber(key).await.expect("subscribe");
let appeared = tokio::time::timeout(util::SETTLE, async {
let mut changes = std::pin::pin!(events.stream());
changes.next().await
})
.await
.expect("a matching change within 5s")
.expect("the listener is alive");
assert!(appeared, "a subscriber appeared");
}
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn a_seeded_subscription_streams_seed_then_boundary_then_live() {
use zenoh_ext::AdvancedPublisherBuilderExt;
let (a, b) = timestamping_pair().await;
let key = "v1/h-cccccccccccc/state/demo/health";
let publisher = a
.declare_publisher(key)
.cache(zenoh_ext::CacheConfig::default().max_samples(4))
.await
.expect("cached publisher");
publisher.put("seeded").await.expect("put");
tokio::time::sleep(util::SETTLE / 10).await;
let sub = seed_subscribe(&b, key, SeedPolicy::default())
.await
.expect("seed subscribe");
let mut items = std::pin::pin!(sub);
let mut seeded = Vec::new();
let coverage = tokio::time::timeout(util::SETTLE, async {
loop {
match items.next().await.expect("stream alive") {
SeedItem::Sample(v) => {
seeded.push(String::from_utf8_lossy(&v.payload.to_bytes()).to_string());
}
SeedItem::Dropped(n) => panic!("this fixture never outruns the channel ({n})"),
SeedItem::SeedComplete(c) => break c,
}
}
})
.await
.expect("the boundary within 5s");
assert_eq!(
seeded,
vec!["seeded"],
"the seed arrived before the boundary"
);
assert!(
coverage.history_replies.is_some() || coverage.storage_replies.is_some(),
"the seed was asked for: {coverage:?}"
);
publisher.put("live").await.expect("put");
let live = tokio::time::timeout(util::SETTLE, async {
loop {
if let SeedItem::Sample(v) = items.next().await.expect("stream alive") {
return String::from_utf8_lossy(&v.payload.to_bytes()).to_string();
}
}
})
.await
.expect("a live sample within 5s");
assert_eq!(live, "live");
}
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn the_monitors_stream_reports_lag_as_data_and_counts_it() {
let (a, b) = peer_pair().await;
let key = "v1/h-cccccccccccc/state/demo/health";
let monitor = zenkey_fleet::Monitor::start(&b, zenkey_fleet::MonitorSpec::default())
.await
.expect("monitor");
let slow = monitor.events();
let monitor = monitor.watching([key]).await.expect("watch");
let publication =
zenkey_fleet::declare_publication(&a, key, zenkey::qos::QosProfile::Transition, None)
.await
.expect("declare");
let matching = publication.matching_events().await.expect("events");
assert!(
tokio::time::timeout(util::SETTLE, matching.recv())
.await
.expect("matching within 5s")
.expect("listener alive")
);
for i in 0..4096u32 {
publication
.send(i.to_string().into_bytes(), None)
.await
.expect("send");
}
tokio::time::sleep(util::SETTLE / 10).await;
let mut items = std::pin::pin!(slow.into_stream());
let dropped = tokio::time::timeout(util::SETTLE, async {
loop {
match items.next().await.expect("stream alive") {
StreamItem::Dropped(n) => return n,
StreamItem::Event(_) => continue,
}
}
})
.await
.expect("a drop report within 5s");
assert!(dropped > 0, "the flood outran the ring");
assert!(
monitor.core().dropped() >= dropped,
"the stream folded its lag into the monitor's counter: \
monitor says {}, stream reported {dropped}",
monitor.core().dropped()
);
monitor.shutdown().await.expect("shutdown");
}