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 303.
1215    #[track_caller]
1216    pub fn assert_status_see_other(&self) {
1217        self.assert_status(StatusCode::SEE_OTHER)
1218    }
1219
1220    /// Assert the response status code is 400.
1221    #[track_caller]
1222    pub fn assert_status_bad_request(&self) {
1223        self.assert_status(StatusCode::BAD_REQUEST)
1224    }
1225
1226    /// Assert the response status code is 404.
1227    #[track_caller]
1228    pub fn assert_status_not_found(&self) {
1229        self.assert_status(StatusCode::NOT_FOUND)
1230    }
1231
1232    /// Assert the response status code is 401.
1233    #[track_caller]
1234    pub fn assert_status_unauthorized(&self) {
1235        self.assert_status(StatusCode::UNAUTHORIZED)
1236    }
1237
1238    /// Assert the response status code is 403.
1239    #[track_caller]
1240    pub fn assert_status_forbidden(&self) {
1241        self.assert_status(StatusCode::FORBIDDEN)
1242    }
1243
1244    /// Assert the response status code is 409.
1245    pub fn assert_status_conflict(&self) {
1246        self.assert_status(StatusCode::CONFLICT)
1247    }
1248
1249    /// Assert the response status code is 413.
1250    ///
1251    /// The payload is too large.
1252    #[track_caller]
1253    pub fn assert_status_payload_too_large(&self) {
1254        self.assert_status(StatusCode::PAYLOAD_TOO_LARGE)
1255    }
1256
1257    /// Assert the response status code is 422.
1258    #[track_caller]
1259    pub fn assert_status_unprocessable_entity(&self) {
1260        self.assert_status(StatusCode::UNPROCESSABLE_ENTITY)
1261    }
1262
1263    /// Assert the response status code is 429.
1264    #[track_caller]
1265    pub fn assert_status_too_many_requests(&self) {
1266        self.assert_status(StatusCode::TOO_MANY_REQUESTS)
1267    }
1268
1269    /// Assert the response status code is 101.
1270    ///
1271    /// This type of code is used in Web Socket connection when
1272    /// first request.
1273    #[track_caller]
1274    pub fn assert_status_switching_protocols(&self) {
1275        self.assert_status(StatusCode::SWITCHING_PROTOCOLS)
1276    }
1277
1278    /// Assert the response status code is 500.
1279    #[track_caller]
1280    pub fn assert_status_internal_server_error(&self) {
1281        self.assert_status(StatusCode::INTERNAL_SERVER_ERROR)
1282    }
1283
1284    /// Assert the response status code is 503.
1285    #[track_caller]
1286    pub fn assert_status_service_unavailable(&self) {
1287        self.assert_status(StatusCode::SERVICE_UNAVAILABLE)
1288    }
1289
1290    fn debug_request_format(&self) -> RequestPathFormatter<'_> {
1291        RequestPathFormatter::new(&self.method, self.full_request_url.as_str(), None)
1292    }
1293}
1294
1295impl From<TestResponse> for Bytes {
1296    fn from(response: TestResponse) -> Self {
1297        response.into_bytes()
1298    }
1299}
1300
1301#[cfg(test)]
1302mod test_assert_header {
1303    use crate::TestServer;
1304    use axum::http::HeaderMap;
1305    use axum::routing::get;
1306    use axum::Router;
1307
1308    async fn route_get_header() -> HeaderMap {
1309        let mut headers = HeaderMap::new();
1310        headers.insert("x-my-custom-header", "content".parse().unwrap());
1311        headers
1312    }
1313
1314    #[tokio::test]
1315    async fn it_should_not_panic_if_contains_header_and_content_matches() {
1316        let router = Router::new().route(&"/header", get(route_get_header));
1317
1318        let server = TestServer::new(router).unwrap();
1319
1320        server
1321            .get(&"/header")
1322            .await
1323            .assert_header("x-my-custom-header", "content");
1324    }
1325
1326    #[tokio::test]
1327    #[should_panic]
1328    async fn it_should_panic_if_contains_header_and_content_does_not_match() {
1329        let router = Router::new().route(&"/header", get(route_get_header));
1330
1331        let server = TestServer::new(router).unwrap();
1332
1333        server
1334            .get(&"/header")
1335            .await
1336            .assert_header("x-my-custom-header", "different-content");
1337    }
1338
1339    #[tokio::test]
1340    #[should_panic]
1341    async fn it_should_panic_if_not_contains_header() {
1342        let router = Router::new().route(&"/header", get(route_get_header));
1343
1344        let server = TestServer::new(router).unwrap();
1345
1346        server
1347            .get(&"/header")
1348            .await
1349            .assert_header("x-custom-header-not-found", "content");
1350    }
1351}
1352
1353#[cfg(test)]
1354mod test_assert_contains_header {
1355    use crate::TestServer;
1356    use axum::http::HeaderMap;
1357    use axum::routing::get;
1358    use axum::Router;
1359
1360    async fn route_get_header() -> HeaderMap {
1361        let mut headers = HeaderMap::new();
1362        headers.insert("x-my-custom-header", "content".parse().unwrap());
1363        headers
1364    }
1365
1366    #[tokio::test]
1367    async fn it_should_not_panic_if_contains_header() {
1368        let router = Router::new().route(&"/header", get(route_get_header));
1369
1370        let server = TestServer::new(router).unwrap();
1371
1372        server
1373            .get(&"/header")
1374            .await
1375            .assert_contains_header("x-my-custom-header");
1376    }
1377
1378    #[tokio::test]
1379    #[should_panic]
1380    async fn it_should_panic_if_not_contains_header() {
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_contains_header("x-custom-header-not-found");
1389    }
1390}
1391
1392#[cfg(test)]
1393mod test_assert_success {
1394    use crate::TestServer;
1395    use axum::routing::get;
1396    use axum::Router;
1397    use http::StatusCode;
1398
1399    pub async fn route_get_pass() -> StatusCode {
1400        StatusCode::OK
1401    }
1402
1403    pub async fn route_get_fail() -> StatusCode {
1404        StatusCode::SERVICE_UNAVAILABLE
1405    }
1406
1407    #[tokio::test]
1408    async fn it_should_pass_when_200() {
1409        let router = Router::new()
1410            .route(&"/pass", get(route_get_pass))
1411            .route(&"/fail", get(route_get_fail));
1412
1413        let server = TestServer::new(router).unwrap();
1414
1415        let response = server.get(&"/pass").await;
1416
1417        response.assert_status_success()
1418    }
1419
1420    #[tokio::test]
1421    #[should_panic]
1422    async fn it_should_panic_when_not_200() {
1423        let router = Router::new()
1424            .route(&"/pass", get(route_get_pass))
1425            .route(&"/fail", get(route_get_fail));
1426
1427        let server = TestServer::new(router).unwrap();
1428
1429        let response = server.get(&"/fail").expect_failure().await;
1430
1431        response.assert_status_success()
1432    }
1433}
1434
1435#[cfg(test)]
1436mod test_assert_failure {
1437    use crate::TestServer;
1438    use axum::routing::get;
1439    use axum::Router;
1440    use http::StatusCode;
1441
1442    pub async fn route_get_pass() -> StatusCode {
1443        StatusCode::OK
1444    }
1445
1446    pub async fn route_get_fail() -> StatusCode {
1447        StatusCode::SERVICE_UNAVAILABLE
1448    }
1449
1450    #[tokio::test]
1451    async fn it_should_pass_when_not_200() {
1452        let router = Router::new()
1453            .route(&"/pass", get(route_get_pass))
1454            .route(&"/fail", get(route_get_fail));
1455
1456        let server = TestServer::new(router).unwrap();
1457        let response = server.get(&"/fail").expect_failure().await;
1458
1459        response.assert_status_failure()
1460    }
1461
1462    #[tokio::test]
1463    #[should_panic]
1464    async fn it_should_panic_when_200() {
1465        let router = Router::new()
1466            .route(&"/pass", get(route_get_pass))
1467            .route(&"/fail", get(route_get_fail));
1468
1469        let server = TestServer::new(router).unwrap();
1470        let response = server.get(&"/pass").await;
1471
1472        response.assert_status_failure()
1473    }
1474}
1475
1476#[cfg(test)]
1477mod test_assert_status {
1478    use crate::TestServer;
1479    use axum::routing::get;
1480    use axum::Router;
1481    use http::StatusCode;
1482
1483    pub async fn route_get_ok() -> StatusCode {
1484        StatusCode::OK
1485    }
1486
1487    #[tokio::test]
1488    async fn it_should_pass_if_given_right_status_code() {
1489        let router = Router::new().route(&"/ok", get(route_get_ok));
1490        let server = TestServer::new(router).unwrap();
1491
1492        server.get(&"/ok").await.assert_status(StatusCode::OK);
1493    }
1494
1495    #[tokio::test]
1496    #[should_panic]
1497    async fn it_should_panic_when_status_code_does_not_match() {
1498        let router = Router::new().route(&"/ok", get(route_get_ok));
1499        let server = TestServer::new(router).unwrap();
1500
1501        server.get(&"/ok").await.assert_status(StatusCode::ACCEPTED);
1502    }
1503}
1504
1505#[cfg(test)]
1506mod test_assert_not_status {
1507    use crate::TestServer;
1508    use axum::routing::get;
1509    use axum::Router;
1510    use http::StatusCode;
1511
1512    pub async fn route_get_ok() -> StatusCode {
1513        StatusCode::OK
1514    }
1515
1516    #[tokio::test]
1517    async fn it_should_pass_if_status_code_does_not_match() {
1518        let router = Router::new().route(&"/ok", get(route_get_ok));
1519        let server = TestServer::new(router).unwrap();
1520
1521        server
1522            .get(&"/ok")
1523            .await
1524            .assert_not_status(StatusCode::ACCEPTED);
1525    }
1526
1527    #[tokio::test]
1528    #[should_panic]
1529    async fn it_should_panic_if_status_code_matches() {
1530        let router = Router::new().route(&"/ok", get(route_get_ok));
1531        let server = TestServer::new(router).unwrap();
1532
1533        server.get(&"/ok").await.assert_not_status(StatusCode::OK);
1534    }
1535}
1536
1537#[cfg(test)]
1538mod test_assert_status_in_range {
1539    use crate::TestServer;
1540    use axum::routing::get;
1541    use axum::routing::Router;
1542    use http::StatusCode;
1543    use std::ops::RangeFull;
1544
1545    #[tokio::test]
1546    async fn it_should_be_true_when_within_int_range() {
1547        let app = Router::new().route(
1548            &"/status",
1549            get(|| async { StatusCode::NON_AUTHORITATIVE_INFORMATION }),
1550        );
1551
1552        TestServer::new(app)
1553            .unwrap()
1554            .get(&"/status")
1555            .await
1556            .assert_status_in_range(200..299);
1557    }
1558
1559    #[tokio::test]
1560    async fn it_should_be_true_when_within_status_code_range() {
1561        let app = Router::new().route(
1562            &"/status",
1563            get(|| async { StatusCode::NON_AUTHORITATIVE_INFORMATION }),
1564        );
1565
1566        TestServer::new(app)
1567            .unwrap()
1568            .get(&"/status")
1569            .await
1570            .assert_status_in_range(StatusCode::OK..StatusCode::IM_USED);
1571    }
1572
1573    #[tokio::test]
1574    #[should_panic]
1575    async fn it_should_be_false_when_outside_int_range() {
1576        let app = Router::new().route(
1577            &"/status",
1578            get(|| async { StatusCode::INTERNAL_SERVER_ERROR }),
1579        );
1580
1581        TestServer::new(app)
1582            .unwrap()
1583            .get(&"/status")
1584            .await
1585            .assert_status_in_range(200..299);
1586    }
1587
1588    #[tokio::test]
1589    #[should_panic]
1590    async fn it_should_be_false_when_outside_status_code_range() {
1591        let app = Router::new().route(
1592            &"/status",
1593            get(|| async { StatusCode::INTERNAL_SERVER_ERROR }),
1594        );
1595
1596        TestServer::new(app)
1597            .unwrap()
1598            .get(&"/status")
1599            .await
1600            .assert_status_in_range(StatusCode::OK..StatusCode::IM_USED);
1601    }
1602
1603    #[tokio::test]
1604    async fn it_should_be_true_when_within_inclusive_range() {
1605        let app = Router::new().route(
1606            &"/status",
1607            get(|| async { StatusCode::NON_AUTHORITATIVE_INFORMATION }),
1608        );
1609
1610        TestServer::new(app)
1611            .unwrap()
1612            .get(&"/status")
1613            .await
1614            .assert_status_in_range(200..=299);
1615    }
1616
1617    #[tokio::test]
1618    #[should_panic]
1619    async fn it_should_be_false_when_outside_inclusive_range() {
1620        let app = Router::new().route(
1621            &"/status",
1622            get(|| async { StatusCode::INTERNAL_SERVER_ERROR }),
1623        );
1624
1625        TestServer::new(app)
1626            .unwrap()
1627            .get(&"/status")
1628            .await
1629            .assert_status_in_range(200..=299);
1630    }
1631
1632    #[tokio::test]
1633    async fn it_should_be_true_when_within_to_range() {
1634        let app = Router::new().route(
1635            &"/status",
1636            get(|| async { StatusCode::NON_AUTHORITATIVE_INFORMATION }),
1637        );
1638
1639        TestServer::new(app)
1640            .unwrap()
1641            .get(&"/status")
1642            .await
1643            .assert_status_in_range(..299);
1644    }
1645
1646    #[tokio::test]
1647    #[should_panic]
1648    async fn it_should_be_false_when_outside_to_range() {
1649        let app = Router::new().route(
1650            &"/status",
1651            get(|| async { StatusCode::INTERNAL_SERVER_ERROR }),
1652        );
1653
1654        TestServer::new(app)
1655            .unwrap()
1656            .get(&"/status")
1657            .await
1658            .assert_status_in_range(..299);
1659    }
1660
1661    #[tokio::test]
1662    async fn it_should_be_true_when_within_to_inclusive_range() {
1663        let app = Router::new().route(
1664            &"/status",
1665            get(|| async { StatusCode::NON_AUTHORITATIVE_INFORMATION }),
1666        );
1667
1668        TestServer::new(app)
1669            .unwrap()
1670            .get(&"/status")
1671            .await
1672            .assert_status_in_range(..=299);
1673    }
1674
1675    #[tokio::test]
1676    #[should_panic]
1677    async fn it_should_be_false_when_outside_to_inclusive_range() {
1678        let app = Router::new().route(
1679            &"/status",
1680            get(|| async { StatusCode::INTERNAL_SERVER_ERROR }),
1681        );
1682
1683        TestServer::new(app)
1684            .unwrap()
1685            .get(&"/status")
1686            .await
1687            .assert_status_in_range(..=299);
1688    }
1689
1690    #[tokio::test]
1691    async fn it_should_be_true_when_within_from_range() {
1692        let app = Router::new().route(
1693            &"/status",
1694            get(|| async { StatusCode::NON_AUTHORITATIVE_INFORMATION }),
1695        );
1696
1697        TestServer::new(app)
1698            .unwrap()
1699            .get(&"/status")
1700            .await
1701            .assert_status_in_range(200..);
1702    }
1703
1704    #[tokio::test]
1705    #[should_panic]
1706    async fn it_should_be_false_when_outside_from_range() {
1707        let app = Router::new().route(
1708            &"/status",
1709            get(|| async { StatusCode::NON_AUTHORITATIVE_INFORMATION }),
1710        );
1711
1712        TestServer::new(app)
1713            .unwrap()
1714            .get(&"/status")
1715            .await
1716            .assert_status_in_range(500..);
1717    }
1718
1719    #[tokio::test]
1720    async fn it_should_be_true_for_rull_range() {
1721        let app = Router::new().route(
1722            &"/status",
1723            get(|| async { StatusCode::NON_AUTHORITATIVE_INFORMATION }),
1724        );
1725
1726        TestServer::new(app)
1727            .unwrap()
1728            .get(&"/status")
1729            .await
1730            .assert_status_in_range::<RangeFull, StatusCode>(..);
1731    }
1732}
1733
1734#[cfg(test)]
1735mod test_assert_status_not_in_range {
1736    use crate::TestServer;
1737    use axum::routing::get;
1738    use axum::routing::Router;
1739    use http::StatusCode;
1740    use std::ops::RangeFull;
1741
1742    #[tokio::test]
1743    #[should_panic]
1744    async fn it_should_be_false_when_within_int_range() {
1745        let app = Router::new().route(
1746            &"/status",
1747            get(|| async { StatusCode::NON_AUTHORITATIVE_INFORMATION }),
1748        );
1749
1750        TestServer::new(app)
1751            .unwrap()
1752            .get(&"/status")
1753            .await
1754            .assert_status_not_in_range(200..299);
1755    }
1756
1757    #[tokio::test]
1758    #[should_panic]
1759    async fn it_should_be_false_when_within_status_code_range() {
1760        let app = Router::new().route(
1761            &"/status",
1762            get(|| async { StatusCode::NON_AUTHORITATIVE_INFORMATION }),
1763        );
1764
1765        TestServer::new(app)
1766            .unwrap()
1767            .get(&"/status")
1768            .await
1769            .assert_status_not_in_range(StatusCode::OK..StatusCode::IM_USED);
1770    }
1771
1772    #[tokio::test]
1773    async fn it_should_be_true_when_outside_int_range() {
1774        let app = Router::new().route(
1775            &"/status",
1776            get(|| async { StatusCode::INTERNAL_SERVER_ERROR }),
1777        );
1778
1779        TestServer::new(app)
1780            .unwrap()
1781            .get(&"/status")
1782            .await
1783            .assert_status_not_in_range(200..299);
1784    }
1785
1786    #[tokio::test]
1787    async fn it_should_be_true_when_outside_status_code_range() {
1788        let app = Router::new().route(
1789            &"/status",
1790            get(|| async { StatusCode::INTERNAL_SERVER_ERROR }),
1791        );
1792
1793        TestServer::new(app)
1794            .unwrap()
1795            .get(&"/status")
1796            .await
1797            .assert_status_not_in_range(StatusCode::OK..StatusCode::IM_USED);
1798    }
1799
1800    #[tokio::test]
1801    #[should_panic]
1802    async fn it_should_be_false_when_within_inclusive_range() {
1803        let app = Router::new().route(
1804            &"/status",
1805            get(|| async { StatusCode::NON_AUTHORITATIVE_INFORMATION }),
1806        );
1807
1808        TestServer::new(app)
1809            .unwrap()
1810            .get(&"/status")
1811            .await
1812            .assert_status_not_in_range(200..=299);
1813    }
1814
1815    #[tokio::test]
1816    async fn it_should_be_true_when_outside_inclusive_range() {
1817        let app = Router::new().route(
1818            &"/status",
1819            get(|| async { StatusCode::INTERNAL_SERVER_ERROR }),
1820        );
1821
1822        TestServer::new(app)
1823            .unwrap()
1824            .get(&"/status")
1825            .await
1826            .assert_status_not_in_range(200..=299);
1827    }
1828
1829    #[tokio::test]
1830    #[should_panic]
1831    async fn it_should_be_false_when_within_to_range() {
1832        let app = Router::new().route(
1833            &"/status",
1834            get(|| async { StatusCode::NON_AUTHORITATIVE_INFORMATION }),
1835        );
1836
1837        TestServer::new(app)
1838            .unwrap()
1839            .get(&"/status")
1840            .await
1841            .assert_status_not_in_range(..299);
1842    }
1843
1844    #[tokio::test]
1845    async fn it_should_be_true_when_outside_to_range() {
1846        let app = Router::new().route(
1847            &"/status",
1848            get(|| async { StatusCode::INTERNAL_SERVER_ERROR }),
1849        );
1850
1851        TestServer::new(app)
1852            .unwrap()
1853            .get(&"/status")
1854            .await
1855            .assert_status_not_in_range(..299);
1856    }
1857
1858    #[tokio::test]
1859    #[should_panic]
1860    async fn it_should_be_false_when_within_to_inclusive_range() {
1861        let app = Router::new().route(
1862            &"/status",
1863            get(|| async { StatusCode::NON_AUTHORITATIVE_INFORMATION }),
1864        );
1865
1866        TestServer::new(app)
1867            .unwrap()
1868            .get(&"/status")
1869            .await
1870            .assert_status_not_in_range(..=299);
1871    }
1872
1873    #[tokio::test]
1874    async fn it_should_be_true_when_outside_to_inclusive_range() {
1875        let app = Router::new().route(
1876            &"/status",
1877            get(|| async { StatusCode::INTERNAL_SERVER_ERROR }),
1878        );
1879
1880        TestServer::new(app)
1881            .unwrap()
1882            .get(&"/status")
1883            .await
1884            .assert_status_not_in_range(..=299);
1885    }
1886
1887    #[tokio::test]
1888    #[should_panic]
1889    async fn it_should_be_false_when_within_from_range() {
1890        let app = Router::new().route(
1891            &"/status",
1892            get(|| async { StatusCode::NON_AUTHORITATIVE_INFORMATION }),
1893        );
1894
1895        TestServer::new(app)
1896            .unwrap()
1897            .get(&"/status")
1898            .await
1899            .assert_status_not_in_range(200..);
1900    }
1901
1902    #[tokio::test]
1903    async fn it_should_be_true_when_outside_from_range() {
1904        let app = Router::new().route(
1905            &"/status",
1906            get(|| async { StatusCode::NON_AUTHORITATIVE_INFORMATION }),
1907        );
1908
1909        TestServer::new(app)
1910            .unwrap()
1911            .get(&"/status")
1912            .await
1913            .assert_status_not_in_range(500..);
1914    }
1915
1916    #[tokio::test]
1917    #[should_panic]
1918    async fn it_should_be_false_for_rull_range() {
1919        let app = Router::new().route(
1920            &"/status",
1921            get(|| async { StatusCode::NON_AUTHORITATIVE_INFORMATION }),
1922        );
1923
1924        TestServer::new(app)
1925            .unwrap()
1926            .get(&"/status")
1927            .await
1928            .assert_status_not_in_range::<RangeFull, StatusCode>(..);
1929    }
1930}
1931
1932#[cfg(test)]
1933mod test_into_bytes {
1934    use crate::TestServer;
1935    use axum::routing::get;
1936    use axum::Json;
1937    use axum::Router;
1938    use serde_json::json;
1939    use serde_json::Value;
1940
1941    async fn route_get_json() -> Json<Value> {
1942        Json(json!({
1943            "message": "it works?"
1944        }))
1945    }
1946
1947    #[tokio::test]
1948    async fn it_should_deserialize_into_json() {
1949        let app = Router::new().route(&"/json", get(route_get_json));
1950
1951        let server = TestServer::new(app).unwrap();
1952
1953        let bytes = server.get(&"/json").await.into_bytes();
1954        let text = String::from_utf8_lossy(&bytes);
1955
1956        assert_eq!(text, r#"{"message":"it works?"}"#);
1957    }
1958}
1959
1960#[cfg(test)]
1961mod test_content_type {
1962    use crate::TestServer;
1963    use axum::routing::get;
1964    use axum::Json;
1965    use axum::Router;
1966    use serde::Deserialize;
1967    use serde::Serialize;
1968
1969    #[derive(Serialize, Deserialize, PartialEq, Debug)]
1970    struct ExampleResponse {
1971        name: String,
1972        age: u32,
1973    }
1974
1975    #[tokio::test]
1976    async fn it_should_retrieve_json_content_type_for_json() {
1977        let app = Router::new().route(
1978            &"/json",
1979            get(|| async {
1980                Json(ExampleResponse {
1981                    name: "Joe".to_string(),
1982                    age: 20,
1983                })
1984            }),
1985        );
1986
1987        let server = TestServer::new(app).unwrap();
1988
1989        let content_type = server.get(&"/json").await.content_type();
1990        assert_eq!(content_type, "application/json");
1991    }
1992
1993    #[cfg(feature = "yaml")]
1994    #[tokio::test]
1995    async fn it_should_retrieve_yaml_content_type_for_yaml() {
1996        use axum_yaml::Yaml;
1997
1998        let app = Router::new().route(
1999            &"/yaml",
2000            get(|| async {
2001                Yaml(ExampleResponse {
2002                    name: "Joe".to_string(),
2003                    age: 20,
2004                })
2005            }),
2006        );
2007
2008        let server = TestServer::new(app).unwrap();
2009
2010        let content_type = server.get(&"/yaml").await.content_type();
2011        assert_eq!(content_type, "application/yaml");
2012    }
2013}
2014
2015#[cfg(test)]
2016mod test_json {
2017    use crate::TestServer;
2018    use axum::routing::get;
2019    use axum::Json;
2020    use axum::Router;
2021    use serde::Deserialize;
2022    use serde::Serialize;
2023
2024    #[derive(Serialize, Deserialize, PartialEq, Debug)]
2025    struct ExampleResponse {
2026        name: String,
2027        age: u32,
2028    }
2029
2030    async fn route_get_json() -> Json<ExampleResponse> {
2031        Json(ExampleResponse {
2032            name: "Joe".to_string(),
2033            age: 20,
2034        })
2035    }
2036
2037    #[tokio::test]
2038    async fn it_should_deserialize_into_json() {
2039        let app = Router::new().route(&"/json", get(route_get_json));
2040
2041        let server = TestServer::new(app).unwrap();
2042
2043        let response = server.get(&"/json").await.json::<ExampleResponse>();
2044
2045        assert_eq!(
2046            response,
2047            ExampleResponse {
2048                name: "Joe".to_string(),
2049                age: 20,
2050            }
2051        );
2052    }
2053}
2054
2055#[cfg(feature = "yaml")]
2056#[cfg(test)]
2057mod test_yaml {
2058    use crate::TestServer;
2059    use axum::routing::get;
2060    use axum::Router;
2061    use axum_yaml::Yaml;
2062    use serde::Deserialize;
2063    use serde::Serialize;
2064
2065    #[derive(Serialize, Deserialize, PartialEq, Debug)]
2066    struct ExampleResponse {
2067        name: String,
2068        age: u32,
2069    }
2070
2071    async fn route_get_yaml() -> Yaml<ExampleResponse> {
2072        Yaml(ExampleResponse {
2073            name: "Joe".to_string(),
2074            age: 20,
2075        })
2076    }
2077
2078    #[tokio::test]
2079    async fn it_should_deserialize_into_yaml() {
2080        let app = Router::new().route(&"/yaml", get(route_get_yaml));
2081
2082        let server = TestServer::new(app).unwrap();
2083
2084        let response = server.get(&"/yaml").await.yaml::<ExampleResponse>();
2085
2086        assert_eq!(
2087            response,
2088            ExampleResponse {
2089                name: "Joe".to_string(),
2090                age: 20,
2091            }
2092        );
2093    }
2094}
2095
2096#[cfg(feature = "msgpack")]
2097#[cfg(test)]
2098mod test_msgpack {
2099    use crate::TestServer;
2100    use axum::routing::get;
2101    use axum::Router;
2102    use axum_msgpack::MsgPack;
2103    use serde::Deserialize;
2104    use serde::Serialize;
2105
2106    #[derive(Serialize, Deserialize, PartialEq, Debug)]
2107    struct ExampleResponse {
2108        name: String,
2109        age: u32,
2110    }
2111
2112    async fn route_get_msgpack() -> MsgPack<ExampleResponse> {
2113        MsgPack(ExampleResponse {
2114            name: "Joe".to_string(),
2115            age: 20,
2116        })
2117    }
2118
2119    #[tokio::test]
2120    async fn it_should_deserialize_into_msgpack() {
2121        let app = Router::new().route(&"/msgpack", get(route_get_msgpack));
2122
2123        let server = TestServer::new(app).unwrap();
2124
2125        let response = server.get(&"/msgpack").await.msgpack::<ExampleResponse>();
2126
2127        assert_eq!(
2128            response,
2129            ExampleResponse {
2130                name: "Joe".to_string(),
2131                age: 20,
2132            }
2133        );
2134    }
2135}
2136
2137#[cfg(test)]
2138mod test_form {
2139    use crate::TestServer;
2140    use axum::routing::get;
2141    use axum::Form;
2142    use axum::Router;
2143    use serde::Deserialize;
2144    use serde::Serialize;
2145
2146    #[derive(Serialize, Deserialize, PartialEq, Debug)]
2147    struct ExampleResponse {
2148        name: String,
2149        age: u32,
2150    }
2151
2152    async fn route_get_form() -> Form<ExampleResponse> {
2153        Form(ExampleResponse {
2154            name: "Joe".to_string(),
2155            age: 20,
2156        })
2157    }
2158
2159    #[tokio::test]
2160    async fn it_should_deserialize_into_form() {
2161        let app = Router::new().route(&"/form", get(route_get_form));
2162
2163        let server = TestServer::new(app).unwrap();
2164
2165        let response = server.get(&"/form").await.form::<ExampleResponse>();
2166
2167        assert_eq!(
2168            response,
2169            ExampleResponse {
2170                name: "Joe".to_string(),
2171                age: 20,
2172            }
2173        );
2174    }
2175}
2176
2177#[cfg(test)]
2178mod test_from {
2179    use crate::TestServer;
2180    use axum::routing::get;
2181    use axum::Router;
2182    use bytes::Bytes;
2183
2184    #[tokio::test]
2185    async fn it_should_turn_into_response_bytes() {
2186        let app = Router::new().route(&"/text", get(|| async { "This is some example text" }));
2187        let server = TestServer::new(app).unwrap();
2188
2189        let response = server.get(&"/text").await;
2190        let bytes: Bytes = response.into();
2191        let text = String::from_utf8_lossy(&bytes);
2192        assert_eq!(text, "This is some example text");
2193    }
2194}
2195
2196#[cfg(test)]
2197mod test_assert_text {
2198    use crate::TestServer;
2199    use axum::routing::get;
2200    use axum::Router;
2201
2202    fn new_test_server() -> TestServer {
2203        async fn route_get_text() -> &'static str {
2204            "This is some example text"
2205        }
2206
2207        let app = Router::new().route(&"/text", get(route_get_text));
2208        TestServer::new(app).unwrap()
2209    }
2210
2211    #[tokio::test]
2212    async fn it_should_match_whole_text() {
2213        let server = new_test_server();
2214
2215        server
2216            .get(&"/text")
2217            .await
2218            .assert_text("This is some example text");
2219    }
2220
2221    #[tokio::test]
2222    #[should_panic]
2223    async fn it_should_not_match_partial_text() {
2224        let server = new_test_server();
2225
2226        server.get(&"/text").await.assert_text("some example");
2227    }
2228
2229    #[tokio::test]
2230    #[should_panic]
2231    async fn it_should_not_match_different_text() {
2232        let server = new_test_server();
2233
2234        server.get(&"/text").await.assert_text("🦊");
2235    }
2236}
2237
2238#[cfg(test)]
2239mod test_assert_text_contains {
2240    use crate::TestServer;
2241    use axum::routing::get;
2242    use axum::Router;
2243
2244    fn new_test_server() -> TestServer {
2245        async fn route_get_text() -> &'static str {
2246            "This is some example text"
2247        }
2248
2249        let app = Router::new().route(&"/text", get(route_get_text));
2250        TestServer::new(app).unwrap()
2251    }
2252
2253    #[tokio::test]
2254    async fn it_should_match_whole_text() {
2255        let server = new_test_server();
2256
2257        server
2258            .get(&"/text")
2259            .await
2260            .assert_text_contains("This is some example text");
2261    }
2262
2263    #[tokio::test]
2264    async fn it_should_match_partial_text() {
2265        let server = new_test_server();
2266
2267        server
2268            .get(&"/text")
2269            .await
2270            .assert_text_contains("some example");
2271    }
2272
2273    #[tokio::test]
2274    #[should_panic]
2275    async fn it_should_not_match_different_text() {
2276        let server = new_test_server();
2277
2278        server.get(&"/text").await.assert_text_contains("🦊");
2279    }
2280}
2281
2282#[cfg(test)]
2283mod test_assert_text_from_file {
2284    use crate::TestServer;
2285    use axum::routing::get;
2286    use axum::routing::Router;
2287
2288    #[tokio::test]
2289    async fn it_should_match_from_file() {
2290        let app = Router::new().route(&"/text", get(|| async { "hello!" }));
2291        let server = TestServer::new(app).unwrap();
2292
2293        server
2294            .get(&"/text")
2295            .await
2296            .assert_text_from_file("files/example.txt");
2297    }
2298
2299    #[tokio::test]
2300    #[should_panic]
2301    async fn it_should_panic_when_not_match_the_file() {
2302        let app = Router::new().route(&"/text", get(|| async { "🦊" }));
2303        let server = TestServer::new(app).unwrap();
2304
2305        server
2306            .get(&"/text")
2307            .await
2308            .assert_text_from_file("files/example.txt");
2309    }
2310}
2311
2312#[cfg(test)]
2313mod test_assert_json {
2314    use crate::expect_json::expect_core::Context;
2315    use crate::expect_json::expect_core::ExpectOp;
2316    use crate::expect_json::expect_core::ExpectOpResult;
2317    use crate::TestServer;
2318    // This needs to be the external crate, as the `::axum_test` path doesn't work within our tests.
2319    use ::expect_json::expect_core::expect_op;
2320    use axum::routing::get;
2321    use axum::Form;
2322    use axum::Json;
2323    use axum::Router;
2324    use serde::Deserialize;
2325    use serde::Serialize;
2326    use serde_json::json;
2327
2328    #[derive(Serialize, Deserialize, PartialEq, Debug)]
2329    struct ExampleResponse {
2330        name: String,
2331        age: u32,
2332    }
2333
2334    async fn route_get_form() -> Form<ExampleResponse> {
2335        Form(ExampleResponse {
2336            name: "Joe".to_string(),
2337            age: 20,
2338        })
2339    }
2340
2341    async fn route_get_json() -> Json<ExampleResponse> {
2342        Json(ExampleResponse {
2343            name: "Joe".to_string(),
2344            age: 20,
2345        })
2346    }
2347
2348    #[tokio::test]
2349    async fn it_should_match_json_returned() {
2350        let app = Router::new().route(&"/json", get(route_get_json));
2351
2352        let server = TestServer::new(app).unwrap();
2353
2354        server.get(&"/json").await.assert_json(&ExampleResponse {
2355            name: "Joe".to_string(),
2356            age: 20,
2357        });
2358    }
2359
2360    #[tokio::test]
2361    #[should_panic]
2362    async fn it_should_panic_if_response_is_different() {
2363        let app = Router::new().route(&"/json", get(route_get_json));
2364
2365        let server = TestServer::new(app).unwrap();
2366
2367        server.get(&"/json").await.assert_json(&ExampleResponse {
2368            name: "Julia".to_string(),
2369            age: 25,
2370        });
2371    }
2372
2373    #[tokio::test]
2374    #[should_panic]
2375    async fn it_should_panic_if_response_is_form() {
2376        let app = Router::new().route(&"/form", get(route_get_form));
2377
2378        let server = TestServer::new(app).unwrap();
2379
2380        server.get(&"/form").await.assert_json(&ExampleResponse {
2381            name: "Joe".to_string(),
2382            age: 20,
2383        });
2384    }
2385
2386    #[tokio::test]
2387    async fn it_should_work_with_custom_expect_op() {
2388        #[expect_op]
2389        #[derive(Clone, Debug)]
2390        struct ExpectStrMinLen {
2391            min: usize,
2392        }
2393
2394        impl ExpectOp for ExpectStrMinLen {
2395            fn on_string(&self, _context: &mut Context<'_>, received: &str) -> ExpectOpResult<()> {
2396                if received.len() < self.min {
2397                    panic!("String is too short, received: {received}");
2398                }
2399
2400                Ok(())
2401            }
2402        }
2403
2404        let app = Router::new().route(&"/json", get(route_get_json));
2405        let server = TestServer::new(app).unwrap();
2406
2407        server.get(&"/json").await.assert_json(&json!({
2408            "name": ExpectStrMinLen { min: 3 },
2409            "age": 20,
2410        }));
2411    }
2412}
2413
2414#[cfg(test)]
2415mod test_assert_json_contains {
2416    use crate::TestServer;
2417    use axum::routing::get;
2418    use axum::Form;
2419    use axum::Json;
2420    use axum::Router;
2421    use serde::Deserialize;
2422    use serde::Serialize;
2423    use serde_json::json;
2424    use std::time::Instant;
2425
2426    #[derive(Serialize, Deserialize, PartialEq, Debug)]
2427    struct ExampleResponse {
2428        time: u64,
2429        name: String,
2430        age: u32,
2431    }
2432
2433    async fn route_get_form() -> Form<ExampleResponse> {
2434        Form(ExampleResponse {
2435            time: Instant::now().elapsed().as_millis() as u64,
2436            name: "Joe".to_string(),
2437            age: 20,
2438        })
2439    }
2440
2441    async fn route_get_json() -> Json<ExampleResponse> {
2442        Json(ExampleResponse {
2443            time: Instant::now().elapsed().as_millis() as u64,
2444            name: "Joe".to_string(),
2445            age: 20,
2446        })
2447    }
2448
2449    #[tokio::test]
2450    async fn it_should_match_subset_of_json_returned() {
2451        let app = Router::new().route(&"/json", get(route_get_json));
2452        let server = TestServer::new(app).unwrap();
2453
2454        server.get(&"/json").await.assert_json_contains(&json!({
2455            "name": "Joe",
2456            "age": 20,
2457        }));
2458    }
2459
2460    #[tokio::test]
2461    #[should_panic]
2462    async fn it_should_panic_if_response_is_different() {
2463        let app = Router::new().route(&"/json", get(route_get_json));
2464        let server = TestServer::new(app).unwrap();
2465
2466        server
2467            .get(&"/json")
2468            .await
2469            .assert_json_contains(&ExampleResponse {
2470                time: 1234,
2471                name: "Julia".to_string(),
2472                age: 25,
2473            });
2474    }
2475
2476    #[tokio::test]
2477    #[should_panic]
2478    async fn it_should_panic_if_response_is_form() {
2479        let app = Router::new().route(&"/form", get(route_get_form));
2480        let server = TestServer::new(app).unwrap();
2481
2482        server.get(&"/form").await.assert_json_contains(&json!({
2483            "name": "Joe",
2484            "age": 20,
2485        }));
2486    }
2487
2488    /// See: https://github.com/JosephLenton/axum-test/issues/151
2489    #[tokio::test]
2490    async fn it_should_propagate_contains_to_sub_objects() {
2491        let json_result = json!({ "a": {"prop1": "value1"} }).to_string();
2492        let app = Router::new().route(&"/json", get(|| async { json_result }));
2493
2494        let server = TestServer::new(app).unwrap();
2495        let response = server.get("/json").await;
2496
2497        response.assert_json_contains(&json!({ "a": {} }));
2498    }
2499}
2500
2501#[cfg(test)]
2502mod test_assert_json_from_file {
2503    use crate::TestServer;
2504    use axum::routing::get;
2505    use axum::routing::Router;
2506    use axum::Form;
2507    use axum::Json;
2508    use serde::Deserialize;
2509    use serde::Serialize;
2510    use serde_json::json;
2511
2512    #[tokio::test]
2513    async fn it_should_match_json_from_file() {
2514        let app = Router::new().route(
2515            &"/json",
2516            get(|| async {
2517                Json(json!(
2518                    {
2519                        "name": "Joe",
2520                        "age": 20,
2521                    }
2522                ))
2523            }),
2524        );
2525        let server = TestServer::new(app).unwrap();
2526
2527        server
2528            .get(&"/json")
2529            .await
2530            .assert_json_from_file("files/example.json");
2531    }
2532
2533    #[tokio::test]
2534    #[should_panic]
2535    async fn it_should_panic_when_not_match_the_file() {
2536        let app = Router::new().route(
2537            &"/json",
2538            get(|| async {
2539                Json(json!(
2540                    {
2541                        "name": "Julia",
2542                        "age": 25,
2543                    }
2544                ))
2545            }),
2546        );
2547        let server = TestServer::new(app).unwrap();
2548
2549        server
2550            .get(&"/json")
2551            .await
2552            .assert_json_from_file("files/example.json");
2553    }
2554
2555    #[tokio::test]
2556    #[should_panic]
2557    async fn it_should_panic_when_content_type_does_not_match() {
2558        #[derive(Serialize, Deserialize, PartialEq, Debug)]
2559        struct ExampleResponse {
2560            name: String,
2561            age: u32,
2562        }
2563
2564        let app = Router::new().route(
2565            &"/form",
2566            get(|| async {
2567                Form(ExampleResponse {
2568                    name: "Joe".to_string(),
2569                    age: 20,
2570                })
2571            }),
2572        );
2573        let server = TestServer::new(app).unwrap();
2574
2575        server
2576            .get(&"/form")
2577            .await
2578            .assert_json_from_file("files/example.json");
2579    }
2580}
2581
2582#[cfg(feature = "yaml")]
2583#[cfg(test)]
2584mod test_assert_yaml {
2585    use crate::TestServer;
2586    use axum::routing::get;
2587    use axum::Form;
2588    use axum::Router;
2589    use axum_yaml::Yaml;
2590    use serde::Deserialize;
2591    use serde::Serialize;
2592
2593    #[derive(Serialize, Deserialize, PartialEq, Debug)]
2594    struct ExampleResponse {
2595        name: String,
2596        age: u32,
2597    }
2598
2599    async fn route_get_form() -> Form<ExampleResponse> {
2600        Form(ExampleResponse {
2601            name: "Joe".to_string(),
2602            age: 20,
2603        })
2604    }
2605
2606    async fn route_get_yaml() -> Yaml<ExampleResponse> {
2607        Yaml(ExampleResponse {
2608            name: "Joe".to_string(),
2609            age: 20,
2610        })
2611    }
2612
2613    #[tokio::test]
2614    async fn it_should_match_yaml_returned() {
2615        let app = Router::new().route(&"/yaml", get(route_get_yaml));
2616
2617        let server = TestServer::new(app).unwrap();
2618
2619        server.get(&"/yaml").await.assert_yaml(&ExampleResponse {
2620            name: "Joe".to_string(),
2621            age: 20,
2622        });
2623    }
2624
2625    #[tokio::test]
2626    #[should_panic]
2627    async fn it_should_panic_if_response_is_different() {
2628        let app = Router::new().route(&"/yaml", get(route_get_yaml));
2629
2630        let server = TestServer::new(app).unwrap();
2631
2632        server.get(&"/yaml").await.assert_yaml(&ExampleResponse {
2633            name: "Julia".to_string(),
2634            age: 25,
2635        });
2636    }
2637
2638    #[tokio::test]
2639    #[should_panic]
2640    async fn it_should_panic_if_response_is_form() {
2641        let app = Router::new().route(&"/form", get(route_get_form));
2642
2643        let server = TestServer::new(app).unwrap();
2644
2645        server.get(&"/form").await.assert_yaml(&ExampleResponse {
2646            name: "Joe".to_string(),
2647            age: 20,
2648        });
2649    }
2650}
2651
2652#[cfg(feature = "yaml")]
2653#[cfg(test)]
2654mod test_assert_yaml_from_file {
2655    use crate::TestServer;
2656    use axum::routing::get;
2657    use axum::routing::Router;
2658    use axum::Form;
2659    use axum_yaml::Yaml;
2660    use serde::Deserialize;
2661    use serde::Serialize;
2662    use serde_json::json;
2663
2664    #[tokio::test]
2665    async fn it_should_match_yaml_from_file() {
2666        let app = Router::new().route(
2667            &"/yaml",
2668            get(|| async {
2669                Yaml(json!(
2670                    {
2671                        "name": "Joe",
2672                        "age": 20,
2673                    }
2674                ))
2675            }),
2676        );
2677        let server = TestServer::new(app).unwrap();
2678
2679        server
2680            .get(&"/yaml")
2681            .await
2682            .assert_yaml_from_file("files/example.yaml");
2683    }
2684
2685    #[tokio::test]
2686    #[should_panic]
2687    async fn it_should_panic_when_not_match_the_file() {
2688        let app = Router::new().route(
2689            &"/yaml",
2690            get(|| async {
2691                Yaml(json!(
2692                    {
2693                        "name": "Julia",
2694                        "age": 25,
2695                    }
2696                ))
2697            }),
2698        );
2699        let server = TestServer::new(app).unwrap();
2700
2701        server
2702            .get(&"/yaml")
2703            .await
2704            .assert_yaml_from_file("files/example.yaml");
2705    }
2706
2707    #[tokio::test]
2708    #[should_panic]
2709    async fn it_should_panic_when_content_type_does_not_match() {
2710        #[derive(Serialize, Deserialize, PartialEq, Debug)]
2711        struct ExampleResponse {
2712            name: String,
2713            age: u32,
2714        }
2715
2716        let app = Router::new().route(
2717            &"/form",
2718            get(|| async {
2719                Form(ExampleResponse {
2720                    name: "Joe".to_string(),
2721                    age: 20,
2722                })
2723            }),
2724        );
2725        let server = TestServer::new(app).unwrap();
2726
2727        server
2728            .get(&"/form")
2729            .await
2730            .assert_yaml_from_file("files/example.yaml");
2731    }
2732}
2733
2734#[cfg(test)]
2735mod test_assert_form {
2736    use crate::TestServer;
2737    use axum::routing::get;
2738    use axum::Form;
2739    use axum::Json;
2740    use axum::Router;
2741    use serde::Deserialize;
2742    use serde::Serialize;
2743
2744    #[derive(Serialize, Deserialize, PartialEq, Debug)]
2745    struct ExampleResponse {
2746        name: String,
2747        age: u32,
2748    }
2749
2750    async fn route_get_form() -> Form<ExampleResponse> {
2751        Form(ExampleResponse {
2752            name: "Joe".to_string(),
2753            age: 20,
2754        })
2755    }
2756
2757    async fn route_get_json() -> Json<ExampleResponse> {
2758        Json(ExampleResponse {
2759            name: "Joe".to_string(),
2760            age: 20,
2761        })
2762    }
2763
2764    #[tokio::test]
2765    async fn it_should_match_form_returned() {
2766        let app = Router::new().route(&"/form", get(route_get_form));
2767
2768        let server = TestServer::new(app).unwrap();
2769
2770        server.get(&"/form").await.assert_form(&ExampleResponse {
2771            name: "Joe".to_string(),
2772            age: 20,
2773        });
2774    }
2775
2776    #[tokio::test]
2777    #[should_panic]
2778    async fn it_should_panic_if_response_is_different() {
2779        let app = Router::new().route(&"/form", get(route_get_form));
2780
2781        let server = TestServer::new(app).unwrap();
2782
2783        server.get(&"/form").await.assert_form(&ExampleResponse {
2784            name: "Julia".to_string(),
2785            age: 25,
2786        });
2787    }
2788
2789    #[tokio::test]
2790    #[should_panic]
2791    async fn it_should_panic_if_response_is_json() {
2792        let app = Router::new().route(&"/json", get(route_get_json));
2793
2794        let server = TestServer::new(app).unwrap();
2795
2796        server.get(&"/json").await.assert_form(&ExampleResponse {
2797            name: "Joe".to_string(),
2798            age: 20,
2799        });
2800    }
2801}
2802
2803#[cfg(test)]
2804mod test_text {
2805    use crate::TestServer;
2806    use axum::routing::get;
2807    use axum::Router;
2808
2809    #[tokio::test]
2810    async fn it_should_deserialize_into_text() {
2811        async fn route_get_text() -> String {
2812            "hello!".to_string()
2813        }
2814
2815        let app = Router::new().route(&"/text", get(route_get_text));
2816
2817        let server = TestServer::new(app).unwrap();
2818
2819        let response = server.get(&"/text").await.text();
2820
2821        assert_eq!(response, "hello!");
2822    }
2823}
2824
2825#[cfg(feature = "ws")]
2826#[cfg(test)]
2827mod test_into_websocket {
2828    use crate::TestServer;
2829
2830    use axum::extract::ws::WebSocket;
2831    use axum::extract::WebSocketUpgrade;
2832    use axum::response::Response;
2833    use axum::routing::get;
2834    use axum::Router;
2835
2836    fn new_test_router() -> Router {
2837        pub async fn route_get_websocket(ws: WebSocketUpgrade) -> Response {
2838            async fn handle_ping_pong(mut socket: WebSocket) {
2839                while let Some(_) = socket.recv().await {
2840                    // do nothing
2841                }
2842            }
2843
2844            ws.on_upgrade(move |socket| handle_ping_pong(socket))
2845        }
2846
2847        let app = Router::new().route(&"/ws", get(route_get_websocket));
2848
2849        app
2850    }
2851
2852    #[tokio::test]
2853    async fn it_should_upgrade_on_http_transport() {
2854        let router = new_test_router();
2855        let server = TestServer::builder()
2856            .http_transport()
2857            .build(router)
2858            .unwrap();
2859
2860        let _ = server.get_websocket(&"/ws").await.into_websocket().await;
2861
2862        assert!(true);
2863    }
2864
2865    #[tokio::test]
2866    #[should_panic]
2867    async fn it_should_fail_to_upgrade_on_mock_transport() {
2868        let router = new_test_router();
2869        let server = TestServer::builder()
2870            .mock_transport()
2871            .build(router)
2872            .unwrap();
2873
2874        let _ = server.get_websocket(&"/ws").await.into_websocket().await;
2875    }
2876}