wiremock/matchers.rs
1//! A collection of different matching strategies provided out-of-the-box by `wiremock`.
2//!
3//! If the set of matchers provided out-of-the-box is not enough for your specific testing needs
4//! you can implement your own thanks to the [`Match`] trait.
5//!
6//! Furthermore, `Fn` closures that take an immutable [`Request`] reference as input and return a boolean
7//! as input automatically implement [`Match`] and can be used where a matcher is expected.
8//!
9//! Check [`Match`]'s documentation for examples.
10use crate::{Match, Request};
11use assert_json_diff::{CompareMode, assert_json_matches_no_panic};
12use base64::prelude::{BASE64_STANDARD, Engine as _};
13use http::{HeaderName, HeaderValue, Method};
14use log::debug;
15use regex::Regex;
16use serde::Serialize;
17use serde_json::Value;
18use std::convert::TryInto;
19use std::str::{self, FromStr};
20use url::Url;
21
22/// Implement the `Match` trait for all closures, out of the box,
23/// if their signature is compatible.
24impl<F> Match for F
25where
26 F: Fn(&Request) -> bool,
27 F: Send + Sync,
28{
29 fn matches(&self, request: &Request) -> bool {
30 // Just call the closure itself!
31 self(request)
32 }
33}
34
35#[derive(Debug)]
36/// Match **exactly** the method of a request.
37///
38/// ### Example:
39/// ```rust
40/// use wiremock::{MockServer, Mock, ResponseTemplate};
41/// use wiremock::matchers::method;
42///
43/// #[async_std::main]
44/// async fn main() {
45/// // Arrange
46/// let mock_server = MockServer::start().await;
47///
48/// let response = ResponseTemplate::new(200);
49/// let mock = Mock::given(method("GET")).respond_with(response);
50///
51/// mock_server.register(mock).await;
52///
53/// // Act
54/// let status = reqwest::get(&mock_server.uri())
55/// .await
56/// .unwrap()
57/// .status();
58///
59/// // Assert
60/// assert_eq!(status, 200);
61/// }
62/// ```
63pub struct MethodExactMatcher(Method);
64
65/// Shorthand for [`MethodExactMatcher::new`].
66pub fn method<T>(method: T) -> MethodExactMatcher
67where
68 T: AsRef<str>,
69{
70 MethodExactMatcher::new(method)
71}
72
73impl MethodExactMatcher {
74 pub fn new<T>(method: T) -> Self
75 where
76 T: AsRef<str>,
77 {
78 let method = Method::from_str(&method.as_ref().to_ascii_uppercase())
79 .expect("Failed to convert to HTTP method.");
80 Self(method)
81 }
82}
83
84impl Match for MethodExactMatcher {
85 fn matches(&self, request: &Request) -> bool {
86 request.method == self.0
87 }
88}
89
90#[derive(Debug)]
91/// Match all incoming requests, regardless of their method, path, headers or body.
92///
93/// You can use it to verify that a request has been fired towards the server, without making
94/// any other assertion about it.
95///
96/// ### Example:
97/// ```rust
98/// use wiremock::{MockServer, Mock, ResponseTemplate};
99/// use wiremock::matchers::any;
100///
101/// #[async_std::main]
102/// async fn main() {
103/// // Arrange
104/// let mock_server = MockServer::start().await;
105///
106/// let response = ResponseTemplate::new(200);
107/// // Respond with a `200 OK` to all requests hitting
108/// // the mock server
109/// let mock = Mock::given(any()).respond_with(response);
110///
111/// mock_server.register(mock).await;
112///
113/// // Act
114/// let status = reqwest::get(&mock_server.uri())
115/// .await
116/// .unwrap()
117/// .status();
118///
119/// // Assert
120/// assert_eq!(status, 200);
121/// }
122/// ```
123pub struct AnyMatcher;
124
125/// Shorthand for [`AnyMatcher`].
126pub fn any() -> AnyMatcher {
127 AnyMatcher
128}
129
130impl Match for AnyMatcher {
131 fn matches(&self, _request: &Request) -> bool {
132 true
133 }
134}
135
136#[derive(Debug)]
137/// Match **exactly** the path of a request.
138///
139/// ### Example:
140/// ```rust
141/// use wiremock::{MockServer, Mock, ResponseTemplate};
142/// use wiremock::matchers::path;
143///
144/// #[async_std::main]
145/// async fn main() {
146/// // Arrange
147/// let mock_server = MockServer::start().await;
148///
149/// let response = ResponseTemplate::new(200).set_body_string("world");
150/// let mock = Mock::given(path("/hello")).respond_with(response);
151///
152/// mock_server.register(mock).await;
153///
154/// // Act
155/// let status = reqwest::get(format!("{}/hello", &mock_server.uri()))
156/// .await
157/// .unwrap()
158/// .status();
159///
160/// // Assert
161/// assert_eq!(status, 200);
162/// }
163/// ```
164///
165/// ### Example:
166///
167/// The path matcher ignores query parameters:
168///
169/// ```rust
170/// use wiremock::{MockServer, Mock, ResponseTemplate};
171/// use wiremock::matchers::path;
172///
173/// #[async_std::main]
174/// async fn main() {
175/// // Arrange
176/// let mock_server = MockServer::start().await;
177///
178/// let response = ResponseTemplate::new(200).set_body_string("world");
179/// let mock = Mock::given(path("/hello")).respond_with(response);
180///
181/// mock_server.register(mock).await;
182///
183/// // Act
184/// let status = reqwest::get(format!("{}/hello?a_parameter=some_value", &mock_server.uri()))
185/// .await
186/// .unwrap()
187/// .status();
188///
189/// // Assert
190/// assert_eq!(status, 200);
191/// }
192/// ```
193pub struct PathExactMatcher(String);
194
195/// Shorthand for [`PathExactMatcher::new`].
196pub fn path<T>(path: T) -> PathExactMatcher
197where
198 T: Into<String>,
199{
200 PathExactMatcher::new(path)
201}
202
203impl PathExactMatcher {
204 pub fn new<T: Into<String>>(path: T) -> Self {
205 let path = path.into();
206
207 if path.contains('?') {
208 panic!(
209 "Wiremock can't match the path `{}` because it contains a `?`. You must use `wiremock::matchers::query_param` to match on query parameters (the part of the path after the `?`).",
210 path
211 );
212 }
213
214 if let Ok(url) = Url::parse(&path)
215 && let Some(host) = url.host_str()
216 {
217 panic!(
218 "Wiremock can't match the path `{}` because it contains the host `{}`. You don't have to specify the host - wiremock knows it. Try replacing your path with `path(\"{}\")`",
219 path,
220 host,
221 url.path()
222 );
223 }
224
225 // Prepend "/" to the path if missing.
226 if path.starts_with('/') {
227 Self(path)
228 } else {
229 Self(format!("/{}", path))
230 }
231 }
232}
233
234impl Match for PathExactMatcher {
235 fn matches(&self, request: &Request) -> bool {
236 request.url.path() == self.0
237 }
238}
239
240#[derive(Debug)]
241/// Match the path of a request against a regular expression.
242///
243/// ### Example:
244/// ```rust
245/// use wiremock::{MockServer, Mock, ResponseTemplate};
246/// use wiremock::matchers::path_regex;
247///
248/// #[async_std::main]
249/// async fn main() {
250/// // Arrange
251/// let mock_server = MockServer::start().await;
252///
253/// let response = ResponseTemplate::new(200).set_body_string("world");
254/// let mock = Mock::given(path_regex(r"^/hello/\d{3}$")).respond_with(response);
255///
256/// mock_server.register(mock).await;
257///
258/// // Act
259/// let status = reqwest::get(format!("{}/hello/123", &mock_server.uri()))
260/// .await
261/// .unwrap()
262/// .status();
263///
264/// // Assert
265/// assert_eq!(status, 200);
266/// }
267/// ```
268///
269/// ### Example:
270/// ```rust
271/// use wiremock::{MockServer, Mock, ResponseTemplate};
272/// use wiremock::matchers::path_regex;
273///
274/// #[async_std::main]
275/// async fn main() {
276/// // Arrange
277/// let mock_server = MockServer::start().await;
278///
279/// let response = ResponseTemplate::new(200).set_body_string("world");
280/// let mock = Mock::given(path_regex(r"^/users/[a-z0-9-~_]{1,}/posts$")).respond_with(response);
281///
282/// mock_server.register(mock).await;
283///
284/// // Act
285/// let status = reqwest::get(format!("{}/users/da2854ea-b70f-46e7-babc-2846eff4d33c/posts", &mock_server.uri()))
286/// .await
287/// .unwrap()
288/// .status();
289///
290/// // Assert
291/// assert_eq!(status, 200);
292/// }
293/// ```
294pub struct PathRegexMatcher(Regex);
295
296/// Shorthand for [`PathRegexMatcher::new`].
297pub fn path_regex<T>(path: T) -> PathRegexMatcher
298where
299 T: Into<String>,
300{
301 PathRegexMatcher::new(path)
302}
303
304impl PathRegexMatcher {
305 pub fn new<T: Into<String>>(path: T) -> Self {
306 let path = path.into();
307
308 Self(Regex::new(&path).expect("Failed to create regex for path matcher"))
309 }
310}
311
312impl Match for PathRegexMatcher {
313 fn matches(&self, request: &Request) -> bool {
314 self.0.is_match(request.url.path())
315 }
316}
317
318#[derive(Debug)]
319/// Match **exactly** the header of a request.
320///
321/// ### Example:
322/// ```rust
323/// use wiremock::{MockServer, Mock, ResponseTemplate};
324/// use wiremock::matchers::{header, headers};
325///
326/// #[async_std::main]
327/// async fn main() {
328/// // Arrange
329/// let mock_server = MockServer::start().await;
330///
331/// Mock::given(header("custom", "header"))
332/// .and(headers("cache-control", vec!["no-cache", "no-store"]))
333/// .respond_with(ResponseTemplate::new(200))
334/// .mount(&mock_server)
335/// .await;
336///
337/// // Act
338/// let client = reqwest::Client::new();
339/// let status = client
340/// .get(&mock_server.uri())
341/// .header("custom", "header")
342/// .header("cache-control", "no-cache, no-store")
343/// .send()
344/// .await
345/// .unwrap()
346/// .status();
347///
348/// // Assert
349/// assert_eq!(status, 200);
350/// }
351/// ```
352pub struct HeaderExactMatcher(HeaderName, Vec<HeaderValue>);
353
354/// Shorthand for [`HeaderExactMatcher::new`].
355pub fn header<K, V>(key: K, value: V) -> HeaderExactMatcher
356where
357 K: TryInto<HeaderName>,
358 <K as TryInto<HeaderName>>::Error: std::fmt::Debug,
359 V: TryInto<HeaderValue>,
360 <V as TryInto<HeaderValue>>::Error: std::fmt::Debug,
361{
362 HeaderExactMatcher::new(key, vec![value])
363}
364
365/// Shorthand for [`HeaderExactMatcher::new`] supporting multi valued headers.
366pub fn headers<K, V>(key: K, values: Vec<V>) -> HeaderExactMatcher
367where
368 K: TryInto<HeaderName>,
369 <K as TryInto<HeaderName>>::Error: std::fmt::Debug,
370 V: TryInto<HeaderValue>,
371 <V as TryInto<HeaderValue>>::Error: std::fmt::Debug,
372{
373 HeaderExactMatcher::new(key, values)
374}
375
376impl HeaderExactMatcher {
377 pub fn new<K, V>(key: K, values: Vec<V>) -> Self
378 where
379 K: TryInto<HeaderName>,
380 <K as TryInto<HeaderName>>::Error: std::fmt::Debug,
381 V: TryInto<HeaderValue>,
382 <V as TryInto<HeaderValue>>::Error: std::fmt::Debug,
383 {
384 let key = key.try_into().expect("Failed to convert to header name.");
385 let values = values
386 .into_iter()
387 .map(|value| {
388 value
389 .try_into()
390 .expect("Failed to convert to header value.")
391 })
392 .collect();
393 Self(key, values)
394 }
395}
396
397impl Match for HeaderExactMatcher {
398 fn matches(&self, request: &Request) -> bool {
399 let values = request
400 .headers
401 .get_all(&self.0)
402 .iter()
403 .filter_map(|v| v.to_str().ok())
404 .flat_map(|v| {
405 v.split(',')
406 .map(str::trim)
407 .filter_map(|v| HeaderValue::from_str(v).ok())
408 })
409 .collect::<Vec<_>>();
410 values == self.1 // order matters
411 }
412}
413
414#[derive(Debug)]
415/// Match **exactly** the header name of a request. It checks that the
416/// header is present but does not validate the value.
417///
418/// ### Example:
419/// ```rust
420/// use wiremock::{MockServer, Mock, ResponseTemplate};
421/// use wiremock::matchers::header;
422///
423/// #[async_std::main]
424/// async fn main() {
425/// // Arrange
426/// use wiremock::matchers::header_exists;
427/// let mock_server = MockServer::start().await;
428///
429/// Mock::given(header_exists("custom"))
430/// .respond_with(ResponseTemplate::new(200))
431/// .mount(&mock_server)
432/// .await;
433///
434/// // Act
435/// let client = reqwest::Client::new();
436/// let status = client
437/// .get(&mock_server.uri())
438/// .header("custom", "header")
439/// .send()
440/// .await
441/// .unwrap()
442/// .status();
443///
444/// // Assert
445/// assert_eq!(status, 200);
446/// }
447/// ```
448pub struct HeaderExistsMatcher(HeaderName);
449
450/// Shorthand for [`HeaderExistsMatcher::new`].
451pub fn header_exists<K>(key: K) -> HeaderExistsMatcher
452where
453 K: TryInto<HeaderName>,
454 <K as TryInto<HeaderName>>::Error: std::fmt::Debug,
455{
456 HeaderExistsMatcher::new(key)
457}
458
459impl HeaderExistsMatcher {
460 pub fn new<K>(key: K) -> Self
461 where
462 K: TryInto<HeaderName>,
463 <K as TryInto<HeaderName>>::Error: std::fmt::Debug,
464 {
465 let key = key.try_into().expect("Failed to convert to header name.");
466 Self(key)
467 }
468}
469
470impl Match for HeaderExistsMatcher {
471 fn matches(&self, request: &Request) -> bool {
472 request.headers.get(&self.0).is_some()
473 }
474}
475
476#[derive(Debug)]
477/// Match the value of a header using a regular expression.
478/// If the header is multi-valued, all values must satisfy the regular expression.
479/// If the header is missing, the mock will not match.
480///
481/// ### Example:
482/// ```rust
483/// use wiremock::{MockServer, Mock, ResponseTemplate};
484/// use wiremock::matchers::header_regex;
485///
486/// #[async_std::main]
487/// async fn main() {
488/// // Arrange
489/// let mock_server = MockServer::start().await;
490///
491/// Mock::given(header_regex("custom", "header"))
492/// .respond_with(ResponseTemplate::new(200))
493/// .mount(&mock_server)
494/// .await;
495///
496/// // Act
497/// let client = reqwest::Client::new();
498/// let status = client.get(&mock_server.uri())
499/// .header("custom", "headers are fun to match on with a regex")
500/// .send()
501/// .await
502/// .unwrap()
503/// .status();
504///
505/// // Assert
506/// assert_eq!(status, 200);
507/// }
508/// ```
509pub struct HeaderRegexMatcher(HeaderName, Regex);
510
511/// Shorthand for [`HeaderRegexMatcher::new`].
512pub fn header_regex<K>(key: K, value: &str) -> HeaderRegexMatcher
513where
514 K: TryInto<HeaderName>,
515 <K as TryInto<HeaderName>>::Error: std::fmt::Debug,
516{
517 HeaderRegexMatcher::new(key, value)
518}
519
520impl HeaderRegexMatcher {
521 pub fn new<K>(key: K, value: &str) -> Self
522 where
523 K: TryInto<HeaderName>,
524 <K as TryInto<HeaderName>>::Error: std::fmt::Debug,
525 {
526 let key = key.try_into().expect("Failed to convert to header name.");
527 let value_matcher = Regex::new(value).expect("Failed to create regex for value matcher");
528 Self(key, value_matcher)
529 }
530}
531
532impl Match for HeaderRegexMatcher {
533 fn matches(&self, request: &Request) -> bool {
534 let mut it = request
535 .headers
536 .get_all(&self.0)
537 .iter()
538 .filter_map(|v| v.to_str().ok())
539 .peekable();
540 if it.peek().is_some() {
541 it.all(|v| self.1.is_match(v))
542 } else {
543 false
544 }
545 }
546}
547
548#[derive(Debug)]
549/// Match **exactly** the body of a request.
550///
551/// ### Example (string):
552/// ```rust
553/// use wiremock::{MockServer, Mock, ResponseTemplate};
554/// use wiremock::matchers::body_string;
555///
556/// #[async_std::main]
557/// async fn main() {
558/// // Arrange
559/// let mock_server = MockServer::start().await;
560///
561/// Mock::given(body_string("hello world!"))
562/// .respond_with(ResponseTemplate::new(200))
563/// .mount(&mock_server)
564/// .await;
565///
566/// // Act
567/// let client = reqwest::Client::new();
568/// let status = client.post(&mock_server.uri())
569/// .body("hello world!")
570/// .send()
571/// .await
572/// .unwrap()
573/// .status();
574///
575/// // Assert
576/// assert_eq!(status, 200);
577/// }
578/// ```
579///
580/// ### Example (json):
581/// ```rust
582/// use wiremock::{MockServer, Mock, ResponseTemplate};
583/// use wiremock::matchers::body_json;
584/// use serde_json::json;
585///
586/// #[async_std::main]
587/// async fn main() {
588/// // Arrange
589/// let mock_server = MockServer::start().await;
590///
591/// let expected_body = json!({
592/// "hello": "world!"
593/// });
594/// Mock::given(body_json(&expected_body))
595/// .respond_with(ResponseTemplate::new(200))
596/// .mount(&mock_server)
597/// .await;
598///
599/// // Act
600/// let client = reqwest::Client::new();
601/// let status = client.post(&mock_server.uri())
602/// .json(&expected_body)
603/// .send()
604/// .await
605/// .unwrap()
606/// .status();
607///
608/// // Assert
609/// assert_eq!(status, 200);
610/// }
611/// ```
612pub struct BodyExactMatcher(Body);
613
614#[derive(Debug)]
615enum Body {
616 Bytes(Vec<u8>),
617 Json(Value),
618}
619
620impl BodyExactMatcher {
621 /// Specify the expected body as a string.
622 pub fn string<T: Into<String>>(body: T) -> Self {
623 let body = body.into();
624 Self(Body::Bytes(body.into_bytes()))
625 }
626
627 /// Specify the expected body as a vector of bytes.
628 pub fn bytes<T: Into<Vec<u8>>>(body: T) -> Self {
629 let body = body.into();
630 Self(Body::Bytes(body))
631 }
632
633 /// Specify something JSON-serializable as the expected body.
634 pub fn json<T: Serialize>(body: T) -> Self {
635 let bytes = serde_json::to_vec(&body).expect("Failed to serialize JSON body");
636 Self::json_string(bytes)
637 }
638
639 /// Specify a JSON string as the expected body.
640 pub fn json_string(body: impl AsRef<[u8]>) -> Self {
641 let body = serde_json::from_slice(body.as_ref()).expect("Failed to parse JSON string");
642 Self(Body::Json(body))
643 }
644}
645
646/// Shorthand for [`BodyExactMatcher::string`].
647pub fn body_string<T>(body: T) -> BodyExactMatcher
648where
649 T: Into<String>,
650{
651 BodyExactMatcher::string(body)
652}
653
654/// Shorthand for [`BodyExactMatcher::bytes`].
655pub fn body_bytes<T>(body: T) -> BodyExactMatcher
656where
657 T: Into<Vec<u8>>,
658{
659 BodyExactMatcher::bytes(body)
660}
661
662/// Shorthand for [`BodyExactMatcher::json`].
663pub fn body_json<T>(body: T) -> BodyExactMatcher
664where
665 T: Serialize,
666{
667 BodyExactMatcher::json(body)
668}
669
670/// Shorthand for [`BodyExactMatcher::json_string`].
671pub fn body_json_string(body: impl AsRef<[u8]>) -> BodyExactMatcher {
672 BodyExactMatcher::json_string(body)
673}
674
675impl Match for BodyExactMatcher {
676 fn matches(&self, request: &Request) -> bool {
677 match &self.0 {
678 Body::Bytes(bytes) => request.body == *bytes,
679 Body::Json(json) => {
680 if let Ok(body) = serde_json::from_slice::<Value>(&request.body) {
681 body == *json
682 } else {
683 false
684 }
685 }
686 }
687 }
688}
689
690#[derive(Debug)]
691/// Match part of the body of a request.
692///
693/// ### Example (string):
694/// ```rust
695/// use wiremock::{MockServer, Mock, ResponseTemplate};
696/// use wiremock::matchers::body_string_contains;
697///
698/// #[async_std::main]
699/// async fn main() {
700/// // Arrange
701/// let mock_server = MockServer::start().await;
702///
703/// Mock::given(body_string_contains("hello world"))
704/// .respond_with(ResponseTemplate::new(200))
705/// .mount(&mock_server)
706/// .await;
707///
708/// // Act
709/// let client = reqwest::Client::new();
710/// let status = client.post(&mock_server.uri())
711/// .body("this is a hello world example!")
712/// .send()
713/// .await
714/// .unwrap()
715/// .status();
716///
717/// // Assert
718/// assert_eq!(status, 200);
719/// }
720/// ```
721pub struct BodyContainsMatcher(Vec<u8>);
722
723impl BodyContainsMatcher {
724 /// Specify the part of the body that should be matched as a string.
725 pub fn string<T: Into<String>>(body: T) -> Self {
726 Self(body.into().as_bytes().into())
727 }
728}
729
730/// Shorthand for [`BodyContainsMatcher::string`].
731pub fn body_string_contains<T>(body: T) -> BodyContainsMatcher
732where
733 T: Into<String>,
734{
735 BodyContainsMatcher::string(body)
736}
737
738impl Match for BodyContainsMatcher {
739 fn matches(&self, request: &Request) -> bool {
740 let body = match str::from_utf8(&request.body) {
741 Ok(body) => body.to_string(),
742 Err(err) => {
743 debug!("can't convert body from byte slice to string: {}", err);
744 return false;
745 }
746 };
747
748 let part = match str::from_utf8(&self.0) {
749 Ok(part) => part,
750 Err(err) => {
751 debug!(
752 "can't convert expected part from byte slice to string: {}",
753 err
754 );
755 return false;
756 }
757 };
758
759 body.contains(part)
760 }
761}
762
763#[derive(Debug)]
764/// Match part JSON body of a request.
765///
766/// ### Example:
767/// ```rust
768/// use wiremock::{MockServer, Mock, ResponseTemplate};
769/// use wiremock::matchers::body_partial_json;
770/// use serde_json::json;
771///
772/// #[async_std::main]
773/// async fn main() {
774/// // Arrange
775/// let mock_server = MockServer::start().await;
776///
777/// let expected_body = json!({
778/// "hello": "world!"
779/// });
780/// Mock::given(body_partial_json(&expected_body))
781/// .respond_with(ResponseTemplate::new(200))
782/// .mount(&mock_server)
783/// .await;
784///
785/// // Act
786/// let body = json!({
787/// "hello": "world!",
788/// "foo": "bar"
789/// });
790/// let client = reqwest::Client::new();
791/// let status = client.post(&mock_server.uri())
792/// .json(&body)
793/// .send()
794/// .await
795/// .unwrap()
796/// .status();
797///
798/// // Assert
799/// assert_eq!(status, 200);
800/// }
801/// ```
802pub struct BodyPartialJsonMatcher(Value);
803
804impl BodyPartialJsonMatcher {
805 /// Specify the part of the body that should be matched as a JSON value.
806 pub fn json<T: Serialize>(body: T) -> Self {
807 Self(serde_json::to_value(body).expect("Can't serialize to JSON"))
808 }
809
810 /// Specify the part of the body that should be matched as a string.
811 pub fn json_string(body: impl AsRef<str>) -> Self {
812 Self(serde_json::from_str(body.as_ref()).expect("Can't deserialize JSON"))
813 }
814}
815
816/// Shorthand for [`BodyPartialJsonMatcher::json`].
817pub fn body_partial_json<T: Serialize>(body: T) -> BodyPartialJsonMatcher {
818 BodyPartialJsonMatcher::json(body)
819}
820
821/// Shorthand for [`BodyPartialJsonMatcher::json_string`].
822pub fn body_partial_json_string(body: impl AsRef<str>) -> BodyPartialJsonMatcher {
823 BodyPartialJsonMatcher::json_string(body)
824}
825
826impl Match for BodyPartialJsonMatcher {
827 fn matches(&self, request: &Request) -> bool {
828 if let Ok(body) = serde_json::from_slice::<Value>(&request.body) {
829 let config = assert_json_diff::Config::new(CompareMode::Inclusive);
830 assert_json_matches_no_panic(&body, &self.0, config).is_ok()
831 } else {
832 false
833 }
834 }
835}
836
837#[derive(Debug)]
838/// Match **exactly** the query parameter of a request.
839///
840/// ### Example:
841/// ```rust
842/// use wiremock::{MockServer, Mock, ResponseTemplate};
843/// use wiremock::matchers::query_param;
844///
845/// #[async_std::main]
846/// async fn main() {
847/// // Arrange
848/// let mock_server = MockServer::start().await;
849///
850/// Mock::given(query_param("hello", "world"))
851/// .respond_with(ResponseTemplate::new(200))
852/// .mount(&mock_server)
853/// .await;
854///
855/// // Act
856/// let status = reqwest::get(format!("{}?hello=world", &mock_server.uri()))
857/// .await
858/// .unwrap()
859/// .status();
860///
861/// // Assert
862/// assert_eq!(status, 200);
863/// }
864/// ```
865pub struct QueryParamExactMatcher(String, String);
866
867impl QueryParamExactMatcher {
868 /// Specify the expected value for a query parameter.
869 pub fn new<K: Into<String>, V: Into<String>>(key: K, value: V) -> Self {
870 let key = key.into();
871 let value = value.into();
872 Self(key, value)
873 }
874}
875
876/// Shorthand for [`QueryParamExactMatcher::new`].
877pub fn query_param<K, V>(key: K, value: V) -> QueryParamExactMatcher
878where
879 K: Into<String>,
880 V: Into<String>,
881{
882 QueryParamExactMatcher::new(key, value)
883}
884
885impl Match for QueryParamExactMatcher {
886 fn matches(&self, request: &Request) -> bool {
887 request
888 .url
889 .query_pairs()
890 .any(|q| q.0 == self.0.as_str() && q.1 == self.1.as_str())
891 }
892}
893
894#[derive(Debug)]
895/// Match when a query parameter contains the specified value as a substring.
896///
897/// ### Example:
898/// ```rust
899/// use wiremock::{MockServer, Mock, ResponseTemplate};
900/// use wiremock::matchers::query_param_contains;
901///
902/// #[async_std::main]
903/// async fn main() {
904/// // Arrange
905/// let mock_server = MockServer::start().await;
906///
907/// // It matches since "world" is a substring of "some_world".
908/// Mock::given(query_param_contains("hello", "world"))
909/// .respond_with(ResponseTemplate::new(200))
910/// .mount(&mock_server)
911/// .await;
912///
913/// // Act
914/// let status = reqwest::get(format!("{}?hello=some_world", &mock_server.uri()))
915/// .await
916/// .unwrap()
917/// .status();
918///
919/// // Assert
920/// assert_eq!(status, 200);
921/// }
922/// ```
923pub struct QueryParamContainsMatcher(String, String);
924
925impl QueryParamContainsMatcher {
926 /// Specify the substring that the query parameter should contain.
927 pub fn new<K: Into<String>, V: Into<String>>(key: K, value: V) -> Self {
928 let key = key.into();
929 let value = value.into();
930 Self(key, value)
931 }
932}
933
934/// Shorthand for [`QueryParamContainsMatcher::new`].
935pub fn query_param_contains<K, V>(key: K, value: V) -> QueryParamContainsMatcher
936where
937 K: Into<String>,
938 V: Into<String>,
939{
940 QueryParamContainsMatcher::new(key, value)
941}
942
943impl Match for QueryParamContainsMatcher {
944 fn matches(&self, request: &Request) -> bool {
945 request
946 .url
947 .query_pairs()
948 .any(|q| q.0 == self.0.as_str() && q.1.contains(self.1.as_str()))
949 }
950}
951
952#[derive(Debug)]
953/// Only match requests that do **not** contain a specified query parameter.
954///
955/// ### Example:
956/// ```rust
957/// use wiremock::{MockServer, Mock, ResponseTemplate};
958/// use wiremock::matchers::{method, query_param_is_missing};
959///
960/// #[async_std::main]
961/// async fn main() {
962/// // Arrange
963/// let mock_server = MockServer::start().await;
964///
965/// Mock::given(method("GET"))
966/// .and(query_param_is_missing("unexpected"))
967/// .respond_with(ResponseTemplate::new(200))
968/// .mount(&mock_server)
969/// .await;
970///
971/// // Act
972/// let ok_status = reqwest::get(mock_server.uri().to_string())
973/// .await
974/// .unwrap()
975/// .status();
976///
977/// // Assert
978/// assert_eq!(ok_status, 200);
979///
980/// // Act
981/// let err_status = reqwest::get(format!("{}?unexpected=foo", mock_server.uri()))
982/// .await.
983/// unwrap().status();
984///
985/// // Assert
986/// assert_eq!(err_status, 404);
987/// }
988/// ```
989pub struct QueryParamIsMissingMatcher(String);
990
991impl QueryParamIsMissingMatcher {
992 /// Specify the query parameter that is expected to not exist.
993 pub fn new<K: Into<String>>(key: K) -> Self {
994 let key = key.into();
995 Self(key)
996 }
997}
998
999/// Shorthand for [`QueryParamIsMissingMatcher::new`].
1000pub fn query_param_is_missing<K>(key: K) -> QueryParamIsMissingMatcher
1001where
1002 K: Into<String>,
1003{
1004 QueryParamIsMissingMatcher::new(key)
1005}
1006
1007impl Match for QueryParamIsMissingMatcher {
1008 fn matches(&self, request: &Request) -> bool {
1009 !request.url.query_pairs().any(|(k, _)| k == self.0)
1010 }
1011}
1012/// Match an incoming request if its body is encoded as JSON and can be deserialized
1013/// according to the specified schema.
1014///
1015/// ### Example:
1016/// ```rust
1017/// use wiremock::{MockServer, Mock, ResponseTemplate};
1018/// use wiremock::matchers::body_json_schema;
1019/// use serde_json::json;
1020/// use serde::{Deserialize, Serialize};
1021///
1022/// // The schema we expect the body to conform to.
1023/// #[derive(Deserialize, Serialize)]
1024/// struct Greeting {
1025/// hello: String,
1026/// }
1027///
1028/// #[async_std::main]
1029/// async fn main() {
1030/// // Arrange
1031/// let mock_server = MockServer::start().await;
1032///
1033/// Mock::given(body_json_schema::<Greeting>)
1034/// .respond_with(ResponseTemplate::new(200))
1035/// .mount(&mock_server)
1036/// .await;
1037///
1038/// // Both JSON objects have the same fields,
1039/// // therefore they'll match.
1040/// let success_cases = vec![
1041/// json!({"hello": "world!"}),
1042/// json!({"hello": "everyone!"}),
1043/// ];
1044/// let client = reqwest::Client::new();
1045/// for case in &success_cases {
1046/// let status = client.post(&mock_server.uri())
1047/// .json(case)
1048/// .send()
1049/// .await
1050/// .unwrap()
1051/// .status();
1052///
1053/// // Assert
1054/// assert_eq!(status, 200);
1055/// }
1056///
1057/// // This JSON object cannot be deserialized as `Greeting`
1058/// // because it does not have the `hello` field.
1059/// // It won't match.
1060/// let failure_case = json!({"world": "hello!"});
1061/// let status = client.post(&mock_server.uri())
1062/// .json(&failure_case)
1063/// .send()
1064/// .await
1065/// .unwrap()
1066/// .status();
1067///
1068/// // Assert
1069/// assert_eq!(status, 404);
1070/// }
1071/// ```
1072pub fn body_json_schema<T>(request: &Request) -> bool
1073where
1074 for<'de> T: serde::de::Deserialize<'de>,
1075{
1076 serde_json::from_slice::<T>(&request.body).is_ok()
1077}
1078
1079#[derive(Debug)]
1080/// Match an incoming request if it contains the basic authentication header with the username and password
1081/// as per [RFC 7617](https://datatracker.ietf.org/doc/html/rfc7617).
1082///
1083/// ### Example:
1084/// ```rust
1085/// use wiremock::{MockServer, Mock, ResponseTemplate};
1086/// use wiremock::matchers::basic_auth;
1087/// use serde::{Deserialize, Serialize};
1088///
1089/// #[async_std::main]
1090/// async fn main() {
1091/// // Arrange
1092/// let mock_server = MockServer::start().await;
1093///
1094///
1095/// Mock::given(basic_auth("username", "password"))
1096/// .respond_with(ResponseTemplate::new(200))
1097/// .mount(&mock_server)
1098/// .await;
1099///
1100/// let client = reqwest::Client::new();
1101///
1102/// // Act
1103/// let status = client
1104/// .get(&mock_server.uri())
1105/// .header("Authorization", "Basic dXNlcm5hbWU6cGFzc3dvcmQ=")
1106/// .send()
1107/// .await
1108/// .unwrap()
1109/// .status();
1110///
1111/// // Assert
1112/// assert_eq!(status, 200);
1113/// }
1114/// ```
1115pub struct BasicAuthMatcher(HeaderExactMatcher);
1116
1117impl BasicAuthMatcher {
1118 /// Match basic authentication header using the given username and password.
1119 pub fn from_credentials(username: impl AsRef<str>, password: impl AsRef<str>) -> Self {
1120 Self::from_token(BASE64_STANDARD.encode(format!(
1121 "{}:{}",
1122 username.as_ref(),
1123 password.as_ref()
1124 )))
1125 }
1126
1127 /// Match basic authentication header with the exact token given.
1128 pub fn from_token(token: impl AsRef<str>) -> Self {
1129 Self(header(
1130 "Authorization",
1131 &*format!("Basic {}", token.as_ref()),
1132 ))
1133 }
1134}
1135
1136/// Shorthand for [`BasicAuthMatcher::from_credentials`].
1137pub fn basic_auth<U, P>(username: U, password: P) -> BasicAuthMatcher
1138where
1139 U: AsRef<str>,
1140 P: AsRef<str>,
1141{
1142 BasicAuthMatcher::from_credentials(username, password)
1143}
1144
1145impl Match for BasicAuthMatcher {
1146 fn matches(&self, request: &Request) -> bool {
1147 self.0.matches(request)
1148 }
1149}
1150
1151#[derive(Debug)]
1152/// Match an incoming request if it contains the bearer token header
1153/// as per [RFC 6750](https://datatracker.ietf.org/doc/html/rfc6750).
1154///
1155/// ### Example:
1156/// ```rust
1157/// use wiremock::{MockServer, Mock, ResponseTemplate};
1158/// use wiremock::matchers::bearer_token;
1159/// use serde::{Deserialize, Serialize};
1160///
1161/// #[async_std::main]
1162/// async fn main() {
1163/// // Arrange
1164/// let mock_server = MockServer::start().await;
1165///
1166/// Mock::given(bearer_token("token"))
1167/// .respond_with(ResponseTemplate::new(200))
1168/// .mount(&mock_server)
1169/// .await;
1170///
1171/// let client = reqwest::Client::new();
1172///
1173/// // Act
1174/// let status = client.get(&mock_server.uri())
1175/// .header("Authorization", "Bearer token")
1176/// .send()
1177/// .await
1178/// .unwrap()
1179/// .status();
1180///
1181/// // Assert
1182/// assert_eq!(status, 200);
1183/// }
1184/// ```
1185pub struct BearerTokenMatcher(HeaderExactMatcher);
1186
1187impl BearerTokenMatcher {
1188 pub fn from_token(token: impl AsRef<str>) -> Self {
1189 Self(header(
1190 "Authorization",
1191 &*format!("Bearer {}", token.as_ref()),
1192 ))
1193 }
1194}
1195
1196impl Match for BearerTokenMatcher {
1197 fn matches(&self, request: &Request) -> bool {
1198 self.0.matches(request)
1199 }
1200}
1201
1202/// Shorthand for [`BearerTokenMatcher::from_token`].
1203pub fn bearer_token<T>(token: T) -> BearerTokenMatcher
1204where
1205 T: AsRef<str>,
1206{
1207 BearerTokenMatcher::from_token(token)
1208}