1use std::convert::Infallible;
4
5use anvil_core::Application;
6use axum::body::Body;
7use axum::Router;
8use http::{Method, Request, StatusCode};
9use http_body_util::BodyExt;
10use serde::de::DeserializeOwned;
11use tower::ServiceExt;
12
13pub struct TestClient {
14 router: Router,
15}
16
17impl TestClient {
18 pub async fn new(app: Application) -> Self {
19 Self {
20 router: app.into_router(),
21 }
22 }
23
24 pub fn from_router(router: Router) -> Self {
25 Self { router }
26 }
27
28 pub async fn get(&self, path: &str) -> TestResponse {
29 self.request(Method::GET, path, None).await
30 }
31
32 pub async fn post(&self, path: &str, body: serde_json::Value) -> TestResponse {
33 self.request(Method::POST, path, Some(body)).await
34 }
35
36 pub async fn put(&self, path: &str, body: serde_json::Value) -> TestResponse {
37 self.request(Method::PUT, path, Some(body)).await
38 }
39
40 pub async fn delete(&self, path: &str) -> TestResponse {
41 self.request(Method::DELETE, path, None).await
42 }
43
44 async fn request(
45 &self,
46 method: Method,
47 path: &str,
48 body: Option<serde_json::Value>,
49 ) -> TestResponse {
50 let mut req = Request::builder().method(method).uri(path);
51 let body = match body {
52 Some(v) => {
53 req = req.header("content-type", "application/json");
54 Body::from(serde_json::to_vec(&v).unwrap())
55 }
56 None => Body::empty(),
57 };
58 let response = self
59 .router
60 .clone()
61 .oneshot(req.body(body).unwrap())
62 .await
63 .unwrap();
64
65 let status = response.status();
66 let bytes = response
67 .into_body()
68 .collect()
69 .await
70 .map(|c| c.to_bytes())
71 .unwrap_or_default();
72
73 TestResponse {
74 status,
75 body: bytes.to_vec(),
76 }
77 }
78}
79
80pub struct TestResponse {
81 pub status: StatusCode,
82 pub body: Vec<u8>,
83}
84
85impl TestResponse {
86 pub fn assert_status(&self, expected: u16) -> &Self {
87 assert_eq!(
88 self.status.as_u16(),
89 expected,
90 "expected status {expected}, got {} — body: {}",
91 self.status,
92 self.body_text()
93 );
94 self
95 }
96
97 pub fn assert_ok(&self) -> &Self {
98 assert!(
99 self.status.is_success(),
100 "expected success, got {} — body: {}",
101 self.status,
102 self.body_text()
103 );
104 self
105 }
106
107 pub fn body_text(&self) -> String {
108 String::from_utf8_lossy(&self.body).to_string()
109 }
110
111 pub fn json<T: DeserializeOwned>(&self) -> T {
112 serde_json::from_slice(&self.body).expect("response was not valid JSON")
113 }
114
115 pub fn assert_contains(&self, needle: &str) -> &Self {
116 let body = self.body_text();
117 assert!(
118 body.contains(needle),
119 "expected response body to contain '{needle}', got: {body}"
120 );
121 self
122 }
123}
124
125fn _force_link() {
127 let _ = std::any::type_name::<Infallible>();
128}