1use std::sync::{
3 atomic::{AtomicUsize, Ordering},
4 Arc,
5};
6
7use uuid::Uuid;
8
9use crate::{
10 matchers::Matcher,
11 mock_builder::{Then, When},
12 request::Request,
13 response::Response,
14};
15
16const DEFAULT_PRIORITY: u8 = 5;
17
18#[derive(Debug)]
20pub struct Mock {
21 pub id: Uuid,
23 pub matchers: Vec<Arc<dyn Matcher>>,
25 pub response: Response,
27 pub priority: u8,
29 pub match_count: AtomicUsize,
31 pub limit: Option<usize>,
33}
34
35impl Mock {
36 pub fn new<F>(f: F) -> Self
38 where
39 F: FnOnce(When, Then),
40 {
41 let id = Uuid::now_v7();
42 let when = When::new();
43 let then = Then::new();
44 f(when.clone(), then.clone());
45 Self {
46 id,
47 matchers: when.into_inner(),
48 response: then.into_inner(),
49 priority: DEFAULT_PRIORITY,
50 match_count: AtomicUsize::new(0),
51 limit: None,
52 }
53 }
54
55 pub fn with_priority(mut self, priority: u8) -> Self {
57 self.priority = priority;
58 self
59 }
60
61 pub fn with_limit(mut self, limit: usize) -> Self {
63 self.limit = Some(limit);
64 self
65 }
66
67 pub fn id(&self) -> &Uuid {
69 &self.id
70 }
71
72 pub fn response(&self) -> &Response {
74 &self.response
75 }
76
77 pub fn priority(&self) -> u8 {
79 self.priority
80 }
81
82 pub fn match_count(&self) -> usize {
84 self.match_count.load(Ordering::Relaxed)
85 }
86
87 pub fn matches(&self, req: &Request) -> bool {
89 if let Some(limit) = self.limit {
90 if self.match_count.load(Ordering::Relaxed) >= limit {
91 return false;
92 }
93 }
94 let matched = self.matchers.iter().all(|matcher| matcher.matches(req));
95 if matched {
96 self.match_count.fetch_add(1, Ordering::Relaxed);
97 }
98 matched
99 }
100
101 pub fn reset(&self) {
103 self.match_count.store(0, Ordering::Relaxed);
104 }
105}
106
107impl PartialEq for Mock {
108 fn eq(&self, other: &Self) -> bool {
109 self.id == other.id
110 && self.matchers == other.matchers
111 && self.response == other.response
112 && self.priority == other.priority
113 && self.match_count.load(Ordering::Relaxed) == other.match_count.load(Ordering::Relaxed)
114 && self.limit == other.limit
115 }
116}
117
118impl Clone for Mock {
119 fn clone(&self) -> Self {
120 Self {
121 id: self.id,
122 matchers: self.matchers.clone(),
123 response: self.response.clone(),
124 priority: self.priority,
125 match_count: AtomicUsize::new(self.match_count.load(Ordering::Relaxed)),
126 limit: self.limit,
127 }
128 }
129}
130
131#[cfg(test)]
132mod tests {
133 use super::*;
134 use crate::Method;
135
136 #[test]
137 fn test_match_counter() {
138 let mock = Mock::new(|when, then| {
139 when.get();
140 then.ok();
141 });
142 let request = Request::new(Method::GET, "http://localhost/".parse().unwrap());
143 mock.matches(&request);
144 assert_eq!(mock.match_count(), 1);
145 mock.matches(&request);
146 assert_eq!(mock.match_count(), 2);
147 mock.reset();
148 assert_eq!(mock.match_count(), 0);
149 }
150
151 #[test]
152 fn test_limit() {
153 let mock = Mock::new(|when, then| {
154 when.get();
155 then.ok();
156 })
157 .with_limit(2);
158 let request = Request::new(Method::GET, "http://localhost/".parse().unwrap());
159 assert!(mock.matches(&request));
160 assert!(mock.matches(&request));
161 assert!(!mock.matches(&request));
162 }
163}