Skip to main content

axum_test/
test_response.rs

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