#![cfg_attr(not(test), allow(dead_code, unused_imports))]
use ruststream::memory::prelude::*;
use serde::{Deserialize, Serialize};
#[derive(Debug, PartialEq, Deserialize, Serialize, Outgoing)]
struct Order {
id: u64,
}
#[derive(Outgoing, Serialized)]
struct Wire(Vec<u8>);
mod assert_called_once {
use std::error::Error;
use ruststream::memory::prelude::*;
use ruststream::testing::TestApp;
use super::Order;
#[subscriber("orders")]
async fn accept(order: &Order) -> HandlerOutcome {
tracing::info!(order.id, "accepted");
HandlerOutcome::ack()
}
fn app() -> impl App {
RustStream::new(AppInfo::new("orders", "0.1.0")).with_broker(MemoryBroker::new(), |b| {
b.include(accept);
})
}
#[tokio::test]
async fn the_handler_ran_once() -> Result<(), Box<dyn Error>> {
let tb = TestApp::start(app()).await?;
tb.broker::<MemoryBroker>()
.message(&Order { id: 7 })
.to("orders")
.publish()
.await?;
tb.broker::<MemoryBroker>()
.subscriber("orders")
.assert_called_once();
Ok(())
}
}
mod assert_called {
use std::error::Error;
use ruststream::memory::prelude::*;
use ruststream::testing::TestApp;
use super::Order;
#[subscriber("orders")]
async fn accept(order: &Order) -> HandlerOutcome {
tracing::info!(order.id, "accepted");
HandlerOutcome::ack()
}
fn app() -> impl App {
RustStream::new(AppInfo::new("orders", "0.1.0")).with_broker(MemoryBroker::new(), |b| {
b.include(accept);
})
}
#[tokio::test]
async fn the_handler_ran_once_per_order() -> Result<(), Box<dyn Error>> {
let tb = TestApp::start(app()).await?;
let broker = tb.broker::<MemoryBroker>();
for id in 1..=3 {
broker.message(&Order { id }).to("orders").publish().await?;
}
broker.subscriber("orders").assert_called(3);
Ok(())
}
}
mod assert_not_called {
use std::error::Error;
use ruststream::memory::prelude::*;
use ruststream::testing::TestApp;
use super::Order;
#[derive(OutSlot)]
#[publishes(Order)]
struct Rejected;
#[subscriber("orders")]
async fn accept(order: &Order, Out(out): Out<impl Publisher, Rejected>) -> HandlerOutcome {
if order.id == 0 && out.message(order).to("rejected").publish().await.is_err() {
return HandlerOutcome::retry();
}
HandlerOutcome::ack()
}
#[subscriber("rejected")]
async fn review(order: &Order) -> HandlerOutcome {
tracing::info!(order.id, "sent to review");
HandlerOutcome::ack()
}
fn app() -> impl App {
RustStream::new(AppInfo::new("orders", "0.1.0")).with_broker(MemoryBroker::new(), |b| {
b.include(accept).out(Rejected, Publish).build();
b.include(review);
})
}
#[tokio::test]
async fn a_valid_order_is_not_reviewed() -> Result<(), Box<dyn Error>> {
let tb = TestApp::start(app()).await?;
let broker = tb.broker::<MemoryBroker>();
broker
.message(&Order { id: 7 })
.to("orders")
.publish()
.await?;
broker.subscriber("orders").assert_called_once();
broker.subscriber("rejected").assert_not_called();
Ok(())
}
}
mod settled {
use std::error::Error;
use ruststream::memory::prelude::*;
use ruststream::testing::TestApp;
use super::Order;
#[subscriber("orders")]
async fn accept(order: &Order) -> HandlerOutcome {
if order.id == 0 {
return HandlerOutcome::drop();
}
HandlerOutcome::ack()
}
fn app() -> impl App {
RustStream::new(AppInfo::new("orders", "0.1.0")).with_broker(MemoryBroker::new(), |b| {
b.include(accept);
})
}
#[tokio::test]
async fn an_order_without_an_id_is_dropped() -> Result<(), Box<dyn Error>> {
let tb = TestApp::start(app()).await?;
let broker = tb.broker::<MemoryBroker>();
broker
.message(&Order { id: 0 })
.to("orders")
.publish()
.await?;
broker
.subscriber("orders")
.assert_called_once()
.settled(HandlerOutcome::drop());
Ok(())
}
}
mod outcomes {
use std::error::Error;
use ruststream::memory::prelude::*;
use ruststream::testing::{Outcome, TestApp};
use super::Order;
#[subscriber("orders")]
async fn accept(order: &Order) -> HandlerOutcome {
if order.id == 0 {
return HandlerOutcome::drop();
}
HandlerOutcome::ack()
}
fn app() -> impl App {
RustStream::new(AppInfo::new("orders", "0.1.0")).with_broker(MemoryBroker::new(), |b| {
b.include(accept);
})
}
#[tokio::test]
async fn every_order_settled_in_turn() -> Result<(), Box<dyn Error>> {
let tb = TestApp::start(app()).await?;
let broker = tb.broker::<MemoryBroker>();
for id in [1, 0, 2] {
broker.message(&Order { id }).to("orders").publish().await?;
}
assert_eq!(
broker.subscriber("orders").outcomes(),
[Outcome::Ack, Outcome::Drop, Outcome::Ack],
);
Ok(())
}
}
mod assert_outcome {
use std::error::Error;
use std::time::Duration;
use ruststream::memory::prelude::*;
use ruststream::testing::{Outcome, TestApp};
use super::Order;
#[subscriber("orders")]
async fn reserve(order: &Order) -> HandlerOutcome {
tracing::info!(order.id, "stock not there yet");
HandlerOutcome::retry_after(Duration::from_secs(30))
}
fn app() -> impl App {
RustStream::new(AppInfo::new("orders", "0.1.0")).with_broker(MemoryBroker::new(), |b| {
b.include(reserve);
})
}
#[tokio::test(start_paused = true)]
async fn a_delayed_retry_reads_as_a_nack() -> Result<(), Box<dyn Error>> {
let tb = TestApp::start(app()).await?;
let broker = tb.broker::<MemoryBroker>();
broker
.message(&Order { id: 7 })
.to("orders")
.publish()
.await?;
broker
.subscriber("orders")
.assert_called_once()
.assert_outcome(Outcome::Nack);
Ok(())
}
}
mod panicked {
use std::error::Error;
use ruststream::memory::prelude::*;
use ruststream::testing::TestApp;
use super::Order;
#[subscriber("orders")]
async fn accept(order: &Order) -> HandlerOutcome {
assert_ne!(order.id, 0, "an order always has an id");
HandlerOutcome::ack()
}
fn app() -> impl App {
RustStream::new(AppInfo::new("orders", "0.1.0")).with_broker(MemoryBroker::new(), |b| {
b.include(accept);
})
}
#[tokio::test]
async fn a_panic_stops_the_service() -> Result<(), Box<dyn Error>> {
let tb = TestApp::start(app()).await?;
let broker = tb.broker::<MemoryBroker>();
broker
.message(&Order { id: 0 })
.to("orders")
.publish()
.await?;
broker.subscriber("orders").assert_called_once().panicked();
tb.assert_shut_down();
Ok(())
}
}
mod assert_last_failed_to_decode {
use std::error::Error;
use ruststream::memory::prelude::*;
use ruststream::testing::TestApp;
use super::{Order, Wire};
#[subscriber("orders")]
async fn accept(order: &Order) -> HandlerOutcome {
tracing::info!(order.id, "accepted");
HandlerOutcome::ack()
}
fn app() -> impl App {
RustStream::new(AppInfo::new("orders", "0.1.0")).with_broker(MemoryBroker::new(), |b| {
b.include(accept);
})
}
#[tokio::test]
async fn bytes_that_are_no_order_are_dropped() -> Result<(), Box<dyn Error>> {
let tb = TestApp::start(app()).await?;
let broker = tb.broker::<MemoryBroker>();
broker
.message(&Wire(b"not json".to_vec()))
.to("orders")
.publish()
.await?;
broker
.subscriber("orders")
.assert_called_once()
.settled(HandlerOutcome::drop())
.assert_last_failed_to_decode();
tb.assert_running();
Ok(())
}
}
fn main() {}