use distributed::microsvc::{Routes, Service, Session};
use distributed::{AggregateBuilder, InMemoryRepository, OutboxStore, Queueable};
use serde_json::json;
use crate::handlers;
use crate::models::counter::Counter;
#[tokio::test]
async fn routes_macro_registers_handlers_and_dispatches() {
let store = InMemoryRepository::new();
let service = Service::new().routes(distributed::routes!(
Routes::new().with_repo(store.clone().queued().aggregate::<Counter>()),
command handlers::counter_create,
command handlers::counter_increment,
));
let mut cmds = service.command_names();
cmds.sort();
assert_eq!(cmds, vec!["counter.increment", "counter.initialize"]);
let result = service
.dispatch("counter.initialize", json!({ "id": "c1" }), Session::new())
.await
.unwrap();
assert_eq!(result, json!({ "id": "c1" }));
let result = service
.dispatch(
"counter.increment",
json!({ "id": "c1", "amount": 10 }),
Session::new(),
)
.await
.unwrap();
assert_eq!(result, json!({ "id": "c1", "value": 10 }));
let counter: Counter = store
.queued()
.aggregate::<Counter>()
.get("c1")
.await
.unwrap()
.unwrap();
assert_eq!(counter.value, 10);
}
#[tokio::test]
async fn guard_rejects_bad_input() {
let service = Service::new().routes(distributed::routes!(
Routes::new().with_repo(InMemoryRepository::new().queued().aggregate::<Counter>()),
command handlers::counter_create,
));
let result = service
.dispatch("counter.initialize", json!({ "wrong": 1 }), Session::new())
.await;
assert!(result.is_err());
}
#[tokio::test]
async fn handler_rejects_duplicate_create() {
let service = Service::new().routes(distributed::routes!(
Routes::new().with_repo(InMemoryRepository::new().queued().aggregate::<Counter>()),
command handlers::counter_create,
));
service
.dispatch("counter.initialize", json!({ "id": "c1" }), Session::new())
.await
.unwrap();
let result = service
.dispatch("counter.initialize", json!({ "id": "c1" }), Session::new())
.await;
assert!(result.is_err());
}
#[tokio::test]
async fn create_persists_outbox_message() {
let store = InMemoryRepository::new();
let service = Service::new().routes(distributed::routes!(
Routes::new().with_repo(store.clone().queued().aggregate::<Counter>()),
command handlers::counter_create,
));
let result = service
.dispatch("counter.initialize", json!({ "id": "c1" }), Session::new())
.await
.unwrap();
assert_eq!(result, json!({ "id": "c1" }));
let counter: Counter = store
.clone()
.queued()
.aggregate::<Counter>()
.get("c1")
.await
.unwrap()
.unwrap();
assert_eq!(counter.value, 0);
let pending = store.outbox_store().pending(usize::MAX).await.unwrap();
assert_eq!(pending.len(), 1);
assert_eq!(pending[0].event_type, "counter.initialized");
}
#[tokio::test]
async fn duplicate_create_leaves_single_outbox_message() {
let store = InMemoryRepository::new();
let service = Service::new().routes(distributed::routes!(
Routes::new().with_repo(store.clone().queued().aggregate::<Counter>()),
command handlers::counter_create,
));
service
.dispatch("counter.initialize", json!({ "id": "c1" }), Session::new())
.await
.unwrap();
let result = service
.dispatch("counter.initialize", json!({ "id": "c1" }), Session::new())
.await;
assert!(result.is_err());
let pending = store.outbox_store().pending(usize::MAX).await.unwrap();
assert_eq!(pending.len(), 1);
}
#[tokio::test]
async fn increment_persists_outbox_message() {
let store = InMemoryRepository::new();
let service = Service::new().routes(distributed::routes!(
Routes::new().with_repo(store.clone().queued().aggregate::<Counter>()),
command handlers::counter_create,
command handlers::counter_increment,
));
service
.dispatch("counter.initialize", json!({ "id": "c1" }), Session::new())
.await
.unwrap();
service
.dispatch(
"counter.increment",
json!({ "id": "c1", "amount": 7 }),
Session::new(),
)
.await
.unwrap();
let counter: Counter = store
.clone()
.queued()
.aggregate::<Counter>()
.get("c1")
.await
.unwrap()
.unwrap();
assert_eq!(counter.value, 7);
let pending = store.outbox_store().pending(usize::MAX).await.unwrap();
assert_eq!(pending.len(), 2);
let mut event_types: Vec<&str> = pending.iter().map(|m| m.event_type.as_str()).collect();
event_types.sort();
assert_eq!(
event_types,
vec!["counter.incremented", "counter.initialized"]
);
}