stubr/wiremock/
mock_set.rs1use crate::wiremock::{
2 mounted_mock::MountedMock,
3 verification::{VerificationOutcome, VerificationReport},
4};
5use crate::wiremock::{Mock, Request, ResponseTemplate};
6use futures_timer::Delay;
7use http_types::{Response, StatusCode};
8use log::debug;
9use std::ops::{Index, IndexMut};
10
11pub(crate) struct MountedMockSet {
18 pub(crate) mocks: Vec<(MountedMock, MountedMockState)>,
19 generation: u16,
25}
26
27#[derive(Copy, Clone)]
31pub(crate) struct MockId {
32 index: usize,
33 generation: u16,
37}
38
39impl MountedMockSet {
40 pub(crate) fn new() -> MountedMockSet {
42 MountedMockSet {
43 mocks: vec![],
44 generation: 0,
45 }
46 }
47
48 pub(crate) async fn handle_request(&mut self, request: Request) -> (Response, Option<Delay>) {
49 debug!("Handling request.");
50 let mut response_template: Option<ResponseTemplate> = None;
51 self.mocks.sort_by_key(|(m, _)| m.specification.priority);
52 for (mock, mock_state) in &mut self.mocks {
53 if *mock_state == MountedMockState::OutOfScope {
54 continue;
55 }
56 if mock.matches(&request) {
57 response_template = mock.response_template(&request).ok();
58 break;
59 }
60 }
61 if let Some(response_template) = response_template {
62 let delay = response_template.delay().map(|d| Delay::new(d.into_owned()));
63 (response_template.generate_response(), delay)
64 } else {
65 debug!("Got unexpected request:\n{}", request);
66 (Response::new(StatusCode::NotFound), None)
67 }
68 }
69
70 pub(crate) fn register(&mut self, mock: Mock) -> MockId {
71 let n_registered_mocks = self.mocks.len();
72 let active_mock = MountedMock::new(mock, n_registered_mocks);
73 self.mocks.push((active_mock, MountedMockState::InScope));
74
75 MockId {
76 index: self.mocks.len() - 1,
77 generation: self.generation,
78 }
79 }
80
81 pub(crate) fn reset(&mut self) {
82 self.mocks = vec![];
83 self.generation += 1;
84 }
85
86 pub(crate) fn deactivate(&mut self, mock_id: MockId) {
90 let mut mock = &mut self[mock_id];
91 mock.1 = MountedMockState::OutOfScope;
92 }
93
94 pub(crate) fn verify_all(&self) -> VerificationOutcome {
96 let failed_verifications: Vec<VerificationReport> = self
97 .mocks
98 .iter()
99 .filter(|(_, state)| *state == MountedMockState::InScope)
100 .map(|(m, _)| m.verify())
101 .filter(|verification_report| !verification_report.is_satisfied())
102 .collect();
103 if failed_verifications.is_empty() {
104 VerificationOutcome::Success
105 } else {
106 VerificationOutcome::Failure(failed_verifications)
107 }
108 }
109
110 pub(crate) fn verify(&self, mock_id: MockId) -> VerificationReport {
112 let (mock, _) = &self[mock_id];
113 mock.verify()
114 }
115}
116
117impl IndexMut<MockId> for MountedMockSet {
118 fn index_mut(&mut self, index: MockId) -> &mut Self::Output {
119 if index.generation != self.generation {
120 panic!("The mock you are trying to access is no longer active. It has been deleted from the active set via `reset` - you should not hold on to a `MockId` after you call `reset`!.")
121 }
122 &mut self.mocks[index.index]
123 }
124}
125
126impl Index<MockId> for MountedMockSet {
127 type Output = (MountedMock, MountedMockState);
128
129 fn index(&self, index: MockId) -> &Self::Output {
130 if index.generation != self.generation {
131 panic!("The mock you are trying to access is no longer active. It has been deleted from the active set via `reset` - you should not hold on to a `MockId` after you call `reset`!.")
132 }
133 &self.mocks[index.index]
134 }
135}
136
137#[derive(Debug, PartialEq, Eq, Copy, Clone)]
154pub(crate) enum MountedMockState {
155 InScope,
156 OutOfScope,
157}
158
159#[cfg(test)]
160mod tests {
161 use crate::wiremock::matchers::path;
162 use crate::wiremock::mock_set::{MountedMockSet, MountedMockState};
163 use crate::wiremock::{Mock, ResponseTemplate};
164
165 #[test]
166 fn generation_is_incremented_for_every_reset() {
167 let mut set = MountedMockSet::new();
168 assert_eq!(set.generation, 0);
169
170 for i in 1..10 {
171 set.reset();
172 assert_eq!(set.generation, i);
173 }
174 }
175
176 #[test]
177 #[should_panic]
178 fn accessing_a_mock_id_after_a_reset_triggers_a_panic() {
179 let mut set = MountedMockSet::new();
181 let mock = Mock::given(path("/")).respond_with(ResponseTemplate::new(200));
182 let mock_id = set.register(mock);
183
184 set.reset();
186
187 let _ = &set[mock_id];
189 }
190
191 #[test]
192 fn deactivating_a_mock_does_not_invalidate_other_ids() {
193 let mut set = MountedMockSet::new();
195 let first_mock = Mock::given(path("/")).respond_with(ResponseTemplate::new(200));
196 let second_mock = Mock::given(path("/hello")).respond_with(ResponseTemplate::new(500));
197 let first_mock_id = set.register(first_mock);
198 let second_mock_id = set.register(second_mock);
199
200 set.deactivate(first_mock_id);
202
203 let first_mock = &set[first_mock_id];
205 assert_eq!(first_mock.1, MountedMockState::OutOfScope);
206 let second_mock = &set[second_mock_id];
207 assert_eq!(second_mock.1, MountedMockState::InScope);
208 }
209}