1#![cfg(native)]
19
20use std::collections::HashMap;
21
22use bytes::Bytes;
23use http::{HeaderMap, HeaderValue, Method, StatusCode, Uri, header::HeaderName};
24use serde::{Deserialize, Serialize};
25
26#[derive(Debug, Clone)]
31pub struct MockHttpRequest {
32 pub method: Method,
34 pub uri: Uri,
36 pub headers: HeaderMap,
38 pub body: Bytes,
40 pub cookies: HashMap<String, String>,
42 pub query_params: HashMap<String, String>,
44}
45
46impl Default for MockHttpRequest {
47 fn default() -> Self {
48 Self {
49 method: Method::GET,
50 uri: "/".parse().unwrap(),
51 headers: HeaderMap::new(),
52 body: Bytes::new(),
53 cookies: HashMap::new(),
54 query_params: HashMap::new(),
55 }
56 }
57}
58
59impl MockHttpRequest {
60 pub fn new(method: Method, uri: &str) -> Self {
62 let parsed_uri: Uri = uri.parse().unwrap_or_else(|_| "/".parse().unwrap());
63
64 let query_params = parsed_uri
66 .query()
67 .map(|q| {
68 url::form_urlencoded::parse(q.as_bytes())
69 .map(|(k, v)| (k.to_string(), v.to_string()))
70 .collect()
71 })
72 .unwrap_or_default();
73
74 Self {
75 method,
76 uri: parsed_uri,
77 query_params,
78 ..Default::default()
79 }
80 }
81
82 pub fn get(uri: &str) -> Self {
84 Self::new(Method::GET, uri)
85 }
86
87 pub fn post(uri: &str) -> Self {
89 Self::new(Method::POST, uri)
90 }
91
92 pub fn put(uri: &str) -> Self {
94 Self::new(Method::PUT, uri)
95 }
96
97 pub fn patch(uri: &str) -> Self {
99 Self::new(Method::PATCH, uri)
100 }
101
102 pub fn delete(uri: &str) -> Self {
104 Self::new(Method::DELETE, uri)
105 }
106
107 pub fn with_json<T: Serialize>(mut self, body: &T) -> Self {
118 let bytes = serde_json::to_vec(body).unwrap_or_else(|err| {
119 panic!("MockHttpRequest::with_json: failed to serialize body to JSON: {err}")
120 });
121 self.body = Bytes::from(bytes);
122 self.headers.insert(
123 http::header::CONTENT_TYPE,
124 HeaderValue::from_static("application/json"),
125 );
126 self
127 }
128
129 pub fn with_form<T: Serialize>(mut self, body: &T) -> Self {
140 let encoded = serde_urlencoded::to_string(body).unwrap_or_else(|err| {
141 panic!("MockHttpRequest::with_form: failed to serialize body as form data: {err}")
142 });
143 self.body = Bytes::from(encoded);
144 self.headers.insert(
145 http::header::CONTENT_TYPE,
146 HeaderValue::from_static("application/x-www-form-urlencoded"),
147 );
148 self
149 }
150
151 pub fn with_body(mut self, body: impl Into<Bytes>) -> Self {
153 self.body = body.into();
154 self
155 }
156
157 pub fn with_text(mut self, body: impl Into<String>) -> Self {
159 self.body = Bytes::from(body.into());
160 self.headers.insert(
161 http::header::CONTENT_TYPE,
162 HeaderValue::from_static("text/plain"),
163 );
164 self
165 }
166
167 pub fn with_header(mut self, name: &str, value: &str) -> Self {
169 if let (Ok(header_name), Ok(header_value)) = (
170 HeaderName::from_bytes(name.as_bytes()),
171 HeaderValue::from_str(value),
172 ) {
173 self.headers.insert(header_name, header_value);
174 }
175 self
176 }
177
178 pub fn with_headers<'a>(
180 mut self,
181 headers: impl IntoIterator<Item = (&'a str, &'a str)>,
182 ) -> Self {
183 for (name, value) in headers {
184 if let (Ok(header_name), Ok(header_value)) = (
185 HeaderName::from_bytes(name.as_bytes()),
186 HeaderValue::from_str(value),
187 ) {
188 self.headers.insert(header_name, header_value);
189 }
190 }
191 self
192 }
193
194 pub fn with_cookie(mut self, name: &str, value: &str) -> Self {
196 self.cookies.insert(name.to_string(), value.to_string());
197 self.update_cookie_header();
198 self
199 }
200
201 pub fn with_cookies<'a>(
203 mut self,
204 cookies: impl IntoIterator<Item = (&'a str, &'a str)>,
205 ) -> Self {
206 for (name, value) in cookies {
207 self.cookies.insert(name.to_string(), value.to_string());
208 }
209 self.update_cookie_header();
210 self
211 }
212
213 pub fn with_query(mut self, name: &str, value: &str) -> Self {
215 self.query_params
216 .insert(name.to_string(), value.to_string());
217 self.update_uri_query();
218 self
219 }
220
221 pub fn with_query_params<'a>(
223 mut self,
224 params: impl IntoIterator<Item = (&'a str, &'a str)>,
225 ) -> Self {
226 for (name, value) in params {
227 self.query_params
228 .insert(name.to_string(), value.to_string());
229 }
230 self.update_uri_query();
231 self
232 }
233
234 pub fn with_bearer_token(self, token: &str) -> Self {
236 self.with_header("Authorization", &format!("Bearer {}", token))
237 }
238
239 pub fn with_basic_auth(self, username: &str, password: &str) -> Self {
241 let credentials =
242 base64_simd::STANDARD.encode_to_string(format!("{}:{}", username, password));
243 self.with_header("Authorization", &format!("Basic {}", credentials))
244 }
245
246 pub fn with_content_type(self, content_type: &str) -> Self {
248 self.with_header("Content-Type", content_type)
249 }
250
251 pub fn with_accept(self, accept: &str) -> Self {
253 self.with_header("Accept", accept)
254 }
255
256 pub fn path(&self) -> &str {
258 self.uri.path()
259 }
260
261 pub fn uri_string(&self) -> String {
263 self.uri.to_string()
264 }
265
266 pub fn get_header(&self, name: &str) -> Option<&str> {
268 self.headers.get(name).and_then(|v| v.to_str().ok())
269 }
270
271 pub fn get_cookie(&self, name: &str) -> Option<&str> {
273 self.cookies.get(name).map(|s| s.as_str())
274 }
275
276 pub fn get_query(&self, name: &str) -> Option<&str> {
278 self.query_params.get(name).map(|s| s.as_str())
279 }
280
281 pub fn json<T: for<'de> Deserialize<'de>>(&self) -> Result<T, serde_json::Error> {
283 serde_json::from_slice(&self.body)
284 }
285
286 pub fn form<T: for<'de> Deserialize<'de>>(&self) -> Result<T, serde_urlencoded::de::Error> {
288 serde_urlencoded::from_bytes(&self.body)
289 }
290
291 pub fn text(&self) -> Result<String, std::string::FromUtf8Error> {
293 String::from_utf8(self.body.to_vec())
294 }
295
296 fn update_cookie_header(&mut self) {
297 if self.cookies.is_empty() {
298 self.headers.remove(http::header::COOKIE);
299 } else {
300 let cookie_str: String = self
301 .cookies
302 .iter()
303 .map(|(k, v)| format!("{}={}", k, v))
304 .collect::<Vec<_>>()
305 .join("; ");
306
307 if let Ok(value) = HeaderValue::from_str(&cookie_str) {
308 self.headers.insert(http::header::COOKIE, value);
309 }
310 }
311 }
312
313 fn update_uri_query(&mut self) {
314 let path = self.uri.path().to_string();
315 if self.query_params.is_empty() {
316 if let Ok(uri) = path.parse() {
317 self.uri = uri;
318 }
319 } else {
320 let query: String = self
321 .query_params
322 .iter()
323 .map(|(k, v)| format!("{}={}", urlencoding::encode(k), urlencoding::encode(v)))
324 .collect::<Vec<_>>()
325 .join("&");
326
327 if let Ok(uri) = format!("{}?{}", path, query).parse() {
328 self.uri = uri;
329 }
330 }
331 }
332}
333
334#[derive(Debug, Clone)]
339pub struct MockHttpResponse {
340 pub status: StatusCode,
342 pub headers: HeaderMap,
344 pub body: Bytes,
346}
347
348impl Default for MockHttpResponse {
349 fn default() -> Self {
350 Self {
351 status: StatusCode::OK,
352 headers: HeaderMap::new(),
353 body: Bytes::new(),
354 }
355 }
356}
357
358impl MockHttpResponse {
359 pub fn new(status: StatusCode) -> Self {
361 Self {
362 status,
363 ..Default::default()
364 }
365 }
366
367 pub fn ok() -> Self {
369 Self::new(StatusCode::OK)
370 }
371
372 pub fn created() -> Self {
374 Self::new(StatusCode::CREATED)
375 }
376
377 pub fn no_content() -> Self {
379 Self::new(StatusCode::NO_CONTENT)
380 }
381
382 pub fn bad_request() -> Self {
384 Self::new(StatusCode::BAD_REQUEST)
385 }
386
387 pub fn unauthorized() -> Self {
389 Self::new(StatusCode::UNAUTHORIZED)
390 }
391
392 pub fn forbidden() -> Self {
394 Self::new(StatusCode::FORBIDDEN)
395 }
396
397 pub fn not_found() -> Self {
399 Self::new(StatusCode::NOT_FOUND)
400 }
401
402 pub fn internal_error() -> Self {
404 Self::new(StatusCode::INTERNAL_SERVER_ERROR)
405 }
406
407 pub fn json<T: Serialize>(body: &T) -> Self {
409 let mut response = Self::ok();
410 if let Ok(bytes) = serde_json::to_vec(body) {
411 response.body = Bytes::from(bytes);
412 response.headers.insert(
413 http::header::CONTENT_TYPE,
414 HeaderValue::from_static("application/json"),
415 );
416 }
417 response
418 }
419
420 pub fn text(body: impl Into<String>) -> Self {
422 let mut response = Self::ok();
423 response.body = Bytes::from(body.into());
424 response.headers.insert(
425 http::header::CONTENT_TYPE,
426 HeaderValue::from_static("text/plain"),
427 );
428 response
429 }
430
431 pub fn with_json<T: Serialize>(mut self, body: &T) -> Self {
433 if let Ok(bytes) = serde_json::to_vec(body) {
434 self.body = Bytes::from(bytes);
435 self.headers.insert(
436 http::header::CONTENT_TYPE,
437 HeaderValue::from_static("application/json"),
438 );
439 }
440 self
441 }
442
443 pub fn with_body(mut self, body: impl Into<Bytes>) -> Self {
445 self.body = body.into();
446 self
447 }
448
449 pub fn with_status(mut self, status: StatusCode) -> Self {
451 self.status = status;
452 self
453 }
454
455 pub fn with_header(mut self, name: &str, value: &str) -> Self {
457 if let (Ok(header_name), Ok(header_value)) = (
458 HeaderName::from_bytes(name.as_bytes()),
459 HeaderValue::from_str(value),
460 ) {
461 self.headers.insert(header_name, header_value);
462 }
463 self
464 }
465
466 pub fn with_cookie(mut self, name: &str, value: &str, options: Option<CookieOptions>) -> Self {
468 let opts = options.unwrap_or_default();
469 let mut cookie = format!("{}={}", name, value);
470
471 if let Some(max_age) = opts.max_age {
472 cookie.push_str(&format!("; Max-Age={}", max_age));
473 }
474 if let Some(ref path) = opts.path {
475 cookie.push_str(&format!("; Path={}", path));
476 }
477 if let Some(ref domain) = opts.domain {
478 cookie.push_str(&format!("; Domain={}", domain));
479 }
480 if opts.secure {
481 cookie.push_str("; Secure");
482 }
483 if opts.http_only {
484 cookie.push_str("; HttpOnly");
485 }
486 if let Some(ref same_site) = opts.same_site {
487 cookie.push_str(&format!("; SameSite={}", same_site));
488 }
489
490 if let Ok(value) = HeaderValue::from_str(&cookie) {
491 self.headers.append(http::header::SET_COOKIE, value);
492 }
493 self
494 }
495
496 pub fn is_success(&self) -> bool {
498 self.status.is_success()
499 }
500
501 pub fn is_client_error(&self) -> bool {
503 self.status.is_client_error()
504 }
505
506 pub fn is_server_error(&self) -> bool {
508 self.status.is_server_error()
509 }
510
511 pub fn get_header(&self, name: &str) -> Option<&str> {
513 self.headers.get(name).and_then(|v| v.to_str().ok())
514 }
515
516 pub fn json_body<T: for<'de> Deserialize<'de>>(&self) -> Result<T, serde_json::Error> {
518 serde_json::from_slice(&self.body)
519 }
520
521 pub fn text_body(&self) -> Result<String, std::string::FromUtf8Error> {
523 String::from_utf8(self.body.to_vec())
524 }
525}
526
527#[derive(Debug, Clone, Default)]
529pub struct CookieOptions {
530 pub max_age: Option<i64>,
532 pub path: Option<String>,
534 pub domain: Option<String>,
536 pub secure: bool,
538 pub http_only: bool,
540 pub same_site: Option<String>,
542}
543
544impl CookieOptions {
545 pub fn new() -> Self {
547 Self::default()
548 }
549
550 pub fn max_age(mut self, seconds: i64) -> Self {
552 self.max_age = Some(seconds);
553 self
554 }
555
556 pub fn path(mut self, path: impl Into<String>) -> Self {
558 self.path = Some(path.into());
559 self
560 }
561
562 pub fn domain(mut self, domain: impl Into<String>) -> Self {
564 self.domain = Some(domain.into());
565 self
566 }
567
568 pub fn secure(mut self) -> Self {
570 self.secure = true;
571 self
572 }
573
574 pub fn http_only(mut self) -> Self {
576 self.http_only = true;
577 self
578 }
579
580 pub fn same_site_strict(mut self) -> Self {
582 self.same_site = Some("Strict".to_string());
583 self
584 }
585
586 pub fn same_site_lax(mut self) -> Self {
588 self.same_site = Some("Lax".to_string());
589 self
590 }
591
592 pub fn same_site_none(mut self) -> Self {
594 self.same_site = Some("None".to_string());
595 self.secure = true; self
597 }
598}
599
600#[cfg(test)]
601mod tests {
602 use super::*;
603 use serde::ser::Error as _;
604
605 #[test]
606 fn test_mock_request_get() {
607 let request = MockHttpRequest::get("/api/users");
608 assert_eq!(request.method, Method::GET);
609 assert_eq!(request.path(), "/api/users");
610 }
611
612 #[test]
613 fn test_mock_request_post_json() {
614 #[derive(Serialize)]
615 struct Input {
616 name: String,
617 }
618
619 let request = MockHttpRequest::post("/api/users").with_json(&Input {
620 name: "Alice".to_string(),
621 });
622
623 assert_eq!(request.method, Method::POST);
624 assert_eq!(request.get_header("content-type"), Some("application/json"));
625 assert!(request.text().unwrap().contains("Alice"));
626 }
627
628 #[test]
629 fn test_mock_request_with_headers() {
630 let request = MockHttpRequest::get("/api")
631 .with_header("X-Custom", "value")
632 .with_bearer_token("token123");
633
634 assert_eq!(request.get_header("x-custom"), Some("value"));
635 assert_eq!(request.get_header("authorization"), Some("Bearer token123"));
636 }
637
638 #[test]
639 fn test_mock_request_with_cookies() {
640 let request = MockHttpRequest::get("/api")
641 .with_cookie("session", "abc")
642 .with_cookie("user", "123");
643
644 assert_eq!(request.get_cookie("session"), Some("abc"));
645 assert_eq!(request.get_cookie("user"), Some("123"));
646 }
647
648 #[test]
649 fn test_mock_request_with_query() {
650 let request = MockHttpRequest::get("/api/search")
651 .with_query("q", "test")
652 .with_query("page", "1");
653
654 assert_eq!(request.get_query("q"), Some("test"));
655 assert_eq!(request.get_query("page"), Some("1"));
656 }
657
658 #[test]
659 fn test_mock_response_json() {
660 #[derive(Serialize, Deserialize, PartialEq, Debug)]
661 struct Output {
662 id: i32,
663 }
664
665 let response = MockHttpResponse::json(&Output { id: 1 });
666
667 assert!(response.is_success());
668 assert_eq!(
669 response.get_header("content-type"),
670 Some("application/json")
671 );
672
673 let body: Output = response.json_body().unwrap();
674 assert_eq!(body.id, 1);
675 }
676
677 #[test]
678 fn test_mock_response_with_cookie() {
679 let response = MockHttpResponse::ok().with_cookie(
680 "session",
681 "xyz",
682 Some(
683 CookieOptions::new()
684 .max_age(3600)
685 .path("/")
686 .secure()
687 .http_only(),
688 ),
689 );
690
691 let cookie = response.get_header("set-cookie").unwrap();
692 assert!(cookie.contains("session=xyz"));
693 assert!(cookie.contains("Max-Age=3600"));
694 assert!(cookie.contains("Path=/"));
695 assert!(cookie.contains("Secure"));
696 assert!(cookie.contains("HttpOnly"));
697 }
698
699 #[test]
700 fn mock_http_request_builders_preserve_method_body_auth_cookies_and_query() {
701 #[derive(Debug, Deserialize, PartialEq, Serialize)]
702 struct Input {
703 name: String,
704 count: u8,
705 }
706
707 let json_input = Input {
709 name: "Ada".into(),
710 count: 3,
711 };
712 let json = MockHttpRequest::put("/api/items?existing=first&existing=last")
713 .with_json(&json_input)
714 .with_headers([("X-Request-ID", "req-7"), ("Accept", "application/json")])
715 .with_cookies([("theme", "light"), ("session", "old")])
716 .with_cookie("session", "new")
717 .with_query_params([("page", "1"), ("filter", "ready")])
718 .with_query("page", "2")
719 .with_bearer_token("token-123");
720 let form = MockHttpRequest::patch("/api/items/7").with_form(&json_input);
721 let text = MockHttpRequest::delete("/api/items/7").with_text("remove");
722 let raw =
723 MockHttpRequest::new(Method::PUT, "/raw").with_body(Bytes::from_static(b"\x00\x01"));
724 let basic_password = std::process::id().to_string();
725 let basic = MockHttpRequest::delete("/admin")
726 .with_basic_auth("alice", &basic_password)
727 .with_content_type("application/custom")
728 .with_accept("text/plain");
729
730 let decoded_json: Input = json.json().unwrap();
732 let decoded_form: Input = form.form().unwrap();
733 let mut query: Vec<_> = json
734 .uri
735 .query()
736 .unwrap()
737 .split('&')
738 .map(|entry| entry.split_once('=').unwrap())
739 .map(|(key, value)| (key.to_string(), value.to_string()))
740 .collect();
741 query.sort_unstable();
742 let mut cookies: Vec<_> = json
743 .get_header("cookie")
744 .expect("Cookie header should contain the configured cookies")
745 .split("; ")
746 .map(|entry| {
747 entry
748 .split_once('=')
749 .expect("Cookie header entries should contain a name and value")
750 })
751 .map(|(name, value)| (name.to_string(), value.to_string()))
752 .collect();
753 cookies.sort_unstable();
754
755 assert_eq!(json.method, Method::PUT);
757 assert_eq!(json.path(), "/api/items");
758 assert_eq!(json.uri_string().starts_with("/api/items?"), true);
759 assert_eq!(decoded_json, json_input);
760 assert_eq!(json.get_header("content-type"), Some("application/json"));
761 assert_eq!(json.get_header("x-request-id"), Some("req-7"));
762 assert_eq!(json.get_header("accept"), Some("application/json"));
763 assert_eq!(json.get_header("authorization"), Some("Bearer token-123"));
764 assert_eq!(cookies.len(), 2);
765 assert_eq!(
766 cookies,
767 vec![
768 ("session".to_string(), "new".to_string()),
769 ("theme".to_string(), "light".to_string()),
770 ]
771 );
772 assert_eq!(json.get_cookie("theme"), Some("light"));
773 assert_eq!(json.get_cookie("session"), Some("new"));
774 assert_eq!(json.query_params.get("existing"), Some(&"last".to_string()));
775 assert_eq!(json.query_params.get("page"), Some(&"2".to_string()));
776 assert_eq!(json.query_params.get("filter"), Some(&"ready".to_string()));
777 assert_eq!(query.len(), 3);
778 assert_eq!(
779 query,
780 vec![
781 ("existing".to_string(), "last".to_string()),
782 ("filter".to_string(), "ready".to_string()),
783 ("page".to_string(), "2".to_string()),
784 ]
785 );
786 assert_eq!(form.method, Method::PATCH);
787 assert_eq!(
788 form.get_header("content-type"),
789 Some("application/x-www-form-urlencoded")
790 );
791 assert_eq!(
792 decoded_form,
793 Input {
794 name: "Ada".into(),
795 count: 3
796 }
797 );
798 assert_eq!(text.method, Method::DELETE);
799 assert_eq!(text.get_header("content-type"), Some("text/plain"));
800 assert_eq!(text.text().unwrap(), "remove");
801 assert_eq!(raw.body, Bytes::from_static(b"\x00\x01"));
802 let encoded_basic_authorization = basic
803 .get_header("authorization")
804 .unwrap()
805 .strip_prefix("Basic ")
806 .unwrap();
807 let decoded_basic_authorization = base64_simd::STANDARD
808 .decode_to_vec(encoded_basic_authorization)
809 .unwrap();
810 assert_eq!(
811 String::from_utf8(decoded_basic_authorization).unwrap(),
812 format!("alice:{}", basic_password)
813 );
814 assert_eq!(basic.get_header("content-type"), Some("application/custom"));
815 assert_eq!(basic.get_header("accept"), Some("text/plain"));
816 }
817
818 #[test]
819 fn mock_http_response_builders_preserve_status_body_headers_and_cookie_policy() {
820 #[derive(Debug, Deserialize, PartialEq, Serialize)]
821 struct Output {
822 id: u8,
823 }
824
825 struct FailingSerialize;
826
827 impl Serialize for FailingSerialize {
828 fn serialize<S>(&self, _serializer: S) -> Result<S::Ok, S::Error>
829 where
830 S: serde::Serializer,
831 {
832 Err(S::Error::custom("cannot serialize"))
833 }
834 }
835
836 let statuses = [
838 MockHttpResponse::ok().status,
839 MockHttpResponse::created().status,
840 MockHttpResponse::no_content().status,
841 MockHttpResponse::bad_request().status,
842 MockHttpResponse::unauthorized().status,
843 MockHttpResponse::forbidden().status,
844 MockHttpResponse::not_found().status,
845 MockHttpResponse::internal_error().status,
846 ];
847 let response = MockHttpResponse::created()
848 .with_json(&Output { id: 7 })
849 .with_header("X-Trace", "trace-9")
850 .with_cookie(
851 "strict",
852 "one",
853 Some(
854 CookieOptions::new()
855 .max_age(60)
856 .path("/")
857 .http_only()
858 .same_site_strict(),
859 ),
860 )
861 .with_cookie(
862 "lax",
863 "two",
864 Some(CookieOptions::new().domain("example.test").same_site_lax()),
865 )
866 .with_cookie("none", "three", Some(CookieOptions::new().same_site_none()));
867 let unchanged = MockHttpResponse::text("keep").with_json(&FailingSerialize);
868 let failed_new = MockHttpResponse::json(&FailingSerialize);
869
870 let decoded: Output = response.json_body().unwrap();
872 let cookies: Vec<_> = response
873 .headers
874 .get_all(http::header::SET_COOKIE)
875 .iter()
876 .map(|value| value.to_str().unwrap().to_string())
877 .collect();
878
879 assert_eq!(
881 statuses,
882 [
883 StatusCode::OK,
884 StatusCode::CREATED,
885 StatusCode::NO_CONTENT,
886 StatusCode::BAD_REQUEST,
887 StatusCode::UNAUTHORIZED,
888 StatusCode::FORBIDDEN,
889 StatusCode::NOT_FOUND,
890 StatusCode::INTERNAL_SERVER_ERROR,
891 ]
892 );
893 assert_eq!(response.status, StatusCode::CREATED);
894 assert_eq!(response.is_success(), true);
895 assert_eq!(response.is_client_error(), false);
896 assert_eq!(response.is_server_error(), false);
897 assert_eq!(decoded, Output { id: 7 });
898 assert_eq!(
899 response.get_header("content-type"),
900 Some("application/json")
901 );
902 assert_eq!(response.get_header("x-trace"), Some("trace-9"));
903 assert_eq!(
904 cookies,
905 vec![
906 "strict=one; Max-Age=60; Path=/; HttpOnly; SameSite=Strict",
907 "lax=two; Domain=example.test; SameSite=Lax",
908 "none=three; Secure; SameSite=None",
909 ]
910 );
911 assert_eq!(MockHttpResponse::bad_request().is_client_error(), true);
912 assert_eq!(MockHttpResponse::internal_error().is_server_error(), true);
913 assert_eq!(unchanged.status, StatusCode::OK);
914 assert_eq!(unchanged.text_body().unwrap(), "keep");
915 assert_eq!(unchanged.get_header("content-type"), Some("text/plain"));
916 assert_eq!(failed_new.body, Bytes::new());
917 assert_eq!(failed_new.headers.len(), 0);
918 }
919}