use serde::{Deserialize, Serialize};
use ruststream_lapin::prelude::*;
#[derive(Debug, Serialize, Deserialize, PartialEq, Outgoing)]
#[outgoing(name = "payments")]
struct Payment {
amount: u64,
}
#[derive(Debug, Serialize, Deserialize, PartialEq, Outgoing)]
#[outgoing(name = "receipt.issued")]
struct Receipt {
amount: u64,
}
#[derive(OutSlot)]
#[publishes(Receipt)]
struct Receipts;
#[subscriber(RabbitQueue::new("payments"))]
async fn accept(
payment: &Payment,
Out(receipts): Out<impl Publisher<Options = LapinPublishOptions>, Receipts>,
) -> HandlerOutcome {
if payment.amount == 0 {
return HandlerOutcome::drop();
}
let receipt = Receipt {
amount: payment.amount,
};
let sent = if payment.amount > 1_000 {
receipts.message(&receipt).priority(9).publish().await
} else {
receipts.message(&receipt).publish().await
};
if sent.is_err() {
return HandlerOutcome::retry();
}
HandlerOutcome::ack()
}
#[subscriber(
RabbitQueue::new("receipts.audit").bind(RabbitExchange::topic("receipts"), "receipt.*")
)]
async fn audit(receipt: &Receipt) -> HandlerOutcome {
tracing::info!(amount = receipt.amount, "receipt audited");
HandlerOutcome::ack()
}
#[ruststream::app]
fn app() -> RustStream {
RustStream::new(AppInfo::new("payments", "0.1.0")).with_broker(
LapinBroker::new("amqp://localhost:5672"),
|b| {
b.include(accept)
.out(
Receipts,
Publish::default().exchange("receipts").priority(3),
)
.build();
b.include(audit);
},
)
}
#[cfg(test)]
mod tests {
use std::error::Error;
use ruststream::testing::TestApp;
use ruststream_lapin::prelude::*;
use super::{Payment, Receipt, Receipts, app};
#[tokio::test]
async fn a_payment_is_accepted() -> Result<(), Box<dyn Error>> {
let tb = TestApp::start(app()).await?;
tb.broker::<LapinBroker>()
.message(&Payment { amount: 100 })
.publish()
.await?;
tb.broker::<LapinBroker>()
.subscriber("payments")
.assert_called_once()
.with(&Payment { amount: 100 })
.settled(HandlerOutcome::ack());
Ok(())
}
#[tokio::test]
async fn a_receipt_reaches_the_queue_bound_to_its_exchange() -> Result<(), Box<dyn Error>> {
let tb = TestApp::start(app()).await?;
tb.broker::<LapinBroker>()
.message(&Payment { amount: 100 })
.publish()
.await?;
tb.broker::<LapinBroker>()
.subscriber("receipts.audit")
.assert_called_once()
.with(&Receipt { amount: 100 })
.settled(HandlerOutcome::ack());
Ok(())
}
#[tokio::test]
async fn a_large_payment_is_receipted_first() -> Result<(), Box<dyn Error>> {
let tb = TestApp::start(app()).await?;
tb.broker::<LapinBroker>()
.message(&Payment { amount: 5_000 })
.publish()
.await?;
tb.out::<Receipts>()
.assert_called_once()
.with_options(&LapinPublishOptions {
priority: Some(9),
..LapinPublishOptions::default()
});
Ok(())
}
}