axum_test/
test_response.rs

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