switchy_http/
simulator.rs1use std::{collections::BTreeMap, marker::PhantomData};
22
23use async_trait::async_trait;
24use bytes::Bytes;
25
26use crate::{
27 Error, GenericClient, GenericClientBuilder, GenericRequestBuilder, GenericResponse, Method,
28 StatusCode,
29};
30
31#[derive(Default)]
36pub struct Client;
37
38impl Client {
39 #[must_use]
41 pub const fn new() -> Self {
42 Self
43 }
44}
45
46impl GenericClient<crate::SimulatorRequestBuilder> for Client {
47 fn request(&self, _method: Method, _url: &str) -> crate::SimulatorRequestBuilder {
48 crate::RequestBuilderWrapper(RequestBuilder, PhantomData)
49 }
50}
51
52pub struct ClientBuilder;
57
58impl crate::SimulatorClientBuilder {
59 #[must_use]
61 pub const fn new() -> Self {
62 Self(ClientBuilder, PhantomData, PhantomData)
63 }
64}
65
66impl GenericClientBuilder<crate::SimulatorRequestBuilder, crate::SimulatorClient>
67 for ClientBuilder
68{
69 fn build(self) -> Result<crate::SimulatorClient, Error> {
70 Ok(crate::ClientWrapper(Client, PhantomData))
71 }
72}
73
74pub struct RequestBuilder;
79
80#[async_trait]
81impl GenericRequestBuilder<crate::SimulatorResponse> for RequestBuilder {
82 fn header(&mut self, _name: &str, _value: &str) {}
83
84 fn query_param(&mut self, _name: &str, _value: &str) {}
85
86 fn query_param_opt(&mut self, _name: &str, _value: Option<&str>) {}
87
88 fn query_params(&mut self, _params: &[(&str, &str)]) {}
89
90 fn body(&mut self, _body: Bytes) {}
91
92 #[cfg(feature = "json")]
93 fn form(&mut self, _form: &serde_json::Value) {}
94
95 async fn send(&mut self) -> Result<crate::SimulatorResponse, Error> {
96 Ok(crate::ResponseWrapper(Response::default()))
97 }
98}
99
100#[derive(Default)]
104pub struct Response {
105 headers: BTreeMap<String, String>,
106}
107
108#[async_trait]
109impl GenericResponse for Response {
110 fn status(&self) -> StatusCode {
111 StatusCode::Ok
112 }
113
114 fn headers(&mut self) -> &BTreeMap<String, String> {
115 &self.headers
116 }
117
118 async fn text(&mut self) -> Result<String, Error> {
119 Ok(String::new())
120 }
121
122 async fn bytes(&mut self) -> Result<Bytes, Error> {
123 Ok(Bytes::new())
124 }
125
126 #[cfg(feature = "stream")]
127 fn bytes_stream(
128 &mut self,
129 ) -> std::pin::Pin<Box<dyn futures_core::Stream<Item = Result<Bytes, Error>> + Send>> {
130 Box::pin(futures_util::stream::empty())
131 }
132}
133
134#[cfg(test)]
135mod tests {
136 use super::*;
137
138 #[test_log::test]
139 fn test_simulator_client_builder_succeeds() {
140 let builder = ClientBuilder;
141 let result =
142 GenericClientBuilder::<crate::SimulatorRequestBuilder, crate::SimulatorClient>::build(
143 builder,
144 );
145 assert!(result.is_ok());
146 }
147
148 #[test_log::test]
149 fn test_simulator_response_returns_ok_status() {
150 let response = Response::default();
151 assert_eq!(response.status(), StatusCode::Ok);
152 }
153
154 #[test_log::test]
155 fn test_simulator_response_returns_empty_headers() {
156 let mut response = Response::default();
157 let headers = response.headers();
158 assert!(headers.is_empty());
159 }
160
161 #[test_log::test]
162 fn test_simulator_client_creates_request_builder() {
163 let client = Client::new();
164 let _builder = client.request(Method::Get, "http://example.com");
165 }
167
168 #[test_log::test(switchy_async::test)]
172 async fn test_simulator_full_request_response_flow() {
173 let client = crate::SimulatorClient::new();
174
175 let response = client
177 .get("http://example.com/test")
178 .header("Authorization", "Bearer token")
179 .query_param("key", "value")
180 .query_param_opt("optional", Some("present"))
181 .query_param_opt("missing", None)
182 .query_params(&[("page", "1"), ("limit", "10")])
183 .send()
184 .await
185 .unwrap();
186
187 assert_eq!(response.status(), StatusCode::Ok);
189
190 let text = response.text().await.unwrap();
192 assert!(text.is_empty());
193 }
194
195 #[test_log::test(switchy_async::test)]
197 async fn test_simulator_all_http_methods() {
198 let client = crate::SimulatorClient::new();
199
200 let get = client.get("http://example.com").send().await.unwrap();
202 assert_eq!(get.status(), StatusCode::Ok);
203
204 let post = client.post("http://example.com").send().await.unwrap();
205 assert_eq!(post.status(), StatusCode::Ok);
206
207 let put = client.put("http://example.com").send().await.unwrap();
208 assert_eq!(put.status(), StatusCode::Ok);
209
210 let patch = client.patch("http://example.com").send().await.unwrap();
211 assert_eq!(patch.status(), StatusCode::Ok);
212
213 let delete = client.delete("http://example.com").send().await.unwrap();
214 assert_eq!(delete.status(), StatusCode::Ok);
215
216 let head = client.head("http://example.com").send().await.unwrap();
217 assert_eq!(head.status(), StatusCode::Ok);
218
219 let options = client.options("http://example.com").send().await.unwrap();
220 assert_eq!(options.status(), StatusCode::Ok);
221 }
222
223 #[cfg(feature = "json")]
226 #[test_log::test(switchy_async::test)]
227 async fn test_simulator_json_body_serialization() {
228 {
229 #[derive(serde::Serialize)]
230 struct TestPayload {
231 name: String,
232 value: i32,
233 }
234
235 let client = crate::SimulatorClient::new();
236
237 let payload = TestPayload {
238 name: "test".to_string(),
239 value: 42,
240 };
241
242 let response = client
244 .post("http://example.com/api")
245 .json(&payload)
246 .send()
247 .await
248 .unwrap();
249
250 assert_eq!(response.status(), StatusCode::Ok);
251 }
252 }
253
254 #[cfg(feature = "json")]
257 #[test_log::test(switchy_async::test)]
258 async fn test_simulator_form_body_serialization() {
259 {
260 #[derive(serde::Serialize)]
261 struct FormData {
262 username: String,
263 password: String,
264 }
265
266 let client = crate::SimulatorClient::new();
267
268 let form = FormData {
269 username: "user".to_string(),
270 password: "pass".to_string(),
271 };
272
273 let response = client
275 .post("http://example.com/login")
276 .form(&form)
277 .send()
278 .await
279 .unwrap();
280
281 assert_eq!(response.status(), StatusCode::Ok);
282 }
283 }
284
285 #[cfg(feature = "stream")]
287 #[test_log::test(switchy_async::test)]
288 async fn test_simulator_bytes_stream_consumption() {
289 {
290 use futures_util::StreamExt;
291
292 let client = crate::SimulatorClient::new();
293 let response = client.get("http://example.com").send().await.unwrap();
294
295 let mut stream = response.bytes_stream();
297 let chunks: Vec<_> = stream.by_ref().collect().await;
298 assert!(chunks.is_empty());
299 }
300 }
301
302 #[test_log::test(switchy_async::test)]
305 async fn test_simulator_response_bytes() {
306 {
307 use crate::GenericResponse;
308
309 let mut response = Response::default();
310 let bytes = response.bytes().await.unwrap();
311 assert!(bytes.is_empty());
312 }
313 }
314
315 #[test_log::test(switchy_async::test)]
318 async fn test_simulator_response_text() {
319 {
320 use crate::GenericResponse;
321
322 let mut response = Response::default();
323 let text = response.text().await.unwrap();
324 assert!(text.is_empty());
325 }
326 }
327
328 #[test_log::test(switchy_async::test)]
331 async fn test_simulator_request_raw_body() {
332 let client = crate::SimulatorClient::new();
333
334 let body_bytes = Bytes::from_static(b"raw request body content");
335
336 let response = client
337 .post("http://example.com/upload")
338 .body(body_bytes)
339 .send()
340 .await
341 .unwrap();
342
343 assert_eq!(response.status(), StatusCode::Ok);
344 }
345
346 #[test_log::test(switchy_async::test)]
349 async fn test_simulator_client_default() {
350 let client = crate::SimulatorClient::default();
351
352 let response = client.get("http://example.com").send().await.unwrap();
353 assert_eq!(response.status(), StatusCode::Ok);
354 }
355
356 #[test_log::test(switchy_async::test)]
358 async fn test_simulator_response_headers_through_wrapper() {
359 let client = crate::SimulatorClient::new();
360
361 let mut response = client.get("http://example.com").send().await.unwrap();
362
363 let headers = response.headers();
365 assert!(headers.is_empty());
366 }
367
368 #[cfg(feature = "stream")]
370 #[test_log::test(switchy_async::test)]
371 async fn test_simulator_response_bytes_stream_trait() {
372 {
373 use crate::GenericResponse;
374 use futures_util::StreamExt;
375
376 let mut response = Response::default();
377
378 let mut stream = response.bytes_stream();
379 let chunks: Vec<_> = stream.by_ref().collect().await;
380 assert!(chunks.is_empty());
381 }
382 }
383
384 #[test_log::test(switchy_async::test)]
387 async fn test_simulator_client_request_with_different_methods() {
388 let client = Client::new();
389
390 let methods = [
392 Method::Get,
393 Method::Post,
394 Method::Put,
395 Method::Patch,
396 Method::Delete,
397 Method::Head,
398 Method::Options,
399 ];
400
401 for method in methods {
402 let _builder = client.request(method, "http://example.com");
403 }
405 }
406
407 #[cfg(feature = "json")]
410 #[test_log::test(switchy_async::test)]
411 async fn test_simulator_response_json_deserialization_empty() {
412 let client = crate::SimulatorClient::new();
413
414 let response = client.get("http://example.com/api").send().await.unwrap();
415
416 let result: Result<serde_json::Value, _> = response.json().await;
420 assert!(result.is_err());
421 }
422}