use std::sync::Arc;
use ruststream::testing::TestApp;
use ruststream_fred::prelude::*;
use serde::{Deserialize, Serialize};
use tokio::sync::Mutex;
#[derive(Debug, Deserialize, Outgoing, Serialize, Clone, PartialEq)]
struct Payment {
id: u64,
amount: u64,
}
#[derive(Debug, Deserialize, Outgoing, Serialize, PartialEq)]
#[outgoing(name = "receipts")]
struct Receipt {
id: u64,
}
#[derive(Clone, Default)]
struct PaymentRepository {
payments: Arc<Mutex<Vec<Payment>>>,
}
impl PaymentRepository {
async fn save(&self, payment: Payment) {
self.payments.lock().await.push(payment);
}
async fn ids(&self) -> Vec<u64> {
self.payments.lock().await.iter().map(|p| p.id).collect()
}
}
#[subscriber(RedisStream::new("payments").group("workers"))]
async fn process_payment(
payment: &Payment,
ctx: &mut Context<'_, (), PaymentRepository>,
) -> HandlerOutcome {
if payment.amount == 0 {
return HandlerOutcome::drop();
}
ctx.state().save(payment.clone()).await;
HandlerOutcome::ack()
}
#[subscriber(RedisList::new("settlements").reliable(), reply)]
async fn settle_payment(payment: &Payment) -> Receipt {
Receipt { id: payment.id }
}
#[subscriber(RedisPubSub::new("notifications"))]
async fn notify(payment: &Payment) -> HandlerOutcome {
println!("notified of payment {}", payment.id);
HandlerOutcome::ack()
}
fn app(repository: PaymentRepository) -> impl App {
RustStream::new(AppInfo::new("payments", "0.1.0"))
.on_startup(move |()| async move { Ok::<_, std::convert::Infallible>(repository) })
.with_broker(RedisBroker::standalone("redis://localhost:6379"), |b| {
b.include(process_payment);
b.include(settle_payment).out_reply(stream::Publish);
b.include(notify);
})
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let repository = PaymentRepository::default();
let tb = TestApp::start(app(repository.clone())).await?;
tb.broker::<RedisBroker>()
.message(&Payment { id: 1, amount: 100 })
.to("payments")
.publish()
.await?;
tb.broker::<RedisBroker>()
.message(&Payment { id: 2, amount: 0 })
.to("payments")
.publish()
.await?;
tb.broker::<RedisBroker>()
.subscriber("payments")
.assert_called(2)
.settled(HandlerOutcome::drop());
assert_eq!(repository.ids().await, [1]);
tb.broker::<RedisBroker>()
.message(&Payment { id: 3, amount: 50 })
.to("settlements")
.publish()
.await?;
tb.broker::<RedisBroker>()
.published::<Receipt>("receipts")
.assert_called_once()
.with(&Receipt { id: 3 });
tb.broker::<RedisBroker>()
.message(&Payment { id: 4, amount: 10 })
.to("notifications")
.publish()
.await?;
tb.broker::<RedisBroker>()
.subscriber("notifications")
.assert_called_once();
tb.shutdown().await?;
Ok(())
}