1use armature_core::{Error, HttpMethod, HttpRequest, HttpResponse, Router};
4use std::collections::HashMap;
5use std::sync::Arc;
6
7pub struct TestClient {
9 router: Arc<Router>,
10}
11
12impl TestClient {
13 pub fn new(router: Arc<Router>) -> Self {
15 Self { router }
16 }
17
18 pub async fn get(&self, path: &str) -> TestResponse {
20 self.request(HttpMethod::GET, path, None).await
21 }
22
23 pub async fn post(&self, path: &str, body: Vec<u8>) -> TestResponse {
25 self.request(HttpMethod::POST, path, Some(body)).await
26 }
27
28 pub async fn put(&self, path: &str, body: Vec<u8>) -> TestResponse {
30 self.request(HttpMethod::PUT, path, Some(body)).await
31 }
32
33 pub async fn delete(&self, path: &str) -> TestResponse {
35 self.request(HttpMethod::DELETE, path, None).await
36 }
37
38 pub async fn patch(&self, path: &str, body: Vec<u8>) -> TestResponse {
40 self.request(HttpMethod::PATCH, path, Some(body)).await
41 }
42
43 pub async fn query(&self, path: &str, body: Vec<u8>) -> TestResponse {
46 self.request(HttpMethod::QUERY, path, Some(body)).await
47 }
48
49 pub async fn request(
51 &self,
52 method: HttpMethod,
53 path: &str,
54 body: Option<Vec<u8>>,
55 ) -> TestResponse {
56 let mut req = HttpRequest::new(method.as_str().to_string(), path.to_string());
57 req.body = body.unwrap_or_default();
58
59 match self.router.route(req).await {
61 Ok(response) => TestResponse::Success(response),
62 Err(error) => TestResponse::Error(error),
63 }
64 }
65}
66
67#[allow(dead_code)]
69pub struct TestRequestBuilder {
70 method: HttpMethod,
71 path: String,
72 headers: HashMap<String, String>,
73 body: Vec<u8>,
74 query_params: HashMap<String, String>,
75}
76
77impl TestRequestBuilder {
78 #[allow(dead_code)]
80 pub fn new(method: HttpMethod, path: &str) -> Self {
81 Self {
82 method,
83 path: path.to_string(),
84 headers: HashMap::new(),
85 body: Vec::new(),
86 query_params: HashMap::new(),
87 }
88 }
89
90 #[allow(dead_code)]
92 pub fn header(mut self, key: &str, value: &str) -> Self {
93 self.headers.insert(key.to_string(), value.to_string());
94 self
95 }
96
97 #[allow(dead_code)]
99 pub fn body(mut self, body: Vec<u8>) -> Self {
100 self.body = body;
101 self
102 }
103
104 #[allow(dead_code)]
106 pub fn json<T: serde::Serialize>(mut self, data: &T) -> Result<Self, Error> {
107 self.body = serde_json::to_vec(data).map_err(|e| Error::Serialization(e.to_string()))?;
108 self.headers
109 .insert("Content-Type".to_string(), "application/json".to_string());
110 Ok(self)
111 }
112
113 #[allow(dead_code)]
115 pub fn query(mut self, key: &str, value: &str) -> Self {
116 self.query_params.insert(key.to_string(), value.to_string());
117 self
118 }
119
120 #[allow(dead_code)]
122 pub fn build(self) -> HttpRequest {
123 let query_string = if !self.query_params.is_empty() {
125 let params: Vec<String> = self
126 .query_params
127 .iter()
128 .map(|(k, v)| format!("{}={}", k, v))
129 .collect();
130 format!("?{}", params.join("&"))
131 } else {
132 String::new()
133 };
134
135 HttpRequest::from_parts(
136 self.method.as_str().to_string(),
137 format!("{}{}", self.path, query_string),
138 self.headers,
139 self.body,
140 HashMap::new(),
141 self.query_params,
142 )
143 }
144}
145
146#[derive(Debug)]
148pub enum TestResponse {
149 Success(HttpResponse),
150 Error(Error),
151}
152
153impl TestResponse {
154 pub fn assert_success(&self) -> &HttpResponse {
156 match self {
157 TestResponse::Success(response) => response,
158 TestResponse::Error(error) => {
159 panic!("Expected success response, got error: {:?}", error)
160 }
161 }
162 }
163
164 pub fn assert_error(&self) -> &Error {
166 match self {
167 TestResponse::Error(error) => error,
168 TestResponse::Success(_) => {
169 panic!("Expected error response, got success")
170 }
171 }
172 }
173
174 pub fn status(&self) -> Option<u16> {
176 match self {
177 TestResponse::Success(response) => Some(response.status),
178 TestResponse::Error(_) => None,
179 }
180 }
181
182 pub fn body_string(&self) -> Option<String> {
184 match self {
185 TestResponse::Success(response) => String::from_utf8(response.body.clone()).ok(),
186 TestResponse::Error(_) => None,
187 }
188 }
189
190 pub fn body_json<T: serde::de::DeserializeOwned>(&self) -> Result<T, String> {
192 match self {
193 TestResponse::Success(response) => serde_json::from_slice(&response.body)
194 .map_err(|e| format!("Serialization error: {}", e)),
195 TestResponse::Error(error) => Err(format!("{:?}", error)),
196 }
197 }
198
199 pub fn header(&self, key: &str) -> Option<&String> {
201 match self {
202 TestResponse::Success(response) => response.headers.get(key),
203 TestResponse::Error(_) => None,
204 }
205 }
206}
207
208#[cfg(test)]
209mod tests {
210 use super::*;
211
212 #[test]
213 fn test_request_builder() {
214 let req = TestRequestBuilder::new(HttpMethod::GET, "/test")
215 .header("Authorization", "Bearer token")
216 .query("foo", "bar")
217 .build();
218
219 assert_eq!(req.method, "GET");
220 assert!(req.path.contains("/test"));
221 assert_eq!(
222 req.headers.get("Authorization"),
223 Some(&"Bearer token".to_string())
224 );
225 }
226}