1use std::fmt::Debug;
5use std::hash::Hash;
6
7use crate::event::store::{Appender, EventStoreExt};
8use crate::{command, event, message, version};
9
10pub struct Scenario;
13
14impl Scenario {
15 #[must_use]
18 pub fn given<Id, Evt>(self, events: Vec<event::Persisted<Id, Evt>>) -> ScenarioGiven<Id, Evt>
19 where
20 Evt: message::Message,
21 {
22 ScenarioGiven { given: events }
23 }
24
25 #[must_use]
33 pub fn when<Id, Evt, Cmd>(self, command: command::Envelope<Cmd>) -> ScenarioWhen<Id, Evt, Cmd>
34 where
35 Evt: message::Message,
36 Cmd: message::Message,
37 {
38 ScenarioWhen {
39 given: Vec::default(),
40 when: command,
41 }
42 }
43}
44
45#[doc(hidden)]
46pub struct ScenarioGiven<Id, Evt>
47where
48 Evt: message::Message,
49{
50 given: Vec<event::Persisted<Id, Evt>>,
51}
52
53impl<Id, Evt> ScenarioGiven<Id, Evt>
54where
55 Evt: message::Message,
56{
57 #[must_use]
59 pub fn when<Cmd>(self, command: command::Envelope<Cmd>) -> ScenarioWhen<Id, Evt, Cmd>
60 where
61 Cmd: message::Message,
62 {
63 ScenarioWhen {
64 given: self.given,
65 when: command,
66 }
67 }
68}
69
70#[doc(hidden)]
71pub struct ScenarioWhen<Id, Evt, Cmd>
72where
73 Evt: message::Message,
74 Cmd: message::Message,
75{
76 given: Vec<event::Persisted<Id, Evt>>,
77 when: command::Envelope<Cmd>,
78}
79
80impl<Id, Evt, Cmd> ScenarioWhen<Id, Evt, Cmd>
81where
82 Evt: message::Message,
83 Cmd: message::Message,
84{
85 #[must_use]
88 pub fn then(self, events: Vec<event::Persisted<Id, Evt>>) -> ScenarioThen<Id, Evt, Cmd> {
89 ScenarioThen {
90 given: self.given,
91 when: self.when,
92 case: ScenarioThenCase::Produces(events),
93 }
94 }
95
96 #[must_use]
98 pub fn then_fails(self) -> ScenarioThen<Id, Evt, Cmd> {
99 ScenarioThen {
100 given: self.given,
101 when: self.when,
102 case: ScenarioThenCase::Fails,
103 }
104 }
105}
106
107enum ScenarioThenCase<Id, Evt>
108where
109 Evt: message::Message,
110{
111 Produces(Vec<event::Persisted<Id, Evt>>),
112 Fails,
113}
114
115#[doc(hidden)]
116pub struct ScenarioThen<Id, Evt, Cmd>
117where
118 Evt: message::Message,
119 Cmd: message::Message,
120{
121 given: Vec<event::Persisted<Id, Evt>>,
122 when: command::Envelope<Cmd>,
123 case: ScenarioThenCase<Id, Evt>,
124}
125
126impl<Id, Evt, Cmd> ScenarioThen<Id, Evt, Cmd>
127where
128 Id: Clone + Eq + Hash + Send + Sync + Debug,
129 Evt: message::Message + Clone + PartialEq + Send + Sync + Debug,
130 Cmd: message::Message,
131{
132 pub async fn assert_on<F, H>(self, handler_factory: F)
139 where
140 F: Fn(event::store::Tracking<event::store::InMemory<Id, Evt>, Id, Evt>) -> H,
141 H: command::Handler<Cmd>,
142 {
143 let event_store = event::store::InMemory::<Id, Evt>::default();
144 let tracking_event_store = event_store.clone().with_recorded_events_tracking();
145
146 for event in self.given {
147 event_store
148 .append(
149 event.stream_id,
150 version::Check::MustBe(event.version - 1),
151 vec![event.event],
152 )
153 .await
154 .expect("domain event in 'given' should be inserted in the event store");
155 }
156
157 let handler = handler_factory(tracking_event_store.clone());
158 let result = handler.handle(self.when).await;
159
160 match self.case {
161 ScenarioThenCase::Produces(events) => {
162 let recorded_events = tracking_event_store.recorded_events();
163 assert_eq!(events, recorded_events);
164 },
165 ScenarioThenCase::Fails => assert!(result.is_err()),
166 }
167 }
168}