axum_test/
test_response.rs

1use crate::internals::DebugResponseBody;
2use crate::internals::RequestPathFormatter;
3use crate::internals::StatusCodeFormatter;
4use crate::internals::TryIntoRangeBounds;
5use crate::internals::format_status_code_range;
6use anyhow::Context;
7use bytes::Bytes;
8use cookie::Cookie;
9use cookie::CookieJar;
10use http::HeaderMap;
11use http::HeaderValue;
12use http::Method;
13use http::StatusCode;
14use http::header::HeaderName;
15use http::header::SET_COOKIE;
16use http::response::Parts;
17use serde::Serialize;
18use serde::de::DeserializeOwned;
19use serde_json::Value;
20use std::convert::AsRef;
21use std::fmt::Debug;
22use std::fmt::Display;
23use std::fs::File;
24use std::fs::read_to_string;
25use std::io::BufReader;
26use std::ops::RangeBounds;
27use std::path::Path;
28use url::Url;
29
30#[cfg(feature = "pretty-assertions")]
31use pretty_assertions::{assert_eq, assert_ne};
32
33#[cfg(feature = "ws")]
34use crate::TestWebSocket;
35#[cfg(feature = "ws")]
36use crate::internals::TestResponseWebSocket;
37
38#[cfg(not(feature = "old-json-diff"))]
39use expect_json::expect;
40#[cfg(not(feature = "old-json-diff"))]
41use expect_json::expect_json_eq;
42
43///
44/// The `TestResponse` is the result of a request created using a [`TestServer`](crate::TestServer).
45/// The `TestServer` builds a [`TestRequest`](crate::TestRequest), which when awaited,
46/// will produce the response.
47///
48/// ```rust
49/// # async fn test() -> Result<(), Box<dyn ::std::error::Error>> {
50/// #
51/// use axum::Json;
52/// use axum::Router;
53/// use axum::routing::get;
54/// use axum_test::TestServer;
55/// use serde::Deserialize;
56/// use serde::Serialize;
57///
58/// let app = Router::new()
59///     .route(&"/test", get(|| async { "hello!" }));
60///
61/// let server = TestServer::new(app)?;
62///
63/// // This builds a `TestResponse`
64/// let response = server.get(&"/todo").await;
65/// #
66/// # Ok(())
67/// # }
68/// ```
69///
70/// # Extracting Response
71///
72/// The functions [`TestResponse::json()`](crate::TestResponse::json()), [`TestResponse::text()`](crate::TestResponse::text()),
73/// and [`TestResponse::form()`](crate::TestResponse::form()),
74/// allow you to extract the underlying response content in different formats.
75///
76/// ```rust
77/// # async fn test() -> Result<(), Box<dyn ::std::error::Error>> {
78/// #
79/// # use axum::Json;
80/// # use axum::Router;
81/// # use axum::routing::get;
82/// # use serde::Deserialize;
83/// # use serde::Serialize;
84/// # use axum_test::TestServer;
85/// #
86/// # #[derive(Serialize, Deserialize, Debug)]
87/// # struct Todo {}
88/// #
89/// # let app = Router::new()
90/// #     .route(&"/test", get(|| async { "hello!" }));
91/// #
92/// # let server = TestServer::new(app)?;
93/// let todo_response = server.get(&"/todo")
94///         .await
95///         .json::<Todo>();
96///
97/// let response_as_raw_text = server.get(&"/todo")
98///         .await
99///         .text();
100/// #
101/// # Ok(())
102/// # }
103/// ```
104///
105/// [`TestResponse::as_bytes()`](crate::TestResponse::as_bytes()) and [`TestResponse::into_bytes()`](crate::TestResponse::into_bytes()),
106/// offer the underlying raw bytes to allow custom decoding.
107///
108/// Full code examples can be found within their documentation.
109///
110/// # Assertions
111///
112/// The result of a response can also be asserted using the many assertion functions.
113///
114/// ```rust
115/// # async fn test() -> Result<(), Box<dyn ::std::error::Error>> {
116/// #
117/// use axum::Json;
118/// use axum::Router;
119/// use axum_test::TestServer;
120/// use axum::routing::get;
121/// use serde::Deserialize;
122/// use serde::Serialize;
123///
124/// let app = Router::new()
125///     .route(&"/test", get(|| async { "hello!" }));
126///
127/// let server = TestServer::new(app)?;
128///
129/// let response = server.get(&"/todo").await;
130///
131/// // These assertions will panic if they are not fulfilled by the response.
132/// response.assert_status_ok();
133/// response.assert_text("hello!");
134/// #
135/// # Ok(())
136/// # }
137/// ```
138///
139#[derive(Debug, Clone)]
140pub struct TestResponse {
141    method: Method,
142
143    /// This is the actual url that was used for the request.
144    full_request_url: Url,
145    headers: HeaderMap<HeaderValue>,
146    status_code: StatusCode,
147    response_body: Bytes,
148
149    #[cfg(feature = "ws")]
150    websockets: TestResponseWebSocket,
151}
152
153impl TestResponse {
154    pub(crate) fn new(
155        method: Method,
156        full_request_url: Url,
157        parts: Parts,
158        response_body: Bytes,
159
160        #[cfg(feature = "ws")] websockets: TestResponseWebSocket,
161    ) -> Self {
162        Self {
163            method,
164            full_request_url,
165            headers: parts.headers,
166            status_code: parts.status,
167            response_body,
168
169            #[cfg(feature = "ws")]
170            websockets,
171        }
172    }
173
174    /// Returns the underlying response, extracted as a UTF-8 string.
175    ///
176    /// # Example
177    ///
178    /// ```rust
179    /// # async fn test() -> Result<(), Box<dyn ::std::error::Error>> {
180    /// #
181    /// use axum::Json;
182    /// use axum::Router;
183    /// use axum::routing::get;
184    /// use serde_json::json;
185    /// use serde_json::Value;
186    ///
187    /// use axum_test::TestServer;
188    ///
189    /// async fn route_get_todo() -> Json<Value> {
190    ///     Json(json!({
191    ///         "description": "buy milk",
192    ///     }))
193    /// }
194    ///
195    /// let app = Router::new()
196    ///     .route(&"/todo", get(route_get_todo));
197    ///
198    /// let server = TestServer::new(app)?;
199    /// let response = server.get(&"/todo").await;
200    ///
201    /// // Extract the response as a string on it's own.
202    /// let raw_text = response.text();
203    /// #
204    /// # Ok(())
205    /// # }
206    /// ```
207    #[must_use]
208    pub fn text(&self) -> String {
209        String::from_utf8_lossy(self.as_bytes()).to_string()
210    }
211
212    /// Deserializes the response, as Json, into the type given.
213    ///
214    /// If deserialization fails then this will panic.
215    ///
216    /// # Example
217    ///
218    /// ```rust
219    /// # async fn test() -> Result<(), Box<dyn ::std::error::Error>> {
220    /// #
221    /// use axum::Json;
222    /// use axum::Router;
223    /// use axum::routing::get;
224    /// use serde::Deserialize;
225    /// use serde::Serialize;
226    ///
227    /// use axum_test::TestServer;
228    ///
229    /// #[derive(Serialize, Deserialize, Debug)]
230    /// struct Todo {
231    ///     description: String,
232    /// }
233    ///
234    /// async fn route_get_todo() -> Json<Todo> {
235    ///     Json(Todo {
236    ///         description: "buy milk".to_string(),
237    ///     })
238    /// }
239    ///
240    /// let app = Router::new()
241    ///     .route(&"/todo", get(route_get_todo));
242    ///
243    /// let server = TestServer::new(app)?;
244    /// let response = server.get(&"/todo").await;
245    ///
246    /// // Extract the response as a `Todo` item.
247    /// let todo = response.json::<Todo>();
248    /// #
249    /// # Ok(())
250    /// # }
251    /// ```
252    ///
253    #[must_use]
254    pub fn json<T>(&self) -> T
255    where
256        T: DeserializeOwned,
257    {
258        serde_json::from_slice::<T>(self.as_bytes())
259            .with_context(|| {
260                let debug_request_format = self.debug_request_format();
261
262                format!("Deserializing response from Json, for request {debug_request_format}")
263            })
264            .unwrap()
265    }
266
267    /// Deserializes the response, as Yaml, into the type given.
268    ///
269    /// If deserialization fails then this will panic.
270    ///
271    /// # Example
272    ///
273    /// ```rust
274    /// # async fn test() -> Result<(), Box<dyn ::std::error::Error>> {
275    /// #
276    /// use axum::Router;
277    /// use axum::routing::get;
278    /// use axum_yaml::Yaml;
279    /// use serde::Deserialize;
280    /// use serde::Serialize;
281    ///
282    /// use axum_test::TestServer;
283    ///
284    /// #[derive(Serialize, Deserialize, Debug)]
285    /// struct Todo {
286    ///     description: String,
287    /// }
288    ///
289    /// async fn route_get_todo() -> Yaml<Todo> {
290    ///     Yaml(Todo {
291    ///         description: "buy milk".to_string(),
292    ///     })
293    /// }
294    ///
295    /// let app = Router::new()
296    ///     .route(&"/todo", get(route_get_todo));
297    ///
298    /// let server = TestServer::new(app)?;
299    /// let response = server.get(&"/todo").await;
300    ///
301    /// // Extract the response as a `Todo` item.
302    /// let todo = response.yaml::<Todo>();
303    /// #
304    /// # Ok(())
305    /// # }
306    /// ```
307    #[cfg(feature = "yaml")]
308    #[must_use]
309    pub fn yaml<T>(&self) -> T
310    where
311        T: DeserializeOwned,
312    {
313        serde_yaml::from_slice::<T>(self.as_bytes())
314            .with_context(|| {
315                let debug_request_format = self.debug_request_format();
316
317                format!("Deserializing response from YAML, for request {debug_request_format}")
318            })
319            .unwrap()
320    }
321
322    /// Deserializes the response, as MsgPack, into the type given.
323    ///
324    /// If deserialization fails then this will panic.
325    ///
326    /// # Example
327    ///
328    /// ```rust
329    /// # async fn test() -> Result<(), Box<dyn ::std::error::Error>> {
330    /// #
331    /// use axum::Router;
332    /// use axum::routing::get;
333    /// use axum_msgpack::MsgPack;
334    /// use serde::Deserialize;
335    /// use serde::Serialize;
336    ///
337    /// use axum_test::TestServer;
338    ///
339    /// #[derive(Serialize, Deserialize, Debug)]
340    /// struct Todo {
341    ///     description: String,
342    /// }
343    ///
344    /// async fn route_get_todo() -> MsgPack<Todo> {
345    ///     MsgPack(Todo {
346    ///         description: "buy milk".to_string(),
347    ///     })
348    /// }
349    ///
350    /// let app = Router::new()
351    ///     .route(&"/todo", get(route_get_todo));
352    ///
353    /// let server = TestServer::new(app)?;
354    /// let response = server.get(&"/todo").await;
355    ///
356    /// // Extract the response as a `Todo` item.
357    /// let todo = response.msgpack::<Todo>();
358    /// #
359    /// # Ok(())
360    /// # }
361    /// ```
362    #[cfg(feature = "msgpack")]
363    #[must_use]
364    pub fn msgpack<T>(&self) -> T
365    where
366        T: DeserializeOwned,
367    {
368        rmp_serde::from_slice::<T>(self.as_bytes())
369            .with_context(|| {
370                let debug_request_format = self.debug_request_format();
371
372                format!("Deserializing response from MsgPack, for request {debug_request_format}")
373            })
374            .unwrap()
375    }
376
377    /// Deserializes the response, as an urlencoded Form, into the type given.
378    ///
379    /// If deserialization fails then this will panic.
380    ///
381    /// # Example
382    ///
383    /// ```rust
384    /// # async fn test() -> Result<(), Box<dyn ::std::error::Error>> {
385    /// #
386    /// use axum::Form;
387    /// use axum::Router;
388    /// use axum::routing::get;
389    /// use serde::Deserialize;
390    /// use serde::Serialize;
391    ///
392    /// use axum_test::TestServer;
393    ///
394    /// #[derive(Serialize, Deserialize, Debug)]
395    /// struct Todo {
396    ///     description: String,
397    /// }
398    ///
399    /// async fn route_get_todo() -> Form<Todo> {
400    ///     Form(Todo {
401    ///         description: "buy milk".to_string(),
402    ///     })
403    /// }
404    ///
405    /// let app = Router::new()
406    ///     .route(&"/todo", get(route_get_todo));
407    ///
408    /// let server = TestServer::new(app)?;
409    /// let response = server.get(&"/todo").await;
410    ///
411    /// // Extract the response as a `Todo` item.
412    /// let todo = response.form::<Todo>();
413    /// #
414    /// # Ok(())
415    /// # }
416    /// ```
417    #[must_use]
418    pub fn form<T>(&self) -> T
419    where
420        T: DeserializeOwned,
421    {
422        serde_urlencoded::from_bytes::<T>(self.as_bytes())
423            .with_context(|| {
424                let debug_request_format = self.debug_request_format();
425
426                format!("Deserializing response from Form, for request {debug_request_format}")
427            })
428            .unwrap()
429    }
430
431    /// Returns the raw underlying response as `Bytes`.
432    #[must_use]
433    pub fn as_bytes(&self) -> &Bytes {
434        &self.response_body
435    }
436
437    /// Consumes this returning the underlying `Bytes`
438    /// in the response.
439    #[must_use]
440    pub fn into_bytes(self) -> Bytes {
441        self.response_body
442    }
443
444    /// The status_code of the response.
445    #[must_use]
446    pub fn status_code(&self) -> StatusCode {
447        self.status_code
448    }
449
450    /// The Method used to produce this response.
451    #[must_use]
452    pub fn request_method(&self) -> Method {
453        self.method.clone()
454    }
455
456    /// The full URL that was used to produce this response.
457    #[must_use]
458    pub fn request_url(&self) -> Url {
459        self.full_request_url.clone()
460    }
461
462    /// Finds a header with the given name.
463    /// If there are multiple headers with the same name,
464    /// then only the first [`HeaderValue`](::http::HeaderValue) will be returned.
465    ///
466    /// `None` is returned when no header was found.
467    #[must_use]
468    pub fn maybe_header<N>(&self, name: N) -> Option<HeaderValue>
469    where
470        N: TryInto<HeaderName>,
471        N::Error: Debug,
472    {
473        let header_name = name
474            .try_into()
475            .expect("Failed to build HeaderName from name given");
476        self.headers.get(header_name).map(|h| h.to_owned())
477    }
478
479    /// Returns the headers returned from the response.
480    #[must_use]
481    pub fn headers(&self) -> &HeaderMap<HeaderValue> {
482        &self.headers
483    }
484
485    #[must_use]
486    pub fn maybe_content_type(&self) -> Option<String> {
487        self.headers.get(http::header::CONTENT_TYPE).map(|header| {
488            header
489                .to_str()
490                .with_context(|| {
491                    format!("Failed to decode header CONTENT_TYPE, received '{header:?}'")
492                })
493                .unwrap()
494                .to_string()
495        })
496    }
497
498    #[must_use]
499    pub fn content_type(&self) -> String {
500        self.maybe_content_type()
501            .expect("CONTENT_TYPE not found in response header")
502    }
503
504    /// Finds a header with the given name.
505    /// If there are multiple headers with the same name,
506    /// then only the first will be returned.
507    ///
508    /// If no header is found, then this will panic.
509    #[must_use]
510    pub fn header<N>(&self, name: N) -> HeaderValue
511    where
512        N: TryInto<HeaderName> + Display + Clone,
513        N::Error: Debug,
514    {
515        let debug_header = name.clone();
516        let header_name = name
517            .try_into()
518            .expect("Failed to build HeaderName from name given, '{debug_header}'");
519        self.headers
520            .get(header_name)
521            .map(|h| h.to_owned())
522            .with_context(|| {
523                let debug_request_format = self.debug_request_format();
524
525                format!("Cannot find header {debug_header}, for request {debug_request_format}",)
526            })
527            .unwrap()
528    }
529
530    /// Iterates over all of the headers contained in the response.
531    pub fn iter_headers(&self) -> impl Iterator<Item = (&'_ HeaderName, &'_ HeaderValue)> {
532        self.headers.iter()
533    }
534
535    /// Iterates over all of the headers for a specific name, contained in the response.
536    pub fn iter_headers_by_name<N>(&self, name: N) -> impl Iterator<Item = &'_ HeaderValue>
537    where
538        N: TryInto<HeaderName>,
539        N::Error: Debug,
540    {
541        let header_name = name
542            .try_into()
543            .expect("Failed to build HeaderName from name given");
544        self.headers.get_all(header_name).iter()
545    }
546
547    #[must_use]
548    pub fn contains_header<N>(&self, name: N) -> bool
549    where
550        N: TryInto<HeaderName>,
551        N::Error: Debug,
552    {
553        let header_name = name
554            .try_into()
555            .expect("Failed to build HeaderName from name given");
556        self.headers.contains_key(header_name)
557    }
558
559    /// Asserts the header named is present in the response.
560    ///
561    /// If the header is not present, then the assertion fails.
562    #[track_caller]
563    pub fn assert_contains_header<N>(&self, name: N)
564    where
565        N: TryInto<HeaderName> + Display + Clone,
566        N::Error: Debug,
567    {
568        let debug_header_name = name.clone();
569        let debug_request_format = self.debug_request_format();
570        let has_header = self.contains_header(name);
571
572        assert!(
573            has_header,
574            "Expected header '{debug_header_name}' to be present in response, header was not found, for request {debug_request_format}"
575        );
576    }
577
578    #[track_caller]
579    pub fn assert_header<N, V>(&self, name: N, value: V)
580    where
581        N: TryInto<HeaderName> + Display + Clone,
582        N::Error: Debug,
583        V: TryInto<HeaderValue>,
584        V::Error: Debug,
585    {
586        let debug_header_name = name.clone();
587        let header_name = name
588            .try_into()
589            .expect("Failed to build HeaderName from name given");
590        let expected_header_value = value
591            .try_into()
592            .expect("Could not turn given value into HeaderValue");
593        let debug_request_format = self.debug_request_format();
594        let maybe_found_header_value = self.maybe_header(header_name);
595
596        match maybe_found_header_value {
597            None => {
598                panic!(
599                    "Expected header '{debug_header_name}' to be present in response, header was not found, for request {debug_request_format}"
600                )
601            }
602            Some(found_header_value) => {
603                assert_eq!(expected_header_value, found_header_value,)
604            }
605        }
606    }
607
608    /// Finds a [`Cookie`] with the given name.
609    /// If there are multiple matching cookies,
610    /// then only the first will be returned.
611    ///
612    /// `None` is returned if no Cookie is found.
613    #[must_use]
614    pub fn maybe_cookie(&self, cookie_name: &str) -> Option<Cookie<'static>> {
615        for cookie in self.iter_cookies() {
616            if cookie.name() == cookie_name {
617                return Some(cookie.into_owned());
618            }
619        }
620
621        None
622    }
623
624    /// Finds a [`Cookie`](::cookie::Cookie) with the given name.
625    /// If there are multiple matching cookies,
626    /// then only the first will be returned.
627    ///
628    /// If no `Cookie` is found, then this will panic.
629    #[must_use]
630    pub fn cookie(&self, cookie_name: &str) -> Cookie<'static> {
631        self.maybe_cookie(cookie_name)
632            .with_context(|| {
633                let debug_request_format = self.debug_request_format();
634
635                format!("Cannot find cookie {cookie_name}, for request {debug_request_format}")
636            })
637            .unwrap()
638    }
639
640    /// Returns all of the cookies contained in the response,
641    /// within a [`CookieJar`](::cookie::CookieJar) object.
642    ///
643    /// See the `cookie` crate for details.
644    #[must_use]
645    pub fn cookies(&self) -> CookieJar {
646        let mut cookies = CookieJar::new();
647
648        for cookie in self.iter_cookies() {
649            cookies.add(cookie.into_owned());
650        }
651
652        cookies
653    }
654
655    /// Iterate over all of the cookies in the response.
656    pub fn iter_cookies(&self) -> impl Iterator<Item = Cookie<'_>> {
657        self.iter_headers_by_name(SET_COOKIE).map(|header| {
658            let header_str = header
659                .to_str()
660                .with_context(|| {
661                    let debug_request_format = self.debug_request_format();
662
663                    format!(
664                        "Reading header 'Set-Cookie' as string, for request {debug_request_format}",
665                    )
666                })
667                .unwrap();
668
669            Cookie::parse(header_str)
670                .with_context(|| {
671                    let debug_request_format = self.debug_request_format();
672
673                    format!("Parsing 'Set-Cookie' header, for request {debug_request_format}",)
674                })
675                .unwrap()
676        })
677    }
678
679    /// Consumes the request, turning it into a `TestWebSocket`.
680    /// If this cannot be done, then the response will panic.
681    ///
682    /// *Note*, this requires the server to be running on a real HTTP
683    /// port. Either using a randomly assigned port, or a specified one.
684    /// See the [`TestServerConfig::transport`](crate::TestServerConfig::transport) for more details.
685    ///
686    /// # Example
687    ///
688    /// ```rust
689    /// # async fn test() -> Result<(), Box<dyn ::std::error::Error>> {
690    /// #
691    /// use axum::Router;
692    /// use axum_test::TestServer;
693    ///
694    /// let app = Router::new();
695    /// let server = TestServer::builder()
696    ///     .http_transport()
697    ///     .build(app)?;
698    ///
699    /// let mut websocket = server
700    ///     .get_websocket(&"/my-web-socket-end-point")
701    ///     .await
702    ///     .into_websocket()
703    ///     .await;
704    ///
705    /// websocket.send_text("Hello!").await;
706    /// #
707    /// # Ok(()) }
708    /// ```
709    ///
710    #[cfg(feature = "ws")]
711    #[must_use]
712    pub async fn into_websocket(self) -> TestWebSocket {
713        use crate::transport_layer::TransportLayerType;
714
715        // Using the mock approach will just fail.
716        if self.websockets.transport_type != TransportLayerType::Http {
717            unimplemented!(
718                "WebSocket requires a HTTP based transport layer, see `TestServerConfig::transport`"
719            );
720        }
721
722        let debug_request_format = self.debug_request_format().to_string();
723
724        let on_upgrade = self.websockets.maybe_on_upgrade.with_context(|| {
725            format!("Expected WebSocket upgrade to be found, it is None, for request {debug_request_format}")
726        })
727        .unwrap();
728
729        let upgraded = on_upgrade
730            .await
731            .with_context(|| {
732                format!("Failed to upgrade connection for, for request {debug_request_format}")
733            })
734            .unwrap();
735
736        TestWebSocket::new(upgraded).await
737    }
738
739    /// This performs an assertion comparing the whole body of the response,
740    /// against the text provided.
741    #[track_caller]
742    pub fn assert_text<C>(&self, expected: C)
743    where
744        C: AsRef<str>,
745    {
746        let expected_contents = expected.as_ref();
747        assert_eq!(expected_contents, &self.text());
748    }
749
750    /// This asserts if the text given is contained, somewhere, within the response.
751    #[track_caller]
752    pub fn assert_text_contains<C>(&self, expected: C)
753    where
754        C: AsRef<str>,
755    {
756        let expected_contents = expected.as_ref();
757        let received = self.text();
758        let is_contained = received.contains(expected_contents);
759
760        assert!(
761            is_contained,
762            "Failed to find '{expected_contents}', received '{received}'"
763        );
764    }
765
766    /// Asserts the response from the server matches the contents of the file.
767    #[track_caller]
768    pub fn assert_text_from_file<P>(&self, path: P)
769    where
770        P: AsRef<Path>,
771    {
772        let path_ref = path.as_ref();
773        let expected = read_to_string(path_ref)
774            .with_context(|| format!("Failed to read from file '{}'", path_ref.display()))
775            .unwrap();
776
777        self.assert_text(expected);
778    }
779
780    /// Deserializes the contents of the request as Json,
781    /// and asserts it matches the value given.
782    ///
783    /// If `other` does not match, or the response is not Json,
784    /// then this will panic.
785    ///
786    /// This includes all of the abilities from [`crate::expect_json`],
787    /// to allow you to check if things partially match. See that module
788    /// for more information.
789    ///
790    /// ```rust
791    /// # async fn test() -> Result<(), Box<dyn ::std::error::Error>> {
792    /// #
793    /// use axum::Router;
794    /// use axum::extract::Json;
795    /// use axum::routing::get;
796    /// use axum_test::TestServer;
797    /// use serde_json::json;
798    ///
799    /// let app = Router::new()
800    ///     .route(&"/user", get(|| async {
801    ///         Json(json!({
802    ///            "name": "Joe",
803    ///            "age": 20,
804    ///        }))
805    ///     }));
806    /// let server = TestServer::new(app)?;
807    ///
808    /// // Example 1
809    /// server.get(&"/user")
810    ///     .await
811    ///     .assert_json(&json!({
812    ///         "name": "Joe",
813    ///         "age": 20,
814    ///     }));
815    ///
816    /// // Example 2
817    /// server.get(&"/user")
818    ///     .await
819    ///     .assert_json(&json!({
820    ///         "name": axum_test::expect_json::string(),
821    ///         "age": axum_test::expect_json::integer().in_range(18..=30),
822    ///     }));
823    /// #
824    /// # Ok(()) }
825    /// ```
826    ///
827    /// This supports the ability to
828    #[track_caller]
829    pub fn assert_json<T>(&self, expected: &T)
830    where
831        T: Serialize + DeserializeOwned + PartialEq<T> + Debug,
832    {
833        let received = self.json::<T>();
834
835        #[cfg(feature = "old-json-diff")]
836        {
837            assert_eq!(*expected, received);
838        }
839
840        #[cfg(not(feature = "old-json-diff"))]
841        {
842            if *expected != received {
843                if let Err(error) = expect_json_eq(&received, &expected) {
844                    panic!(
845                        "
846{error}
847",
848                    );
849                }
850            }
851        }
852    }
853
854    /// Asserts the content is within the json returned.
855    /// This is useful for when servers return times and IDs that you
856    /// wish to ignore.
857    ///
858    /// ```rust
859    /// # async fn test() -> Result<(), Box<dyn ::std::error::Error>> {
860    /// #
861    /// use axum::Router;
862    /// use axum::extract::Json;
863    /// use axum::routing::get;
864    /// use axum_test::TestServer;
865    /// use serde_json::json;
866    /// use std::time::Instant;
867    ///
868    /// let app = Router::new()
869    ///     .route(&"/user", get(|| async {
870    ///         let id = Instant::now().elapsed().as_millis();
871    ///
872    ///         Json(json!({
873    ///            "id": id,
874    ///            "name": "Joe",
875    ///            "age": 20,
876    ///        }))
877    ///     }));
878    /// let server = TestServer::new(app)?;
879    ///
880    /// // Checks the response contains _only_ the values listed here,
881    /// // and ignores the rest.
882    /// server.get(&"/user")
883    ///     .await
884    ///     .assert_json_contains(&json!({
885    ///         "name": "Joe",
886    ///         "age": 20,
887    ///     }));
888    /// #
889    /// # Ok(()) }
890    /// ```
891    #[track_caller]
892    pub fn assert_json_contains<T>(&self, expected: &T)
893    where
894        T: Serialize,
895    {
896        let received = self.json::<Value>();
897
898        #[cfg(feature = "old-json-diff")]
899        {
900            assert_json_diff::assert_json_include!(actual: received, expected: expected);
901        }
902
903        #[cfg(not(feature = "old-json-diff"))]
904        {
905            let expected_value = serde_json::to_value(expected).unwrap();
906            let result = expect_json_eq(
907                &received,
908                &expect::object().propagated_contains(expected_value),
909            );
910            if let Err(error) = result {
911                panic!(
912                    "
913{error}
914",
915                );
916            }
917        }
918    }
919
920    /// Read json file from given path and assert it with json response.
921    ///
922    /// ```rust
923    /// # async fn test() -> Result<(), Box<dyn ::std::error::Error>> {
924    /// #
925    /// use axum::Json;
926    /// use axum::routing::get;
927    /// use axum::routing::Router;
928    /// use axum_test::TestServer;
929    /// use serde_json::json;
930    ///
931    /// let app = Router::new()
932    ///     .route(&"/json", get(|| async {
933    ///         Json(json!({
934    ///             "name": "Joe",
935    ///             "age": 20,
936    ///         }))
937    ///     }));
938    ///
939    /// let server = TestServer::new(app).unwrap();
940    /// server
941    ///     .get(&"/json")
942    ///     .await
943    ///     .assert_json_from_file("files/example.json");
944    /// #
945    /// # Ok(()) }
946    /// ```
947    ///
948    #[track_caller]
949    pub fn assert_json_from_file<P>(&self, path: P)
950    where
951        P: AsRef<Path>,
952    {
953        let path_ref = path.as_ref();
954        let file = File::open(path_ref)
955            .with_context(|| format!("Failed to read from file '{}'", path_ref.display()))
956            .unwrap();
957
958        let reader = BufReader::new(file);
959        let expected = serde_json::from_reader::<_, serde_json::Value>(reader)
960            .with_context(|| {
961                format!(
962                    "Failed to deserialize file '{}' as json",
963                    path_ref.display()
964                )
965            })
966            .unwrap();
967
968        self.assert_json(&expected);
969    }
970
971    /// Deserializes the contents of the request as Yaml,
972    /// and asserts it matches the value given.
973    ///
974    /// If `other` does not match, or the response is not Yaml,
975    /// then this will panic.
976    #[cfg(feature = "yaml")]
977    #[track_caller]
978    pub fn assert_yaml<T>(&self, other: &T)
979    where
980        T: DeserializeOwned + PartialEq<T> + Debug,
981    {
982        assert_eq!(*other, self.yaml::<T>());
983    }
984
985    /// Read yaml file from given path and assert it with yaml response.
986    #[cfg(feature = "yaml")]
987    #[track_caller]
988    pub fn assert_yaml_from_file<P>(&self, path: P)
989    where
990        P: AsRef<Path>,
991    {
992        let path_ref = path.as_ref();
993        let file = File::open(path_ref)
994            .with_context(|| format!("Failed to read from file '{}'", path_ref.display()))
995            .unwrap();
996
997        let reader = BufReader::new(file);
998        let expected = serde_yaml::from_reader::<_, serde_yaml::Value>(reader)
999            .with_context(|| {
1000                format!(
1001                    "Failed to deserialize file '{}' as yaml",
1002                    path_ref.display()
1003                )
1004            })
1005            .unwrap();
1006
1007        self.assert_yaml(&expected);
1008    }
1009
1010    /// Deserializes the contents of the request as MsgPack,
1011    /// and asserts it matches the value given.
1012    ///
1013    /// If `other` does not match, or the response is not MsgPack,
1014    /// then this will panic.
1015    #[cfg(feature = "msgpack")]
1016    #[track_caller]
1017    pub fn assert_msgpack<T>(&self, other: &T)
1018    where
1019        T: DeserializeOwned + PartialEq<T> + Debug,
1020    {
1021        assert_eq!(*other, self.msgpack::<T>());
1022    }
1023
1024    /// Deserializes the contents of the request as an url encoded form,
1025    /// and asserts it matches the value given.
1026    ///
1027    /// If `other` does not match, or the response cannot be deserialized,
1028    /// then this will panic.
1029    #[track_caller]
1030    pub fn assert_form<T>(&self, other: &T)
1031    where
1032        T: DeserializeOwned + PartialEq<T> + Debug,
1033    {
1034        assert_eq!(*other, self.form::<T>());
1035    }
1036
1037    /// Assert the response status code matches the one given.
1038    #[track_caller]
1039    pub fn assert_status(&self, expected_status_code: StatusCode) {
1040        let received_debug = StatusCodeFormatter(self.status_code);
1041        let expected_debug = StatusCodeFormatter(expected_status_code);
1042        let debug_request_format = self.debug_request_format();
1043        let debug_body = DebugResponseBody(self);
1044
1045        assert_eq!(
1046            expected_status_code, self.status_code,
1047            "Expected status code to be {expected_debug}, received {received_debug}, for request {debug_request_format}, with body {debug_body}"
1048        );
1049    }
1050
1051    /// Assert the response status code does **not** match the one given.
1052    #[track_caller]
1053    pub fn assert_not_status(&self, expected_status_code: StatusCode) {
1054        let received_debug = StatusCodeFormatter(self.status_code);
1055        let expected_debug = StatusCodeFormatter(expected_status_code);
1056        let debug_request_format = self.debug_request_format();
1057        let debug_body = DebugResponseBody(self);
1058
1059        assert_ne!(
1060            expected_status_code, self.status_code,
1061            "Expected status code to not be {expected_debug}, received {received_debug}, for request {debug_request_format}, with body {debug_body}"
1062        );
1063    }
1064
1065    /// Assert that the status code is **within** the 2xx range.
1066    /// i.e. The range from 200-299.
1067    #[track_caller]
1068    pub fn assert_status_success(&self) {
1069        let status_code = self.status_code.as_u16();
1070        let received_debug = StatusCodeFormatter(self.status_code);
1071        let debug_request_format = self.debug_request_format();
1072        let debug_body = DebugResponseBody(self);
1073
1074        assert!(
1075            200 <= status_code && status_code <= 299,
1076            "Expect status code within 2xx range, received {received_debug}, for request {debug_request_format}, with body {debug_body}"
1077        );
1078    }
1079
1080    /// Assert that the status code is **outside** the 2xx range.
1081    /// i.e. A status code less than 200, or 300 or more.
1082    #[track_caller]
1083    pub fn assert_status_failure(&self) {
1084        let status_code = self.status_code.as_u16();
1085        let received_debug = StatusCodeFormatter(self.status_code);
1086        let debug_request_format = self.debug_request_format();
1087        let debug_body = DebugResponseBody(self);
1088
1089        assert!(
1090            status_code < 200 || 299 < status_code,
1091            "Expect status code outside 2xx range, received {received_debug}, for request {debug_request_format}, with body {debug_body}"
1092        );
1093    }
1094
1095    /// Assert the status code is within the range given.
1096    ///
1097    /// ```rust
1098    /// # async fn test() -> Result<(), Box<dyn ::std::error::Error>> {
1099    /// #
1100    /// use axum::Json;
1101    /// use axum::routing::get;
1102    /// use axum::routing::Router;
1103    /// use axum_test::TestServer;
1104    /// use http::StatusCode;
1105    ///
1106    /// let app = Router::new()
1107    ///     .route(&"/json", get(|| async {
1108    ///         StatusCode::OK
1109    ///     }));
1110    /// let server = TestServer::new(app).unwrap();
1111    ///
1112    /// // Within success statuses
1113    /// server
1114    ///     .get(&"/json")
1115    ///     .await
1116    ///     .assert_status_in_range(200..=299);
1117    ///
1118    /// // Outside success
1119    /// server
1120    ///     .get(&"/json")
1121    ///     .await
1122    ///     .assert_status_in_range(300..);
1123    ///
1124    /// // Before server error
1125    /// server
1126    ///     .get(&"/json")
1127    ///     .await
1128    ///     .assert_status_in_range(..StatusCode::INTERNAL_SERVER_ERROR);
1129    /// #
1130    /// # Ok(()) }
1131    /// ```
1132    #[track_caller]
1133    pub fn assert_status_in_range<R, S>(&self, expected_status_range: R)
1134    where
1135        R: RangeBounds<S> + TryIntoRangeBounds<StatusCode> + Debug,
1136        S: TryInto<StatusCode>,
1137    {
1138        let range = TryIntoRangeBounds::<StatusCode>::try_into_range_bounds(expected_status_range)
1139            .expect("Failed to convert status code");
1140
1141        let status_code = self.status_code();
1142        let is_in_range = range.contains(&status_code);
1143        let debug_request_format = self.debug_request_format();
1144        let debug_body = DebugResponseBody(self);
1145
1146        assert!(
1147            is_in_range,
1148            "Expected status to be in range {}, received {status_code}, for request {debug_request_format}, with body {debug_body}",
1149            format_status_code_range(range)
1150        );
1151    }
1152
1153    /// Assert the status code is not within the range given.
1154    ///
1155    /// ```rust
1156    /// # async fn test() -> Result<(), Box<dyn ::std::error::Error>> {
1157    /// #
1158    /// use axum::Json;
1159    /// use axum::routing::get;
1160    /// use axum::routing::Router;
1161    /// use axum_test::TestServer;
1162    /// use http::StatusCode;
1163    ///
1164    /// let app = Router::new()
1165    ///     .route(&"/json", get(|| async {
1166    ///         StatusCode::NOT_FOUND
1167    ///     }));
1168    /// let server = TestServer::new(app).unwrap();
1169    ///
1170    /// // Is not success
1171    /// server
1172    ///     .get(&"/json")
1173    ///     .await
1174    ///     .assert_status_not_in_range(200..=299);
1175    ///
1176    /// // 300 or higher
1177    /// server
1178    ///     .get(&"/json")
1179    ///     .await
1180    ///     .assert_status_not_in_range(300..);
1181    ///
1182    /// // After server error
1183    /// server
1184    ///     .get(&"/json")
1185    ///     .await
1186    ///     .assert_status_not_in_range(..StatusCode::INTERNAL_SERVER_ERROR);
1187    /// #
1188    /// # Ok(()) }
1189    /// ```
1190    #[track_caller]
1191    pub fn assert_status_not_in_range<R, S>(&self, expected_status_range: R)
1192    where
1193        R: RangeBounds<S> + TryIntoRangeBounds<StatusCode> + Debug,
1194        S: TryInto<StatusCode>,
1195    {
1196        let range = TryIntoRangeBounds::<StatusCode>::try_into_range_bounds(expected_status_range)
1197            .expect("Failed to convert status code");
1198
1199        let status_code = self.status_code();
1200        let is_not_in_range = !range.contains(&status_code);
1201        let debug_request_format = self.debug_request_format();
1202        let debug_body = DebugResponseBody(self);
1203
1204        assert!(
1205            is_not_in_range,
1206            "Expected status is not in range {}, received {status_code}, for request {debug_request_format}, with body {debug_body}",
1207            format_status_code_range(range)
1208        );
1209    }
1210
1211    /// Assert the response status code is 200.
1212    #[track_caller]
1213    pub fn assert_status_ok(&self) {
1214        self.assert_status(StatusCode::OK)
1215    }
1216
1217    /// Assert the response status code is **not** 200.
1218    #[track_caller]
1219    pub fn assert_status_not_ok(&self) {
1220        self.assert_not_status(StatusCode::OK)
1221    }
1222
1223    /// Assert the response status code is 204.
1224    #[track_caller]
1225    pub fn assert_status_no_content(&self) {
1226        self.assert_status(StatusCode::NO_CONTENT)
1227    }
1228
1229    /// Assert the response status code is 303.
1230    #[track_caller]
1231    pub fn assert_status_see_other(&self) {
1232        self.assert_status(StatusCode::SEE_OTHER)
1233    }
1234
1235    /// Assert the response status code is 400.
1236    #[track_caller]
1237    pub fn assert_status_bad_request(&self) {
1238        self.assert_status(StatusCode::BAD_REQUEST)
1239    }
1240
1241    /// Assert the response status code is 404.
1242    #[track_caller]
1243    pub fn assert_status_not_found(&self) {
1244        self.assert_status(StatusCode::NOT_FOUND)
1245    }
1246
1247    /// Assert the response status code is 401.
1248    #[track_caller]
1249    pub fn assert_status_unauthorized(&self) {
1250        self.assert_status(StatusCode::UNAUTHORIZED)
1251    }
1252
1253    /// Assert the response status code is 403.
1254    #[track_caller]
1255    pub fn assert_status_forbidden(&self) {
1256        self.assert_status(StatusCode::FORBIDDEN)
1257    }
1258
1259    /// Assert the response status code is 409.
1260    pub fn assert_status_conflict(&self) {
1261        self.assert_status(StatusCode::CONFLICT)
1262    }
1263
1264    /// Assert the response status code is 413.
1265    ///
1266    /// The payload is too large.
1267    #[track_caller]
1268    pub fn assert_status_payload_too_large(&self) {
1269        self.assert_status(StatusCode::PAYLOAD_TOO_LARGE)
1270    }
1271
1272    /// Assert the response status code is 422.
1273    #[track_caller]
1274    pub fn assert_status_unprocessable_entity(&self) {
1275        self.assert_status(StatusCode::UNPROCESSABLE_ENTITY)
1276    }
1277
1278    /// Assert the response status code is 429.
1279    #[track_caller]
1280    pub fn assert_status_too_many_requests(&self) {
1281        self.assert_status(StatusCode::TOO_MANY_REQUESTS)
1282    }
1283
1284    /// Assert the response status code is 101.
1285    ///
1286    /// This type of code is used in Web Socket connection when
1287    /// first request.
1288    #[track_caller]
1289    pub fn assert_status_switching_protocols(&self) {
1290        self.assert_status(StatusCode::SWITCHING_PROTOCOLS)
1291    }
1292
1293    /// Assert the response status code is 500.
1294    #[track_caller]
1295    pub fn assert_status_internal_server_error(&self) {
1296        self.assert_status(StatusCode::INTERNAL_SERVER_ERROR)
1297    }
1298
1299    /// Assert the response status code is 503.
1300    #[track_caller]
1301    pub fn assert_status_service_unavailable(&self) {
1302        self.assert_status(StatusCode::SERVICE_UNAVAILABLE)
1303    }
1304
1305    fn debug_request_format(&self) -> RequestPathFormatter<'_> {
1306        RequestPathFormatter::new(&self.method, self.full_request_url.as_str(), None)
1307    }
1308}
1309
1310impl From<TestResponse> for Bytes {
1311    fn from(response: TestResponse) -> Self {
1312        response.into_bytes()
1313    }
1314}
1315
1316#[cfg(test)]
1317mod test_assert_header {
1318    use crate::TestServer;
1319    use axum::Router;
1320    use axum::http::HeaderMap;
1321    use axum::routing::get;
1322
1323    async fn route_get_header() -> HeaderMap {
1324        let mut headers = HeaderMap::new();
1325        headers.insert("x-my-custom-header", "content".parse().unwrap());
1326        headers
1327    }
1328
1329    #[tokio::test]
1330    async fn it_should_not_panic_if_contains_header_and_content_matches() {
1331        let router = Router::new().route(&"/header", get(route_get_header));
1332
1333        let server = TestServer::new(router).unwrap();
1334
1335        server
1336            .get(&"/header")
1337            .await
1338            .assert_header("x-my-custom-header", "content");
1339    }
1340
1341    #[tokio::test]
1342    #[should_panic]
1343    async fn it_should_panic_if_contains_header_and_content_does_not_match() {
1344        let router = Router::new().route(&"/header", get(route_get_header));
1345
1346        let server = TestServer::new(router).unwrap();
1347
1348        server
1349            .get(&"/header")
1350            .await
1351            .assert_header("x-my-custom-header", "different-content");
1352    }
1353
1354    #[tokio::test]
1355    #[should_panic]
1356    async fn it_should_panic_if_not_contains_header() {
1357        let router = Router::new().route(&"/header", get(route_get_header));
1358
1359        let server = TestServer::new(router).unwrap();
1360
1361        server
1362            .get(&"/header")
1363            .await
1364            .assert_header("x-custom-header-not-found", "content");
1365    }
1366}
1367
1368#[cfg(test)]
1369mod test_assert_contains_header {
1370    use crate::TestServer;
1371    use axum::Router;
1372    use axum::http::HeaderMap;
1373    use axum::routing::get;
1374
1375    async fn route_get_header() -> HeaderMap {
1376        let mut headers = HeaderMap::new();
1377        headers.insert("x-my-custom-header", "content".parse().unwrap());
1378        headers
1379    }
1380
1381    #[tokio::test]
1382    async fn it_should_not_panic_if_contains_header() {
1383        let router = Router::new().route(&"/header", get(route_get_header));
1384
1385        let server = TestServer::new(router).unwrap();
1386
1387        server
1388            .get(&"/header")
1389            .await
1390            .assert_contains_header("x-my-custom-header");
1391    }
1392
1393    #[tokio::test]
1394    #[should_panic]
1395    async fn it_should_panic_if_not_contains_header() {
1396        let router = Router::new().route(&"/header", get(route_get_header));
1397
1398        let server = TestServer::new(router).unwrap();
1399
1400        server
1401            .get(&"/header")
1402            .await
1403            .assert_contains_header("x-custom-header-not-found");
1404    }
1405}
1406
1407#[cfg(test)]
1408mod test_assert_success {
1409    use crate::TestServer;
1410    use axum::Router;
1411    use axum::routing::get;
1412    use http::StatusCode;
1413
1414    pub async fn route_get_pass() -> StatusCode {
1415        StatusCode::OK
1416    }
1417
1418    pub async fn route_get_fail() -> StatusCode {
1419        StatusCode::SERVICE_UNAVAILABLE
1420    }
1421
1422    #[tokio::test]
1423    async fn it_should_pass_when_200() {
1424        let router = Router::new()
1425            .route(&"/pass", get(route_get_pass))
1426            .route(&"/fail", get(route_get_fail));
1427
1428        let server = TestServer::new(router).unwrap();
1429
1430        let response = server.get(&"/pass").await;
1431
1432        response.assert_status_success()
1433    }
1434
1435    #[tokio::test]
1436    #[should_panic]
1437    async fn it_should_panic_when_not_200() {
1438        let router = Router::new()
1439            .route(&"/pass", get(route_get_pass))
1440            .route(&"/fail", get(route_get_fail));
1441
1442        let server = TestServer::new(router).unwrap();
1443
1444        let response = server.get(&"/fail").expect_failure().await;
1445
1446        response.assert_status_success()
1447    }
1448}
1449
1450#[cfg(test)]
1451mod test_assert_failure {
1452    use crate::TestServer;
1453    use axum::Router;
1454    use axum::routing::get;
1455    use http::StatusCode;
1456
1457    pub async fn route_get_pass() -> StatusCode {
1458        StatusCode::OK
1459    }
1460
1461    pub async fn route_get_fail() -> StatusCode {
1462        StatusCode::SERVICE_UNAVAILABLE
1463    }
1464
1465    #[tokio::test]
1466    async fn it_should_pass_when_not_200() {
1467        let router = Router::new()
1468            .route(&"/pass", get(route_get_pass))
1469            .route(&"/fail", get(route_get_fail));
1470
1471        let server = TestServer::new(router).unwrap();
1472        let response = server.get(&"/fail").expect_failure().await;
1473
1474        response.assert_status_failure()
1475    }
1476
1477    #[tokio::test]
1478    #[should_panic]
1479    async fn it_should_panic_when_200() {
1480        let router = Router::new()
1481            .route(&"/pass", get(route_get_pass))
1482            .route(&"/fail", get(route_get_fail));
1483
1484        let server = TestServer::new(router).unwrap();
1485        let response = server.get(&"/pass").await;
1486
1487        response.assert_status_failure()
1488    }
1489}
1490
1491#[cfg(test)]
1492mod test_assert_status {
1493    use crate::TestServer;
1494    use axum::Router;
1495    use axum::routing::get;
1496    use http::StatusCode;
1497
1498    pub async fn route_get_ok() -> StatusCode {
1499        StatusCode::OK
1500    }
1501
1502    #[tokio::test]
1503    async fn it_should_pass_if_given_right_status_code() {
1504        let router = Router::new().route(&"/ok", get(route_get_ok));
1505        let server = TestServer::new(router).unwrap();
1506
1507        server.get(&"/ok").await.assert_status(StatusCode::OK);
1508    }
1509
1510    #[tokio::test]
1511    #[should_panic]
1512    async fn it_should_panic_when_status_code_does_not_match() {
1513        let router = Router::new().route(&"/ok", get(route_get_ok));
1514        let server = TestServer::new(router).unwrap();
1515
1516        server.get(&"/ok").await.assert_status(StatusCode::ACCEPTED);
1517    }
1518}
1519
1520#[cfg(test)]
1521mod test_assert_not_status {
1522    use crate::TestServer;
1523    use axum::Router;
1524    use axum::routing::get;
1525    use http::StatusCode;
1526
1527    pub async fn route_get_ok() -> StatusCode {
1528        StatusCode::OK
1529    }
1530
1531    #[tokio::test]
1532    async fn it_should_pass_if_status_code_does_not_match() {
1533        let router = Router::new().route(&"/ok", get(route_get_ok));
1534        let server = TestServer::new(router).unwrap();
1535
1536        server
1537            .get(&"/ok")
1538            .await
1539            .assert_not_status(StatusCode::ACCEPTED);
1540    }
1541
1542    #[tokio::test]
1543    #[should_panic]
1544    async fn it_should_panic_if_status_code_matches() {
1545        let router = Router::new().route(&"/ok", get(route_get_ok));
1546        let server = TestServer::new(router).unwrap();
1547
1548        server.get(&"/ok").await.assert_not_status(StatusCode::OK);
1549    }
1550}
1551
1552#[cfg(test)]
1553mod test_assert_status_in_range {
1554    use crate::TestServer;
1555    use axum::routing::Router;
1556    use axum::routing::get;
1557    use http::StatusCode;
1558    use std::ops::RangeFull;
1559
1560    #[tokio::test]
1561    async fn it_should_be_true_when_within_int_range() {
1562        let app = Router::new().route(
1563            &"/status",
1564            get(|| async { StatusCode::NON_AUTHORITATIVE_INFORMATION }),
1565        );
1566
1567        TestServer::new(app)
1568            .unwrap()
1569            .get(&"/status")
1570            .await
1571            .assert_status_in_range(200..299);
1572    }
1573
1574    #[tokio::test]
1575    async fn it_should_be_true_when_within_status_code_range() {
1576        let app = Router::new().route(
1577            &"/status",
1578            get(|| async { StatusCode::NON_AUTHORITATIVE_INFORMATION }),
1579        );
1580
1581        TestServer::new(app)
1582            .unwrap()
1583            .get(&"/status")
1584            .await
1585            .assert_status_in_range(StatusCode::OK..StatusCode::IM_USED);
1586    }
1587
1588    #[tokio::test]
1589    #[should_panic]
1590    async fn it_should_be_false_when_outside_int_range() {
1591        let app = Router::new().route(
1592            &"/status",
1593            get(|| async { StatusCode::INTERNAL_SERVER_ERROR }),
1594        );
1595
1596        TestServer::new(app)
1597            .unwrap()
1598            .get(&"/status")
1599            .await
1600            .assert_status_in_range(200..299);
1601    }
1602
1603    #[tokio::test]
1604    #[should_panic]
1605    async fn it_should_be_false_when_outside_status_code_range() {
1606        let app = Router::new().route(
1607            &"/status",
1608            get(|| async { StatusCode::INTERNAL_SERVER_ERROR }),
1609        );
1610
1611        TestServer::new(app)
1612            .unwrap()
1613            .get(&"/status")
1614            .await
1615            .assert_status_in_range(StatusCode::OK..StatusCode::IM_USED);
1616    }
1617
1618    #[tokio::test]
1619    async fn it_should_be_true_when_within_inclusive_range() {
1620        let app = Router::new().route(
1621            &"/status",
1622            get(|| async { StatusCode::NON_AUTHORITATIVE_INFORMATION }),
1623        );
1624
1625        TestServer::new(app)
1626            .unwrap()
1627            .get(&"/status")
1628            .await
1629            .assert_status_in_range(200..=299);
1630    }
1631
1632    #[tokio::test]
1633    #[should_panic]
1634    async fn it_should_be_false_when_outside_inclusive_range() {
1635        let app = Router::new().route(
1636            &"/status",
1637            get(|| async { StatusCode::INTERNAL_SERVER_ERROR }),
1638        );
1639
1640        TestServer::new(app)
1641            .unwrap()
1642            .get(&"/status")
1643            .await
1644            .assert_status_in_range(200..=299);
1645    }
1646
1647    #[tokio::test]
1648    async fn it_should_be_true_when_within_to_range() {
1649        let app = Router::new().route(
1650            &"/status",
1651            get(|| async { StatusCode::NON_AUTHORITATIVE_INFORMATION }),
1652        );
1653
1654        TestServer::new(app)
1655            .unwrap()
1656            .get(&"/status")
1657            .await
1658            .assert_status_in_range(..299);
1659    }
1660
1661    #[tokio::test]
1662    #[should_panic]
1663    async fn it_should_be_false_when_outside_to_range() {
1664        let app = Router::new().route(
1665            &"/status",
1666            get(|| async { StatusCode::INTERNAL_SERVER_ERROR }),
1667        );
1668
1669        TestServer::new(app)
1670            .unwrap()
1671            .get(&"/status")
1672            .await
1673            .assert_status_in_range(..299);
1674    }
1675
1676    #[tokio::test]
1677    async fn it_should_be_true_when_within_to_inclusive_range() {
1678        let app = Router::new().route(
1679            &"/status",
1680            get(|| async { StatusCode::NON_AUTHORITATIVE_INFORMATION }),
1681        );
1682
1683        TestServer::new(app)
1684            .unwrap()
1685            .get(&"/status")
1686            .await
1687            .assert_status_in_range(..=299);
1688    }
1689
1690    #[tokio::test]
1691    #[should_panic]
1692    async fn it_should_be_false_when_outside_to_inclusive_range() {
1693        let app = Router::new().route(
1694            &"/status",
1695            get(|| async { StatusCode::INTERNAL_SERVER_ERROR }),
1696        );
1697
1698        TestServer::new(app)
1699            .unwrap()
1700            .get(&"/status")
1701            .await
1702            .assert_status_in_range(..=299);
1703    }
1704
1705    #[tokio::test]
1706    async fn it_should_be_true_when_within_from_range() {
1707        let app = Router::new().route(
1708            &"/status",
1709            get(|| async { StatusCode::NON_AUTHORITATIVE_INFORMATION }),
1710        );
1711
1712        TestServer::new(app)
1713            .unwrap()
1714            .get(&"/status")
1715            .await
1716            .assert_status_in_range(200..);
1717    }
1718
1719    #[tokio::test]
1720    #[should_panic]
1721    async fn it_should_be_false_when_outside_from_range() {
1722        let app = Router::new().route(
1723            &"/status",
1724            get(|| async { StatusCode::NON_AUTHORITATIVE_INFORMATION }),
1725        );
1726
1727        TestServer::new(app)
1728            .unwrap()
1729            .get(&"/status")
1730            .await
1731            .assert_status_in_range(500..);
1732    }
1733
1734    #[tokio::test]
1735    async fn it_should_be_true_for_rull_range() {
1736        let app = Router::new().route(
1737            &"/status",
1738            get(|| async { StatusCode::NON_AUTHORITATIVE_INFORMATION }),
1739        );
1740
1741        TestServer::new(app)
1742            .unwrap()
1743            .get(&"/status")
1744            .await
1745            .assert_status_in_range::<RangeFull, StatusCode>(..);
1746    }
1747}
1748
1749#[cfg(test)]
1750mod test_assert_status_not_in_range {
1751    use crate::TestServer;
1752    use axum::routing::Router;
1753    use axum::routing::get;
1754    use http::StatusCode;
1755    use std::ops::RangeFull;
1756
1757    #[tokio::test]
1758    #[should_panic]
1759    async fn it_should_be_false_when_within_int_range() {
1760        let app = Router::new().route(
1761            &"/status",
1762            get(|| async { StatusCode::NON_AUTHORITATIVE_INFORMATION }),
1763        );
1764
1765        TestServer::new(app)
1766            .unwrap()
1767            .get(&"/status")
1768            .await
1769            .assert_status_not_in_range(200..299);
1770    }
1771
1772    #[tokio::test]
1773    #[should_panic]
1774    async fn it_should_be_false_when_within_status_code_range() {
1775        let app = Router::new().route(
1776            &"/status",
1777            get(|| async { StatusCode::NON_AUTHORITATIVE_INFORMATION }),
1778        );
1779
1780        TestServer::new(app)
1781            .unwrap()
1782            .get(&"/status")
1783            .await
1784            .assert_status_not_in_range(StatusCode::OK..StatusCode::IM_USED);
1785    }
1786
1787    #[tokio::test]
1788    async fn it_should_be_true_when_outside_int_range() {
1789        let app = Router::new().route(
1790            &"/status",
1791            get(|| async { StatusCode::INTERNAL_SERVER_ERROR }),
1792        );
1793
1794        TestServer::new(app)
1795            .unwrap()
1796            .get(&"/status")
1797            .await
1798            .assert_status_not_in_range(200..299);
1799    }
1800
1801    #[tokio::test]
1802    async fn it_should_be_true_when_outside_status_code_range() {
1803        let app = Router::new().route(
1804            &"/status",
1805            get(|| async { StatusCode::INTERNAL_SERVER_ERROR }),
1806        );
1807
1808        TestServer::new(app)
1809            .unwrap()
1810            .get(&"/status")
1811            .await
1812            .assert_status_not_in_range(StatusCode::OK..StatusCode::IM_USED);
1813    }
1814
1815    #[tokio::test]
1816    #[should_panic]
1817    async fn it_should_be_false_when_within_inclusive_range() {
1818        let app = Router::new().route(
1819            &"/status",
1820            get(|| async { StatusCode::NON_AUTHORITATIVE_INFORMATION }),
1821        );
1822
1823        TestServer::new(app)
1824            .unwrap()
1825            .get(&"/status")
1826            .await
1827            .assert_status_not_in_range(200..=299);
1828    }
1829
1830    #[tokio::test]
1831    async fn it_should_be_true_when_outside_inclusive_range() {
1832        let app = Router::new().route(
1833            &"/status",
1834            get(|| async { StatusCode::INTERNAL_SERVER_ERROR }),
1835        );
1836
1837        TestServer::new(app)
1838            .unwrap()
1839            .get(&"/status")
1840            .await
1841            .assert_status_not_in_range(200..=299);
1842    }
1843
1844    #[tokio::test]
1845    #[should_panic]
1846    async fn it_should_be_false_when_within_to_range() {
1847        let app = Router::new().route(
1848            &"/status",
1849            get(|| async { StatusCode::NON_AUTHORITATIVE_INFORMATION }),
1850        );
1851
1852        TestServer::new(app)
1853            .unwrap()
1854            .get(&"/status")
1855            .await
1856            .assert_status_not_in_range(..299);
1857    }
1858
1859    #[tokio::test]
1860    async fn it_should_be_true_when_outside_to_range() {
1861        let app = Router::new().route(
1862            &"/status",
1863            get(|| async { StatusCode::INTERNAL_SERVER_ERROR }),
1864        );
1865
1866        TestServer::new(app)
1867            .unwrap()
1868            .get(&"/status")
1869            .await
1870            .assert_status_not_in_range(..299);
1871    }
1872
1873    #[tokio::test]
1874    #[should_panic]
1875    async fn it_should_be_false_when_within_to_inclusive_range() {
1876        let app = Router::new().route(
1877            &"/status",
1878            get(|| async { StatusCode::NON_AUTHORITATIVE_INFORMATION }),
1879        );
1880
1881        TestServer::new(app)
1882            .unwrap()
1883            .get(&"/status")
1884            .await
1885            .assert_status_not_in_range(..=299);
1886    }
1887
1888    #[tokio::test]
1889    async fn it_should_be_true_when_outside_to_inclusive_range() {
1890        let app = Router::new().route(
1891            &"/status",
1892            get(|| async { StatusCode::INTERNAL_SERVER_ERROR }),
1893        );
1894
1895        TestServer::new(app)
1896            .unwrap()
1897            .get(&"/status")
1898            .await
1899            .assert_status_not_in_range(..=299);
1900    }
1901
1902    #[tokio::test]
1903    #[should_panic]
1904    async fn it_should_be_false_when_within_from_range() {
1905        let app = Router::new().route(
1906            &"/status",
1907            get(|| async { StatusCode::NON_AUTHORITATIVE_INFORMATION }),
1908        );
1909
1910        TestServer::new(app)
1911            .unwrap()
1912            .get(&"/status")
1913            .await
1914            .assert_status_not_in_range(200..);
1915    }
1916
1917    #[tokio::test]
1918    async fn it_should_be_true_when_outside_from_range() {
1919        let app = Router::new().route(
1920            &"/status",
1921            get(|| async { StatusCode::NON_AUTHORITATIVE_INFORMATION }),
1922        );
1923
1924        TestServer::new(app)
1925            .unwrap()
1926            .get(&"/status")
1927            .await
1928            .assert_status_not_in_range(500..);
1929    }
1930
1931    #[tokio::test]
1932    #[should_panic]
1933    async fn it_should_be_false_for_rull_range() {
1934        let app = Router::new().route(
1935            &"/status",
1936            get(|| async { StatusCode::NON_AUTHORITATIVE_INFORMATION }),
1937        );
1938
1939        TestServer::new(app)
1940            .unwrap()
1941            .get(&"/status")
1942            .await
1943            .assert_status_not_in_range::<RangeFull, StatusCode>(..);
1944    }
1945}
1946
1947#[cfg(test)]
1948mod test_into_bytes {
1949    use crate::TestServer;
1950    use axum::Json;
1951    use axum::Router;
1952    use axum::routing::get;
1953    use serde_json::Value;
1954    use serde_json::json;
1955
1956    async fn route_get_json() -> Json<Value> {
1957        Json(json!({
1958            "message": "it works?"
1959        }))
1960    }
1961
1962    #[tokio::test]
1963    async fn it_should_deserialize_into_json() {
1964        let app = Router::new().route(&"/json", get(route_get_json));
1965
1966        let server = TestServer::new(app).unwrap();
1967
1968        let bytes = server.get(&"/json").await.into_bytes();
1969        let text = String::from_utf8_lossy(&bytes);
1970
1971        assert_eq!(text, r#"{"message":"it works?"}"#);
1972    }
1973}
1974
1975#[cfg(test)]
1976mod test_content_type {
1977    use crate::TestServer;
1978    use axum::Json;
1979    use axum::Router;
1980    use axum::routing::get;
1981    use serde::Deserialize;
1982    use serde::Serialize;
1983
1984    #[derive(Serialize, Deserialize, PartialEq, Debug)]
1985    struct ExampleResponse {
1986        name: String,
1987        age: u32,
1988    }
1989
1990    #[tokio::test]
1991    async fn it_should_retrieve_json_content_type_for_json() {
1992        let app = Router::new().route(
1993            &"/json",
1994            get(|| async {
1995                Json(ExampleResponse {
1996                    name: "Joe".to_string(),
1997                    age: 20,
1998                })
1999            }),
2000        );
2001
2002        let server = TestServer::new(app).unwrap();
2003
2004        let content_type = server.get(&"/json").await.content_type();
2005        assert_eq!(content_type, "application/json");
2006    }
2007
2008    #[cfg(feature = "yaml")]
2009    #[tokio::test]
2010    async fn it_should_retrieve_yaml_content_type_for_yaml() {
2011        use axum_yaml::Yaml;
2012
2013        let app = Router::new().route(
2014            &"/yaml",
2015            get(|| async {
2016                Yaml(ExampleResponse {
2017                    name: "Joe".to_string(),
2018                    age: 20,
2019                })
2020            }),
2021        );
2022
2023        let server = TestServer::new(app).unwrap();
2024
2025        let content_type = server.get(&"/yaml").await.content_type();
2026        assert_eq!(content_type, "application/yaml");
2027    }
2028}
2029
2030#[cfg(test)]
2031mod test_json {
2032    use crate::TestServer;
2033    use axum::Json;
2034    use axum::Router;
2035    use axum::routing::get;
2036    use serde::Deserialize;
2037    use serde::Serialize;
2038
2039    #[derive(Serialize, Deserialize, PartialEq, Debug)]
2040    struct ExampleResponse {
2041        name: String,
2042        age: u32,
2043    }
2044
2045    async fn route_get_json() -> Json<ExampleResponse> {
2046        Json(ExampleResponse {
2047            name: "Joe".to_string(),
2048            age: 20,
2049        })
2050    }
2051
2052    #[tokio::test]
2053    async fn it_should_deserialize_into_json() {
2054        let app = Router::new().route(&"/json", get(route_get_json));
2055
2056        let server = TestServer::new(app).unwrap();
2057
2058        let response = server.get(&"/json").await.json::<ExampleResponse>();
2059
2060        assert_eq!(
2061            response,
2062            ExampleResponse {
2063                name: "Joe".to_string(),
2064                age: 20,
2065            }
2066        );
2067    }
2068}
2069
2070#[cfg(feature = "yaml")]
2071#[cfg(test)]
2072mod test_yaml {
2073    use crate::TestServer;
2074    use axum::Router;
2075    use axum::routing::get;
2076    use axum_yaml::Yaml;
2077    use serde::Deserialize;
2078    use serde::Serialize;
2079
2080    #[derive(Serialize, Deserialize, PartialEq, Debug)]
2081    struct ExampleResponse {
2082        name: String,
2083        age: u32,
2084    }
2085
2086    async fn route_get_yaml() -> Yaml<ExampleResponse> {
2087        Yaml(ExampleResponse {
2088            name: "Joe".to_string(),
2089            age: 20,
2090        })
2091    }
2092
2093    #[tokio::test]
2094    async fn it_should_deserialize_into_yaml() {
2095        let app = Router::new().route(&"/yaml", get(route_get_yaml));
2096
2097        let server = TestServer::new(app).unwrap();
2098
2099        let response = server.get(&"/yaml").await.yaml::<ExampleResponse>();
2100
2101        assert_eq!(
2102            response,
2103            ExampleResponse {
2104                name: "Joe".to_string(),
2105                age: 20,
2106            }
2107        );
2108    }
2109}
2110
2111#[cfg(feature = "msgpack")]
2112#[cfg(test)]
2113mod test_msgpack {
2114    use crate::TestServer;
2115    use axum::Router;
2116    use axum::routing::get;
2117    use axum_msgpack::MsgPack;
2118    use serde::Deserialize;
2119    use serde::Serialize;
2120
2121    #[derive(Serialize, Deserialize, PartialEq, Debug)]
2122    struct ExampleResponse {
2123        name: String,
2124        age: u32,
2125    }
2126
2127    async fn route_get_msgpack() -> MsgPack<ExampleResponse> {
2128        MsgPack(ExampleResponse {
2129            name: "Joe".to_string(),
2130            age: 20,
2131        })
2132    }
2133
2134    #[tokio::test]
2135    async fn it_should_deserialize_into_msgpack() {
2136        let app = Router::new().route(&"/msgpack", get(route_get_msgpack));
2137
2138        let server = TestServer::new(app).unwrap();
2139
2140        let response = server.get(&"/msgpack").await.msgpack::<ExampleResponse>();
2141
2142        assert_eq!(
2143            response,
2144            ExampleResponse {
2145                name: "Joe".to_string(),
2146                age: 20,
2147            }
2148        );
2149    }
2150}
2151
2152#[cfg(test)]
2153mod test_form {
2154    use crate::TestServer;
2155    use axum::Form;
2156    use axum::Router;
2157    use axum::routing::get;
2158    use serde::Deserialize;
2159    use serde::Serialize;
2160
2161    #[derive(Serialize, Deserialize, PartialEq, Debug)]
2162    struct ExampleResponse {
2163        name: String,
2164        age: u32,
2165    }
2166
2167    async fn route_get_form() -> Form<ExampleResponse> {
2168        Form(ExampleResponse {
2169            name: "Joe".to_string(),
2170            age: 20,
2171        })
2172    }
2173
2174    #[tokio::test]
2175    async fn it_should_deserialize_into_form() {
2176        let app = Router::new().route(&"/form", get(route_get_form));
2177
2178        let server = TestServer::new(app).unwrap();
2179
2180        let response = server.get(&"/form").await.form::<ExampleResponse>();
2181
2182        assert_eq!(
2183            response,
2184            ExampleResponse {
2185                name: "Joe".to_string(),
2186                age: 20,
2187            }
2188        );
2189    }
2190}
2191
2192#[cfg(test)]
2193mod test_from {
2194    use crate::TestServer;
2195    use axum::Router;
2196    use axum::routing::get;
2197    use bytes::Bytes;
2198
2199    #[tokio::test]
2200    async fn it_should_turn_into_response_bytes() {
2201        let app = Router::new().route(&"/text", get(|| async { "This is some example text" }));
2202        let server = TestServer::new(app).unwrap();
2203
2204        let response = server.get(&"/text").await;
2205        let bytes: Bytes = response.into();
2206        let text = String::from_utf8_lossy(&bytes);
2207        assert_eq!(text, "This is some example text");
2208    }
2209}
2210
2211#[cfg(test)]
2212mod test_assert_text {
2213    use crate::TestServer;
2214    use axum::Router;
2215    use axum::routing::get;
2216
2217    fn new_test_server() -> TestServer {
2218        async fn route_get_text() -> &'static str {
2219            "This is some example text"
2220        }
2221
2222        let app = Router::new().route(&"/text", get(route_get_text));
2223        TestServer::new(app).unwrap()
2224    }
2225
2226    #[tokio::test]
2227    async fn it_should_match_whole_text() {
2228        let server = new_test_server();
2229
2230        server
2231            .get(&"/text")
2232            .await
2233            .assert_text("This is some example text");
2234    }
2235
2236    #[tokio::test]
2237    #[should_panic]
2238    async fn it_should_not_match_partial_text() {
2239        let server = new_test_server();
2240
2241        server.get(&"/text").await.assert_text("some example");
2242    }
2243
2244    #[tokio::test]
2245    #[should_panic]
2246    async fn it_should_not_match_different_text() {
2247        let server = new_test_server();
2248
2249        server.get(&"/text").await.assert_text("🦊");
2250    }
2251}
2252
2253#[cfg(test)]
2254mod test_assert_text_contains {
2255    use crate::TestServer;
2256    use axum::Router;
2257    use axum::routing::get;
2258
2259    fn new_test_server() -> TestServer {
2260        async fn route_get_text() -> &'static str {
2261            "This is some example text"
2262        }
2263
2264        let app = Router::new().route(&"/text", get(route_get_text));
2265        TestServer::new(app).unwrap()
2266    }
2267
2268    #[tokio::test]
2269    async fn it_should_match_whole_text() {
2270        let server = new_test_server();
2271
2272        server
2273            .get(&"/text")
2274            .await
2275            .assert_text_contains("This is some example text");
2276    }
2277
2278    #[tokio::test]
2279    async fn it_should_match_partial_text() {
2280        let server = new_test_server();
2281
2282        server
2283            .get(&"/text")
2284            .await
2285            .assert_text_contains("some example");
2286    }
2287
2288    #[tokio::test]
2289    #[should_panic]
2290    async fn it_should_not_match_different_text() {
2291        let server = new_test_server();
2292
2293        server.get(&"/text").await.assert_text_contains("🦊");
2294    }
2295}
2296
2297#[cfg(test)]
2298mod test_assert_text_from_file {
2299    use crate::TestServer;
2300    use axum::routing::Router;
2301    use axum::routing::get;
2302
2303    #[tokio::test]
2304    async fn it_should_match_from_file() {
2305        let app = Router::new().route(&"/text", get(|| async { "hello!" }));
2306        let server = TestServer::new(app).unwrap();
2307
2308        server
2309            .get(&"/text")
2310            .await
2311            .assert_text_from_file("files/example.txt");
2312    }
2313
2314    #[tokio::test]
2315    #[should_panic]
2316    async fn it_should_panic_when_not_match_the_file() {
2317        let app = Router::new().route(&"/text", get(|| async { "🦊" }));
2318        let server = TestServer::new(app).unwrap();
2319
2320        server
2321            .get(&"/text")
2322            .await
2323            .assert_text_from_file("files/example.txt");
2324    }
2325}
2326
2327#[cfg(test)]
2328mod test_assert_json {
2329    use super::*;
2330    use crate::TestServer;
2331    use axum::Form;
2332    use axum::Json;
2333    use axum::Router;
2334    use axum::routing::get;
2335    use serde::Deserialize;
2336    use serde_json::json;
2337
2338    #[cfg(not(feature = "old-json-diff"))]
2339    use crate::testing::ExpectStrMinLen;
2340
2341    #[derive(Serialize, Deserialize, PartialEq, Debug)]
2342    struct ExampleResponse {
2343        name: String,
2344        age: u32,
2345    }
2346
2347    async fn route_get_form() -> Form<ExampleResponse> {
2348        Form(ExampleResponse {
2349            name: "Joe".to_string(),
2350            age: 20,
2351        })
2352    }
2353
2354    async fn route_get_json() -> Json<ExampleResponse> {
2355        Json(ExampleResponse {
2356            name: "Joe".to_string(),
2357            age: 20,
2358        })
2359    }
2360
2361    #[tokio::test]
2362    async fn it_should_match_json_returned() {
2363        let app = Router::new().route(&"/json", get(route_get_json));
2364
2365        let server = TestServer::new(app).unwrap();
2366
2367        server.get(&"/json").await.assert_json(&ExampleResponse {
2368            name: "Joe".to_string(),
2369            age: 20,
2370        });
2371    }
2372
2373    #[tokio::test]
2374    async fn it_should_match_json_returned_using_json_value() {
2375        let app = Router::new().route(&"/json", get(route_get_json));
2376
2377        let server = TestServer::new(app).unwrap();
2378
2379        server.get(&"/json").await.assert_json(&json!({
2380            "name": "Joe",
2381            "age": 20,
2382        }));
2383    }
2384
2385    #[tokio::test]
2386    #[should_panic]
2387    async fn it_should_panic_if_response_is_different() {
2388        let app = Router::new().route(&"/json", get(route_get_json));
2389
2390        let server = TestServer::new(app).unwrap();
2391
2392        server.get(&"/json").await.assert_json(&ExampleResponse {
2393            name: "Julia".to_string(),
2394            age: 25,
2395        });
2396    }
2397
2398    #[tokio::test]
2399    #[should_panic]
2400    async fn it_should_panic_if_response_is_form() {
2401        let app = Router::new().route(&"/form", get(route_get_form));
2402
2403        let server = TestServer::new(app).unwrap();
2404
2405        server.get(&"/form").await.assert_json(&ExampleResponse {
2406            name: "Joe".to_string(),
2407            age: 20,
2408        });
2409    }
2410
2411    #[cfg(not(feature = "old-json-diff"))]
2412    #[tokio::test]
2413    async fn it_should_work_with_custom_expect_op() {
2414        let app = Router::new().route(&"/json", get(route_get_json));
2415        let server = TestServer::new(app).unwrap();
2416
2417        server.get(&"/json").await.assert_json(&json!({
2418            "name": ExpectStrMinLen { min: 3 },
2419            "age": 20,
2420        }));
2421    }
2422
2423    #[cfg(not(feature = "old-json-diff"))]
2424    #[tokio::test]
2425    #[should_panic]
2426    async fn it_should_panic_if_custom_expect_op_fails() {
2427        let app = Router::new().route(&"/json", get(route_get_json));
2428        let server = TestServer::new(app).unwrap();
2429
2430        server.get(&"/json").await.assert_json(&json!({
2431            "name": ExpectStrMinLen { min: 10 },
2432            "age": 20,
2433        }));
2434    }
2435}
2436
2437#[cfg(test)]
2438mod test_assert_json_contains {
2439    use crate::TestServer;
2440    use axum::Form;
2441    use axum::Json;
2442    use axum::Router;
2443    use axum::routing::get;
2444    use serde::Deserialize;
2445    use serde::Serialize;
2446    use serde_json::json;
2447    use std::time::Instant;
2448
2449    #[derive(Serialize, Deserialize, PartialEq, Debug)]
2450    struct ExampleResponse {
2451        time: u64,
2452        name: String,
2453        age: u32,
2454    }
2455
2456    async fn route_get_form() -> Form<ExampleResponse> {
2457        Form(ExampleResponse {
2458            time: Instant::now().elapsed().as_millis() as u64,
2459            name: "Joe".to_string(),
2460            age: 20,
2461        })
2462    }
2463
2464    async fn route_get_json() -> Json<ExampleResponse> {
2465        Json(ExampleResponse {
2466            time: Instant::now().elapsed().as_millis() as u64,
2467            name: "Joe".to_string(),
2468            age: 20,
2469        })
2470    }
2471
2472    #[tokio::test]
2473    async fn it_should_match_subset_of_json_returned() {
2474        let app = Router::new().route(&"/json", get(route_get_json));
2475        let server = TestServer::new(app).unwrap();
2476
2477        server.get(&"/json").await.assert_json_contains(&json!({
2478            "name": "Joe",
2479            "age": 20,
2480        }));
2481    }
2482
2483    #[tokio::test]
2484    #[should_panic]
2485    async fn it_should_panic_if_response_is_different() {
2486        let app = Router::new().route(&"/json", get(route_get_json));
2487        let server = TestServer::new(app).unwrap();
2488
2489        server
2490            .get(&"/json")
2491            .await
2492            .assert_json_contains(&ExampleResponse {
2493                time: 1234,
2494                name: "Julia".to_string(),
2495                age: 25,
2496            });
2497    }
2498
2499    #[tokio::test]
2500    #[should_panic]
2501    async fn it_should_panic_if_response_is_form() {
2502        let app = Router::new().route(&"/form", get(route_get_form));
2503        let server = TestServer::new(app).unwrap();
2504
2505        server.get(&"/form").await.assert_json_contains(&json!({
2506            "name": "Joe",
2507            "age": 20,
2508        }));
2509    }
2510
2511    /// See: https://github.com/JosephLenton/axum-test/issues/151
2512    #[tokio::test]
2513    async fn it_should_propagate_contains_to_sub_objects() {
2514        let json_result = json!({ "a": {"prop1": "value1"} }).to_string();
2515        let app = Router::new().route(&"/json", get(|| async { json_result }));
2516
2517        let server = TestServer::new(app).unwrap();
2518        let response = server.get("/json").await;
2519
2520        response.assert_json_contains(&json!({ "a": {} }));
2521    }
2522}
2523
2524#[cfg(test)]
2525mod test_assert_json_from_file {
2526    use crate::TestServer;
2527    use axum::Form;
2528    use axum::Json;
2529    use axum::routing::Router;
2530    use axum::routing::get;
2531    use serde::Deserialize;
2532    use serde::Serialize;
2533    use serde_json::json;
2534
2535    #[tokio::test]
2536    async fn it_should_match_json_from_file() {
2537        let app = Router::new().route(
2538            &"/json",
2539            get(|| async {
2540                Json(json!(
2541                    {
2542                        "name": "Joe",
2543                        "age": 20,
2544                    }
2545                ))
2546            }),
2547        );
2548        let server = TestServer::new(app).unwrap();
2549
2550        server
2551            .get(&"/json")
2552            .await
2553            .assert_json_from_file("files/example.json");
2554    }
2555
2556    #[tokio::test]
2557    #[should_panic]
2558    async fn it_should_panic_when_not_match_the_file() {
2559        let app = Router::new().route(
2560            &"/json",
2561            get(|| async {
2562                Json(json!(
2563                    {
2564                        "name": "Julia",
2565                        "age": 25,
2566                    }
2567                ))
2568            }),
2569        );
2570        let server = TestServer::new(app).unwrap();
2571
2572        server
2573            .get(&"/json")
2574            .await
2575            .assert_json_from_file("files/example.json");
2576    }
2577
2578    #[tokio::test]
2579    #[should_panic]
2580    async fn it_should_panic_when_content_type_does_not_match() {
2581        #[derive(Serialize, Deserialize, PartialEq, Debug)]
2582        struct ExampleResponse {
2583            name: String,
2584            age: u32,
2585        }
2586
2587        let app = Router::new().route(
2588            &"/form",
2589            get(|| async {
2590                Form(ExampleResponse {
2591                    name: "Joe".to_string(),
2592                    age: 20,
2593                })
2594            }),
2595        );
2596        let server = TestServer::new(app).unwrap();
2597
2598        server
2599            .get(&"/form")
2600            .await
2601            .assert_json_from_file("files/example.json");
2602    }
2603}
2604
2605#[cfg(feature = "yaml")]
2606#[cfg(test)]
2607mod test_assert_yaml {
2608    use crate::TestServer;
2609    use axum::Form;
2610    use axum::Router;
2611    use axum::routing::get;
2612    use axum_yaml::Yaml;
2613    use serde::Deserialize;
2614    use serde::Serialize;
2615
2616    #[derive(Serialize, Deserialize, PartialEq, Debug)]
2617    struct ExampleResponse {
2618        name: String,
2619        age: u32,
2620    }
2621
2622    async fn route_get_form() -> Form<ExampleResponse> {
2623        Form(ExampleResponse {
2624            name: "Joe".to_string(),
2625            age: 20,
2626        })
2627    }
2628
2629    async fn route_get_yaml() -> Yaml<ExampleResponse> {
2630        Yaml(ExampleResponse {
2631            name: "Joe".to_string(),
2632            age: 20,
2633        })
2634    }
2635
2636    #[tokio::test]
2637    async fn it_should_match_yaml_returned() {
2638        let app = Router::new().route(&"/yaml", get(route_get_yaml));
2639
2640        let server = TestServer::new(app).unwrap();
2641
2642        server.get(&"/yaml").await.assert_yaml(&ExampleResponse {
2643            name: "Joe".to_string(),
2644            age: 20,
2645        });
2646    }
2647
2648    #[tokio::test]
2649    #[should_panic]
2650    async fn it_should_panic_if_response_is_different() {
2651        let app = Router::new().route(&"/yaml", get(route_get_yaml));
2652
2653        let server = TestServer::new(app).unwrap();
2654
2655        server.get(&"/yaml").await.assert_yaml(&ExampleResponse {
2656            name: "Julia".to_string(),
2657            age: 25,
2658        });
2659    }
2660
2661    #[tokio::test]
2662    #[should_panic]
2663    async fn it_should_panic_if_response_is_form() {
2664        let app = Router::new().route(&"/form", get(route_get_form));
2665
2666        let server = TestServer::new(app).unwrap();
2667
2668        server.get(&"/form").await.assert_yaml(&ExampleResponse {
2669            name: "Joe".to_string(),
2670            age: 20,
2671        });
2672    }
2673}
2674
2675#[cfg(feature = "yaml")]
2676#[cfg(test)]
2677mod test_assert_yaml_from_file {
2678    use crate::TestServer;
2679    use axum::Form;
2680    use axum::routing::Router;
2681    use axum::routing::get;
2682    use axum_yaml::Yaml;
2683    use serde::Deserialize;
2684    use serde::Serialize;
2685    use serde_json::json;
2686
2687    #[tokio::test]
2688    async fn it_should_match_yaml_from_file() {
2689        let app = Router::new().route(
2690            &"/yaml",
2691            get(|| async {
2692                Yaml(json!(
2693                    {
2694                        "name": "Joe",
2695                        "age": 20,
2696                    }
2697                ))
2698            }),
2699        );
2700        let server = TestServer::new(app).unwrap();
2701
2702        server
2703            .get(&"/yaml")
2704            .await
2705            .assert_yaml_from_file("files/example.yaml");
2706    }
2707
2708    #[tokio::test]
2709    #[should_panic]
2710    async fn it_should_panic_when_not_match_the_file() {
2711        let app = Router::new().route(
2712            &"/yaml",
2713            get(|| async {
2714                Yaml(json!(
2715                    {
2716                        "name": "Julia",
2717                        "age": 25,
2718                    }
2719                ))
2720            }),
2721        );
2722        let server = TestServer::new(app).unwrap();
2723
2724        server
2725            .get(&"/yaml")
2726            .await
2727            .assert_yaml_from_file("files/example.yaml");
2728    }
2729
2730    #[tokio::test]
2731    #[should_panic]
2732    async fn it_should_panic_when_content_type_does_not_match() {
2733        #[derive(Serialize, Deserialize, PartialEq, Debug)]
2734        struct ExampleResponse {
2735            name: String,
2736            age: u32,
2737        }
2738
2739        let app = Router::new().route(
2740            &"/form",
2741            get(|| async {
2742                Form(ExampleResponse {
2743                    name: "Joe".to_string(),
2744                    age: 20,
2745                })
2746            }),
2747        );
2748        let server = TestServer::new(app).unwrap();
2749
2750        server
2751            .get(&"/form")
2752            .await
2753            .assert_yaml_from_file("files/example.yaml");
2754    }
2755}
2756
2757#[cfg(test)]
2758mod test_assert_form {
2759    use crate::TestServer;
2760    use axum::Form;
2761    use axum::Json;
2762    use axum::Router;
2763    use axum::routing::get;
2764    use serde::Deserialize;
2765    use serde::Serialize;
2766
2767    #[derive(Serialize, Deserialize, PartialEq, Debug)]
2768    struct ExampleResponse {
2769        name: String,
2770        age: u32,
2771    }
2772
2773    async fn route_get_form() -> Form<ExampleResponse> {
2774        Form(ExampleResponse {
2775            name: "Joe".to_string(),
2776            age: 20,
2777        })
2778    }
2779
2780    async fn route_get_json() -> Json<ExampleResponse> {
2781        Json(ExampleResponse {
2782            name: "Joe".to_string(),
2783            age: 20,
2784        })
2785    }
2786
2787    #[tokio::test]
2788    async fn it_should_match_form_returned() {
2789        let app = Router::new().route(&"/form", get(route_get_form));
2790
2791        let server = TestServer::new(app).unwrap();
2792
2793        server.get(&"/form").await.assert_form(&ExampleResponse {
2794            name: "Joe".to_string(),
2795            age: 20,
2796        });
2797    }
2798
2799    #[tokio::test]
2800    #[should_panic]
2801    async fn it_should_panic_if_response_is_different() {
2802        let app = Router::new().route(&"/form", get(route_get_form));
2803
2804        let server = TestServer::new(app).unwrap();
2805
2806        server.get(&"/form").await.assert_form(&ExampleResponse {
2807            name: "Julia".to_string(),
2808            age: 25,
2809        });
2810    }
2811
2812    #[tokio::test]
2813    #[should_panic]
2814    async fn it_should_panic_if_response_is_json() {
2815        let app = Router::new().route(&"/json", get(route_get_json));
2816
2817        let server = TestServer::new(app).unwrap();
2818
2819        server.get(&"/json").await.assert_form(&ExampleResponse {
2820            name: "Joe".to_string(),
2821            age: 20,
2822        });
2823    }
2824}
2825
2826#[cfg(test)]
2827mod test_text {
2828    use crate::TestServer;
2829    use axum::Router;
2830    use axum::routing::get;
2831
2832    #[tokio::test]
2833    async fn it_should_deserialize_into_text() {
2834        async fn route_get_text() -> String {
2835            "hello!".to_string()
2836        }
2837
2838        let app = Router::new().route(&"/text", get(route_get_text));
2839
2840        let server = TestServer::new(app).unwrap();
2841
2842        let response = server.get(&"/text").await.text();
2843
2844        assert_eq!(response, "hello!");
2845    }
2846}
2847
2848#[cfg(feature = "ws")]
2849#[cfg(test)]
2850mod test_into_websocket {
2851    use crate::TestServer;
2852
2853    use axum::Router;
2854    use axum::extract::WebSocketUpgrade;
2855    use axum::extract::ws::WebSocket;
2856    use axum::response::Response;
2857    use axum::routing::get;
2858
2859    fn new_test_router() -> Router {
2860        pub async fn route_get_websocket(ws: WebSocketUpgrade) -> Response {
2861            async fn handle_ping_pong(mut socket: WebSocket) {
2862                while let Some(_) = socket.recv().await {
2863                    // do nothing
2864                }
2865            }
2866
2867            ws.on_upgrade(move |socket| handle_ping_pong(socket))
2868        }
2869
2870        let app = Router::new().route(&"/ws", get(route_get_websocket));
2871
2872        app
2873    }
2874
2875    #[tokio::test]
2876    async fn it_should_upgrade_on_http_transport() {
2877        let router = new_test_router();
2878        let server = TestServer::builder()
2879            .http_transport()
2880            .build(router)
2881            .unwrap();
2882
2883        let _ = server.get_websocket(&"/ws").await.into_websocket().await;
2884
2885        assert!(true);
2886    }
2887
2888    #[tokio::test]
2889    #[should_panic]
2890    async fn it_should_fail_to_upgrade_on_mock_transport() {
2891        let router = new_test_router();
2892        let server = TestServer::builder()
2893            .mock_transport()
2894            .build(router)
2895            .unwrap();
2896
2897        let _ = server.get_websocket(&"/ws").await.into_websocket().await;
2898    }
2899}