#![cfg(feature = "sqlite")]
use serde_json::{json, Value};
use distributed::bus::{Bus, InMemoryBus, RunOptions};
use distributed::microsvc::{Context, HandlerError, HasOutboxStore, Routes, Service, Session};
use distributed::{
sourced, AggregateBuilder, AggregateRepository, Entity, OutboxMessage, OutboxMessageStatus,
OutboxStore, Queueable, QueuedRepository, SqliteRepository,
};
#[derive(Default)]
struct Counter {
entity: Entity,
value: i64,
}
#[sourced(entity, aggregate_type = "counter")]
impl Counter {
#[event("touched")]
fn touch(&mut self, id: String) {
self.entity.set_id(&id);
self.value += 1;
}
}
type Repo = AggregateRepository<QueuedRepository<SqliteRepository>, Counter>;
async fn handle_touch(ctx: &Context<'_, Repo>) -> Result<Value, HandlerError> {
let mut counter = Counter::default();
counter.touch("c1".to_string())?;
let message = OutboxMessage::create("evt-c1", "counter.touched", b"{}".to_vec())?;
ctx.repo().outbox(message).commit(&mut counter).await?;
Ok(json!({ "value": counter.value }))
}
async fn service() -> Repo {
SqliteRepository::connect_and_migrate("sqlite::memory:")
.await
.expect("sqlite repository should migrate")
.queued()
.aggregate::<Counter>()
}
#[tokio::test]
async fn commit_publishes_immediately_over_sqlite() {
let repo = service().await;
let store = repo.outbox_store();
let service = Service::new()
.routes(
Routes::new()
.with_repo(repo)
.command("counter.touch")
.handle(handle_touch),
)
.with_bus(InMemoryBus::new());
service
.dispatch("counter.touch", json!({}), Session::new())
.await
.unwrap();
let published = store
.messages_by_status(OutboxMessageStatus::Published)
.await
.unwrap();
assert_eq!(published.len(), 1, "row should be published immediately");
assert_eq!(published[0].id(), "evt-c1");
assert!(
store.pending().await.unwrap().is_empty(),
"nothing should be left for the poller"
);
}
#[tokio::test]
async fn run_consumes_command_and_publishes_over_sqlite() {
let bus = InMemoryBus::new();
let repo = service().await;
let store = repo.outbox_store();
let service = Service::new()
.routes(
Routes::new()
.with_repo(repo)
.command("counter.touch")
.handle(handle_touch),
)
.with_bus(bus.clone());
bus.send("counter.touch", b"{}".to_vec()).await.unwrap();
service.run(RunOptions::idempotent()).await.unwrap();
let published = store
.messages_by_status(OutboxMessageStatus::Published)
.await
.unwrap();
assert_eq!(published.len(), 1);
assert_eq!(published[0].id(), "evt-c1");
}