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