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 request(
45 &self,
46 method: HttpMethod,
47 path: &str,
48 body: Option<Vec<u8>>,
49 ) -> TestResponse {
50 let mut req = HttpRequest::new(method.as_str().to_string(), path.to_string());
51 req.body = body.unwrap_or_default();
52
53 match self.router.route(req).await {
55 Ok(response) => TestResponse::Success(response),
56 Err(error) => TestResponse::Error(error),
57 }
58 }
59}
60
61#[allow(dead_code)]
63pub struct TestRequestBuilder {
64 method: HttpMethod,
65 path: String,
66 headers: HashMap<String, String>,
67 body: Vec<u8>,
68 query_params: HashMap<String, String>,
69}
70
71impl TestRequestBuilder {
72 #[allow(dead_code)]
74 pub fn new(method: HttpMethod, path: &str) -> Self {
75 Self {
76 method,
77 path: path.to_string(),
78 headers: HashMap::new(),
79 body: Vec::new(),
80 query_params: HashMap::new(),
81 }
82 }
83
84 #[allow(dead_code)]
86 pub fn header(mut self, key: &str, value: &str) -> Self {
87 self.headers.insert(key.to_string(), value.to_string());
88 self
89 }
90
91 #[allow(dead_code)]
93 pub fn body(mut self, body: Vec<u8>) -> Self {
94 self.body = body;
95 self
96 }
97
98 #[allow(dead_code)]
100 pub fn json<T: serde::Serialize>(mut self, data: &T) -> Result<Self, Error> {
101 self.body = serde_json::to_vec(data).map_err(|e| Error::Serialization(e.to_string()))?;
102 self.headers
103 .insert("Content-Type".to_string(), "application/json".to_string());
104 Ok(self)
105 }
106
107 #[allow(dead_code)]
109 pub fn query(mut self, key: &str, value: &str) -> Self {
110 self.query_params.insert(key.to_string(), value.to_string());
111 self
112 }
113
114 #[allow(dead_code)]
116 pub fn build(self) -> HttpRequest {
117 let query_string = if !self.query_params.is_empty() {
119 let params: Vec<String> = self
120 .query_params
121 .iter()
122 .map(|(k, v)| format!("{}={}", k, v))
123 .collect();
124 format!("?{}", params.join("&"))
125 } else {
126 String::new()
127 };
128
129 HttpRequest::from_parts(
130 self.method.as_str().to_string(),
131 format!("{}{}", self.path, query_string),
132 self.headers,
133 self.body,
134 HashMap::new(),
135 self.query_params,
136 )
137 }
138}
139
140#[derive(Debug)]
142pub enum TestResponse {
143 Success(HttpResponse),
144 Error(Error),
145}
146
147impl TestResponse {
148 pub fn assert_success(&self) -> &HttpResponse {
150 match self {
151 TestResponse::Success(response) => response,
152 TestResponse::Error(error) => {
153 panic!("Expected success response, got error: {:?}", error)
154 }
155 }
156 }
157
158 pub fn assert_error(&self) -> &Error {
160 match self {
161 TestResponse::Error(error) => error,
162 TestResponse::Success(_) => {
163 panic!("Expected error response, got success")
164 }
165 }
166 }
167
168 pub fn status(&self) -> Option<u16> {
170 match self {
171 TestResponse::Success(response) => Some(response.status),
172 TestResponse::Error(_) => None,
173 }
174 }
175
176 pub fn body_string(&self) -> Option<String> {
178 match self {
179 TestResponse::Success(response) => String::from_utf8(response.body.clone()).ok(),
180 TestResponse::Error(_) => None,
181 }
182 }
183
184 pub fn body_json<T: serde::de::DeserializeOwned>(&self) -> Result<T, String> {
186 match self {
187 TestResponse::Success(response) => serde_json::from_slice(&response.body)
188 .map_err(|e| format!("Serialization error: {}", e)),
189 TestResponse::Error(error) => Err(format!("{:?}", error)),
190 }
191 }
192
193 pub fn header(&self, key: &str) -> Option<&String> {
195 match self {
196 TestResponse::Success(response) => response.headers.get(key),
197 TestResponse::Error(_) => None,
198 }
199 }
200}
201
202#[cfg(test)]
203mod tests {
204 use super::*;
205
206 #[test]
207 fn test_request_builder() {
208 let req = TestRequestBuilder::new(HttpMethod::GET, "/test")
209 .header("Authorization", "Bearer token")
210 .query("foo", "bar")
211 .build();
212
213 assert_eq!(req.method, "GET");
214 assert!(req.path.contains("/test"));
215 assert_eq!(
216 req.headers.get("Authorization"),
217 Some(&"Bearer token".to_string())
218 );
219 }
220}