eventcore_testing/
scenario.rs1use eventcore_memory::InMemoryEventStore;
4use eventcore_types::{CommandLogic, Event, EventStore, StreamId, StreamVersion, StreamWrites};
5
6pub struct TestScenario {
8 store: InMemoryEventStore,
9}
10
11impl Default for TestScenario {
12 fn default() -> Self {
13 Self::new()
14 }
15}
16
17impl TestScenario {
18 pub fn new() -> Self {
20 Self {
21 store: InMemoryEventStore::new(),
22 }
23 }
24
25 pub async fn given_events<E: Event>(self, stream_id: StreamId, events: Vec<E>) -> Self {
27 if events.is_empty() {
28 return self;
29 }
30
31 let reader = self
32 .store
33 .read_stream::<E>(stream_id.clone())
34 .await
35 .expect("reading stream for test setup should not fail");
36 let current_version = StreamVersion::new(reader.len());
37
38 let mut writes = StreamWrites::new()
39 .register_stream(stream_id, current_version)
40 .expect("registering stream for test setup should not fail");
41
42 for event in events {
43 writes = writes
44 .append(event)
45 .expect("appending event for test setup should not fail");
46 }
47
48 let _ = self
49 .store
50 .append_events(writes)
51 .await
52 .expect("appending events for test setup should not fail");
53
54 self
55 }
56
57 pub async fn when<C>(self, command: C) -> ScenarioResult<C::Event>
59 where
60 C: CommandLogic,
61 C::Event: Clone + PartialEq + std::fmt::Debug,
62 {
63 let result = eventcore::execute(&self.store, command, eventcore::RetryPolicy::new()).await;
64
65 let storage: std::sync::Arc<std::sync::Mutex<Vec<C::Event>>> =
66 std::sync::Arc::new(std::sync::Mutex::new(Vec::new()));
67 let collector = crate::EventCollector::new(storage.clone());
68 let _ = eventcore::run_projection(
69 collector,
70 &self.store,
71 eventcore::ProjectionConfig::default(),
72 )
73 .await;
74
75 let all_events = storage.lock().unwrap().clone();
76
77 ScenarioResult {
78 result: result.map(|_| ()),
79 all_events,
80 }
81 }
82}
83
84pub struct ScenarioResult<E> {
86 result: Result<(), eventcore_types::CommandError>,
87 all_events: Vec<E>,
88}
89
90impl<E: PartialEq + std::fmt::Debug> ScenarioResult<E> {
91 pub fn succeeded(&self) -> &Self {
93 assert!(
94 self.result.is_ok(),
95 "expected command to succeed, got: {:?}",
96 self.result.as_ref().err()
97 );
98 self
99 }
100
101 pub fn failed_with<Err: Into<eventcore_types::CommandError>>(&self, expected: Err) -> &Self {
107 let expected_error = expected.into();
108 match &self.result {
109 Err(actual) => {
110 assert_eq!(
111 actual.to_string(),
112 expected_error.to_string(),
113 "command error mismatch"
114 );
115 }
116 Ok(()) => panic!(
117 "expected command to fail with {}, but it succeeded",
118 expected_error
119 ),
120 }
121 self
122 }
123
124 pub fn then_events(&self, expected: Vec<E>) -> &Self {
126 assert_eq!(
127 self.all_events, expected,
128 "events in store should match expected"
129 );
130 self
131 }
132
133 pub fn then_event_count(&self, expected: usize) -> &Self {
135 assert_eq!(
136 self.all_events.len(),
137 expected,
138 "expected {} events, found {}",
139 expected,
140 self.all_events.len()
141 );
142 self
143 }
144}