1use std::collections::HashMap;
4use std::time::Duration;
5
6use itertools::Itertools;
7use serde_json::{json, Map, Value};
8
9use pact_matching::Mismatch;
10
11#[derive(Clone, Debug)]
13pub struct VerificationInteractionResult {
14 pub interaction_id: Option<String>,
16 pub interaction_key: Option<String>,
18 pub description: String,
20 pub interaction_description: String,
22 pub result: Result<(), crate::MismatchResult>,
24 pub pending: bool,
26 pub duration: Duration
28}
29
30#[derive(Clone, Debug)]
32pub struct VerificationResult {
33 pub results: Vec<VerificationInteractionResult>,
35 pub output: Vec<String>
37}
38
39#[derive(Debug, Clone, Default)]
41pub struct VerificationExecutionResult {
42 pub result: bool,
44 pub notices: Vec<HashMap<String, String>>,
46 pub output: Vec<String>,
48 pub pending_errors: Vec<(String, VerificationMismatchResult)>,
50 pub errors: Vec<(String, VerificationMismatchResult)>,
52 pub interaction_results: Vec<VerificationInteractionResult>
54}
55
56impl VerificationExecutionResult {
57 pub fn new() -> Self {
59 VerificationExecutionResult {
60 result: true,
61 notices: vec![],
62 output: vec![],
63 pending_errors: vec![],
64 errors: vec![],
65 interaction_results: vec![],
66 }
67 }
68}
69
70impl Into<Value> for &VerificationExecutionResult {
71 fn into(self) -> Value {
72 json!({
73 "result": self.result,
74 "notices": self.notices.iter().map(|m| Value::Object(
75 m.iter().map(|(k, v)| (k.clone(), Value::String(v.clone()))).collect()
76 )).collect_vec(),
77 "output": self.output,
78 "pendingErrors": self.pending_errors.iter().map(|(e, r)| {
79 let err: Value = r.into();
80 json!({
81 "interaction": e,
82 "mismatch": err
83 })
84 }).collect_vec(),
85 "errors": self.errors.iter().map(|(e, r)| {
86 let err: Value = r.into();
87 json!({
88 "interaction": e,
89 "mismatch": err
90 })
91 }).collect_vec(),
92 "interactionResults": self.interaction_results.iter().map(|r| {
93 let mut attributes = Map::new();
94 if let Some(interaction_id) = &r.interaction_id {
95 attributes.insert("interactionId".to_string(), Value::String(interaction_id.clone()));
96 }
97 if let Some(interaction_key) = &r.interaction_key {
98 attributes.insert("interactionKey".to_string(), Value::String(interaction_key.clone()));
99 }
100 attributes.insert("description".to_string(), Value::String(r.interaction_description.clone()));
101 match r.result {
102 Ok(_) => attributes.insert("result".to_string(), Value::String("OK".to_string())),
103 Err(_) => attributes.insert("result".to_string(), Value::String("Error".to_string()))
104 };
105 attributes.insert("duration".to_string(), Value::String(format!("{:?}", r.duration)));
106 Value::Object(attributes)
107 }).collect_vec()
108 })
109 }
110}
111
112impl Into<Value> for VerificationExecutionResult {
113 fn into(self) -> Value {
114 (&self).into()
115 }
116}
117
118#[derive(Debug, Clone, PartialEq, PartialOrd)]
121pub enum VerificationMismatchResult {
122 Mismatches {
124 mismatches: Vec<Mismatch>,
126 interaction_id: Option<String>
128 },
129 Error {
131 error: String,
133 interaction_id: Option<String>
135 }
136}
137
138impl From<&crate::MismatchResult> for VerificationMismatchResult {
139 fn from(result: &crate::MismatchResult) -> Self {
140 match result {
141 crate::MismatchResult::Mismatches { mismatches, interaction_id, .. } => {
142 VerificationMismatchResult::Mismatches {
143 mismatches: mismatches.clone(),
144 interaction_id: interaction_id.clone()
145 }
146 }
147 crate::MismatchResult::Error(error, interaction_id) => {
148 VerificationMismatchResult::Error {
149 error: error.clone(),
150 interaction_id: interaction_id.clone()
151 }
152 }
153 }
154 }
155}
156
157impl Into<Value> for &VerificationMismatchResult {
158 fn into(self) -> Value {
159 match self {
160 VerificationMismatchResult::Mismatches { mismatches, interaction_id } => {
161 json!({
162 "type": "mismatches",
163 "mismatches": mismatches.iter().map(|i| i.to_json()).collect_vec(),
164 "interactionId": interaction_id.clone().unwrap_or_default()
165 })
166 }
167 VerificationMismatchResult::Error { error, interaction_id } => {
168 json!({
169 "type": "error",
170 "message": error,
171 "interactionId": interaction_id.clone().unwrap_or_default()
172 })
173 }
174 }
175 }
176}
177
178impl Into<Value> for VerificationMismatchResult {
179 fn into(self) -> Value {
180 (&self).into()
181 }
182}
183
184#[cfg(test)]
185mod tests {
186 use expectest::prelude::*;
187 use maplit::hashmap;
188 use pretty_assertions::assert_eq;
189 use serde_json::{json, Value};
190
191 use pact_matching::Mismatch;
192
193 use crate::{MismatchResult, VerificationExecutionResult};
194 use crate::verification_result::{VerificationInteractionResult, VerificationMismatchResult};
195
196 #[test]
197 fn match_result_to_json() {
198 let mismatch = VerificationMismatchResult::Mismatches {
199 mismatches: vec![
200 Mismatch::BodyMismatch {
201 path: "1.2.3.4".to_string(),
202 expected: Some("100".into()),
203 actual: Some("200".into()),
204 mismatch: "Expected 100 but got 200".to_string()
205 }
206 ],
207 interaction_id: None
208 };
209 let json: Value = mismatch.into();
210 expect!(json).to(be_equal_to(json!({
211 "interactionId": "",
212 "mismatches": [
213 {
214 "actual": "200",
215 "expected": "100",
216 "mismatch": "Expected 100 but got 200",
217 "path": "1.2.3.4",
218 "type": "BodyMismatch"
219 }
220 ],
221 "type": "mismatches"
222 })));
223
224 let error = VerificationMismatchResult::Error {
225 error: "It went bang, Mate!".to_string(),
226 interaction_id: Some("1234".to_string())
227 };
228 let json: Value = error.into();
229 expect!(json).to(be_equal_to(json!({
230 "interactionId": "1234",
231 "message": "It went bang, Mate!",
232 "type": "error"
233 })));
234 }
235
236 #[test]
237 fn verification_execution_result_to_json() {
238 let result = VerificationExecutionResult {
239 result: false,
240 notices: vec![
241 hashmap!{
242 "comment".to_string() => "This is a comment".to_string()
243 }
244 ],
245 output: vec![
246 "line 1".to_string(),
247 "line 2".to_string(),
248 "line 3".to_string(),
249 "line 4".to_string()
250 ],
251 pending_errors: vec![
252 (
253 "interaction 1".to_string(),
254 VerificationMismatchResult::Error {
255 error: "Boom!".to_string(),
256 interaction_id: None
257 }
258 )
259 ],
260 errors: vec![
261 (
262 "interaction 2".to_string(),
263 VerificationMismatchResult::Error {
264 error: "Boom!".to_string(),
265 interaction_id: None
266 }
267 )
268 ],
269 interaction_results: vec![],
270 };
271
272 let json: Value = result.into();
273
274 assert_eq!(json, json!({
275 "errors": [
276 {
277 "interaction": "interaction 2".to_string(),
278 "mismatch": {
279 "interactionId": "".to_string(),
280 "message": "Boom!".to_string(),
281 "type": "error".to_string()
282 }
283 }
284 ],
285 "interactionResults": [],
286 "notices": [
287 {
288 "comment": "This is a comment".to_string()
289 }
290 ],
291 "output": [
292 "line 1".to_string(),
293 "line 2".to_string(),
294 "line 3".to_string(),
295 "line 4".to_string()
296 ],
297 "pendingErrors": [
298 {
299 "interaction": "interaction 1".to_string(),
300 "mismatch": {
301 "interactionId": "".to_string(),
302 "message": "Boom!".to_string(),
303 "type": "error".to_string()
304 }
305 }
306 ],
307 "result": false
308 }));
309 }
310
311 #[test]
312 fn verification_execution_result_to_json_includes_interaction_details() {
313 let result = VerificationExecutionResult {
314 interaction_results: vec![
315 VerificationInteractionResult {
316 interaction_id: None,
317 interaction_key: None,
318 description: "".to_string(),
319 interaction_description: "result-1".to_string(),
320 result: Ok(()),
321 pending: false,
322 duration: Default::default(),
323 },
324 VerificationInteractionResult {
325 interaction_id: None,
326 interaction_key: None,
327 description: "".to_string(),
328 interaction_description: "result-2".to_string(),
329 result: Err(MismatchResult::Error("test".to_string(), None)),
330 pending: false,
331 duration: Default::default(),
332 },
333 VerificationInteractionResult {
334 interaction_id: Some("test-id".to_string()),
335 interaction_key: None,
336 description: "".to_string(),
337 interaction_description: "result-3".to_string(),
338 result: Ok(()),
339 pending: false,
340 duration: Default::default(),
341 },
342 VerificationInteractionResult {
343 interaction_id: None,
344 interaction_key: Some("test-key".to_string()),
345 description: "".to_string(),
346 interaction_description: "result-4".to_string(),
347 result: Ok(()),
348 pending: false,
349 duration: Default::default(),
350 }
351 ],
352 .. VerificationExecutionResult::default()
353 };
354
355 let json: Value = result.into();
356
357 assert_eq!(json!({
358 "errors": [],
359 "interactionResults": [
360 {
361 "description": "result-1",
362 "duration": "0ns",
363 "result": "OK",
364 },
365 {
366 "description": "result-2",
367 "duration": "0ns",
368 "result": "Error",
369 },
370 {
371 "description": "result-3",
372 "duration": "0ns",
373 "interactionId": "test-id",
374 "result": "OK",
375 },
376 {
377 "description": "result-4",
378 "duration": "0ns",
379 "interactionKey": "test-key",
380 "result": "OK",
381 }
382 ],
383 "notices": [],
384 "output": [],
385 "pendingErrors": [],
386 "result": false
387 }), json);
388 }
389}