axum_test/
test_response.rs

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