#![cfg(all(
feature = "memory",
feature = "macros",
feature = "json",
feature = "testing"
))]
use std::time::Duration;
use ruststream::memory::{MemoryBroker, MemoryPublish, MemoryPublisher, MemoryRequest};
use ruststream::runtime::{
AppInfo, Outgoing, PublishContext, PublishTransform, RustStream, TypedPublisher,
};
use ruststream::testing::expect_published;
use ruststream::{Broker, OutgoingMessage, PublishPolicy, Publisher, RequestReply, subscriber};
use serde::{Deserialize, Serialize};
#[derive(Debug, Serialize, Deserialize)]
struct Order {
id: u32,
}
#[derive(Debug, Serialize, Deserialize)]
struct Receipt {
id: u32,
}
struct Envelope;
impl<C> PublishTransform<C> for Envelope {
fn apply(&self, out: &mut Outgoing<'_>, _cx: &PublishContext<'_, C>) {
out.headers_mut().insert("x-envelope", b"1".to_vec());
}
}
#[tokio::test]
async fn a_bare_policy_pairs_into_a_live_publisher() {
let connected = MemoryBroker::new()
.connect()
.await
.expect("memory connect is infallible");
let publisher = MemoryPublish
.pair(&connected)
.await
.expect("memory pairing is infallible");
publisher
.publish(OutgoingMessage::new("policy.out", b"paired".as_slice()))
.await
.expect("publish through the paired publisher");
let seen = expect_published(&connected, "policy.out", 1, Duration::from_secs(2)).await;
assert_eq!(seen.len(), 1);
assert_eq!(seen[0].payload(), b"paired");
}
#[tokio::test]
async fn a_request_policy_pairs_into_a_requester() {
let connected = MemoryBroker::new()
.connect()
.await
.expect("memory connect is infallible");
let requester = MemoryRequest
.pair(&connected)
.await
.expect("memory pairing is infallible");
let unanswered = RequestReply::request(
&requester,
OutgoingMessage::new("policy.void", b"ping".as_slice()),
Duration::from_millis(50),
)
.await;
assert!(unanswered.is_err(), "{unanswered:?}");
}
#[subscriber("policy.requests", publish("policy.replies"))]
async fn respond(order: &Order) -> Receipt {
Receipt { id: order.id }
}
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn a_typed_policy_stack_pairs_functorially() {
let broker = MemoryBroker::new();
let publisher = broker.publisher();
let connected = Broker::connect(broker.clone())
.await
.expect("memory connect is infallible");
let paired = TypedPublisher::new(MemoryPublish)
.transform(Envelope)
.pair(&connected)
.await
.expect("memory pairing is infallible");
let _type_check: TypedPublisher<MemoryPublisher, _, _> = paired;
let replies = TypedPublisher::new(MemoryPublish).transform(Envelope);
let app = RustStream::new(AppInfo::new("policy", "0.1.0")).with_broker(broker, |b| {
b.include(respond).publisher(replies);
});
let running = app.start().await.expect("startup failed");
publisher
.publish(OutgoingMessage::new(
"policy.requests",
serde_json::to_vec(&Order { id: 7 }).unwrap().as_slice(),
))
.await
.expect("publish request");
let seen = expect_published(&connected, "policy.replies", 1, Duration::from_secs(2)).await;
assert_eq!(seen.len(), 1, "the reply must be published");
assert_eq!(
seen[0].headers().get("x-envelope"),
Some(b"1".as_slice()),
"the transform stack must survive pairing",
);
running.shutdown().await.expect("graceful shutdown failed");
}