fmodel_rust/
specification.rs

1//! ## A test specification DSL for deciders and views that supports the given-when-then format.
2
3use crate::{
4    decider::{Decider, EventComputation, StateComputation},
5    view::{View, ViewStateComputation},
6};
7
8// ########################################################
9// ############# Decider Specification DSL ################
10// ########################################################
11
12/// A test specification DSL for deciders that supports the `given-when-then` format.
13/// The DSL is used to specify the events that have already occurred (GIVEN), the command that is being executed (WHEN), and the expected events (THEN) that should be generated.
14pub struct DeciderTestSpecification<'a, Command, State, Event, Error>
15where
16    Event: PartialEq + std::fmt::Debug,
17    Error: PartialEq + std::fmt::Debug,
18{
19    events: Vec<Event>,
20    state: Option<State>,
21    command: Option<Command>,
22    decider: Option<Decider<'a, Command, State, Event, Error>>,
23}
24
25impl<Command, State, Event, Error> Default
26    for DeciderTestSpecification<'_, Command, State, Event, Error>
27where
28    Event: PartialEq + std::fmt::Debug,
29    Error: PartialEq + std::fmt::Debug,
30{
31    fn default() -> Self {
32        Self {
33            events: Vec::new(),
34            state: None,
35            command: None,
36            decider: None,
37        }
38    }
39}
40
41impl<'a, Command, State, Event, Error> DeciderTestSpecification<'a, Command, State, Event, Error>
42where
43    Event: PartialEq + std::fmt::Debug,
44    State: PartialEq + std::fmt::Debug,
45    Error: PartialEq + std::fmt::Debug,
46{
47    #[allow(dead_code)]
48    /// Specify the decider you want to test
49    pub fn for_decider(mut self, decider: Decider<'a, Command, State, Event, Error>) -> Self {
50        self.decider = Some(decider);
51        self
52    }
53
54    #[allow(dead_code)]
55    /// Given preconditions / previous events
56    pub fn given(mut self, events: Vec<Event>) -> Self {
57        self.events = events;
58        self
59    }
60
61    #[allow(dead_code)]
62    /// Given preconditions / previous state
63    pub fn given_state(mut self, state: Option<State>) -> Self {
64        self.state = state;
65        self
66    }
67
68    #[allow(dead_code)]
69    /// When action/command
70    pub fn when(mut self, command: Command) -> Self {
71        self.command = Some(command);
72        self
73    }
74
75    #[allow(dead_code)]
76    /// Then expect result / new events
77    pub fn then(self, expected_events: Vec<Event>) {
78        let decider = self
79            .decider
80            .expect("Decider must be initialized. Did you forget to call `for_decider`?");
81        let command = self
82            .command
83            .expect("Command must be initialized. Did you forget to call `when`?");
84        let events = self.events;
85
86        let new_events_result = decider.compute_new_events(&events, &command);
87        let new_events = match new_events_result {
88            Ok(events) => events,
89            Err(error) => panic!(
90                "Events were expected but the decider returned an error instead: {:?}",
91                error
92            ),
93        };
94        assert_eq!(new_events, expected_events);
95    }
96
97    #[allow(dead_code)]
98    /// Then expect result / new events
99    pub fn then_state(self, expected_state: State) {
100        let decider = self
101            .decider
102            .expect("Decider must be initialized. Did you forget to call `for_decider`?");
103        let command = self
104            .command
105            .expect("Command must be initialized. Did you forget to call `when`?");
106        let state = self.state;
107
108        let new_state_result = decider.compute_new_state(state, &command);
109        let new_state = match new_state_result {
110            Ok(state) => state,
111            Err(error) => panic!(
112                "State was expected but the decider returned an error instead: {:?}",
113                error
114            ),
115        };
116        assert_eq!(new_state, expected_state);
117    }
118
119    #[allow(dead_code)]
120    /// Then expect error result / these are not events
121    pub fn then_error(self, expected_error: Error) {
122        let decider = self
123            .decider
124            .expect("Decider must be initialized. Did you forget to call `for_decider`?");
125        let command = self
126            .command
127            .expect("Command must be initialized. Did you forget to call `when`?");
128        let events = self.events;
129
130        let error_result = decider.compute_new_events(&events, &command);
131        let error = match error_result {
132            Ok(events) => panic!(
133                "An error was expected but the decider returned events instead: {:?}",
134                events
135            ),
136            Err(error) => error,
137        };
138        assert_eq!(error, expected_error);
139    }
140}
141
142// ########################################################
143// ############### View Specification DSL #################
144// ########################################################
145
146/// A test specification DSL for views that supports the `given-then`` format.
147/// The DSL is used to specify the events that have already occurred (GIVEN), and the expected view state (THEN) that should be generated based on these events.
148pub struct ViewTestSpecification<'a, State, Event>
149where
150    State: PartialEq + std::fmt::Debug,
151{
152    events: Vec<Event>,
153    view: Option<View<'a, State, Event>>,
154}
155
156impl<State, Event> Default for ViewTestSpecification<'_, State, Event>
157where
158    State: PartialEq + std::fmt::Debug,
159{
160    fn default() -> Self {
161        Self {
162            events: Vec::new(),
163            view: None,
164        }
165    }
166}
167
168impl<'a, State, Event> ViewTestSpecification<'a, State, Event>
169where
170    State: PartialEq + std::fmt::Debug,
171{
172    #[allow(dead_code)]
173    /// Specify the view you want to test
174    pub fn for_view(mut self, view: View<'a, State, Event>) -> Self {
175        self.view = Some(view);
176        self
177    }
178
179    #[allow(dead_code)]
180    /// Given preconditions / events
181    pub fn given(mut self, events: Vec<Event>) -> Self {
182        self.events = events;
183        self
184    }
185
186    #[allow(dead_code)]
187    /// Then expect evolving new state of the view
188    pub fn then(self, expected_state: State) {
189        let view = self
190            .view
191            .expect("View must be initialized. Did you forget to call `for_view`?");
192
193        let events = self.events;
194
195        let initial_state = (view.initial_state)();
196        let event_refs: Vec<&Event> = events.iter().collect();
197        let new_state_result = view.compute_new_state(Some(initial_state), &event_refs);
198
199        assert_eq!(new_state_result, expected_state);
200    }
201}