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}