use std::{collections::BTreeMap, marker::PhantomData};
use async_trait::async_trait;
use bytes::Bytes;
use crate::{
Error, GenericClient, GenericClientBuilder, GenericRequestBuilder, GenericResponse, Method,
StatusCode,
};
#[derive(Default)]
pub struct Client;
impl Client {
#[must_use]
pub const fn new() -> Self {
Self
}
}
impl GenericClient<crate::SimulatorRequestBuilder> for Client {
fn request(&self, _method: Method, _url: &str) -> crate::SimulatorRequestBuilder {
crate::RequestBuilderWrapper(RequestBuilder, PhantomData)
}
}
pub struct ClientBuilder;
impl crate::SimulatorClientBuilder {
#[must_use]
pub const fn new() -> Self {
Self(ClientBuilder, PhantomData, PhantomData)
}
}
impl GenericClientBuilder<crate::SimulatorRequestBuilder, crate::SimulatorClient>
for ClientBuilder
{
fn build(self) -> Result<crate::SimulatorClient, Error> {
Ok(crate::ClientWrapper(Client, PhantomData))
}
}
pub struct RequestBuilder;
#[async_trait]
impl GenericRequestBuilder<crate::SimulatorResponse> for RequestBuilder {
fn header(&mut self, _name: &str, _value: &str) {}
fn query_param(&mut self, _name: &str, _value: &str) {}
fn query_param_opt(&mut self, _name: &str, _value: Option<&str>) {}
fn query_params(&mut self, _params: &[(&str, &str)]) {}
fn body(&mut self, _body: Bytes) {}
#[cfg(feature = "json")]
fn form(&mut self, _form: &serde_json::Value) {}
async fn send(&mut self) -> Result<crate::SimulatorResponse, Error> {
Ok(crate::ResponseWrapper(Response::default()))
}
}
#[derive(Default)]
pub struct Response {
headers: BTreeMap<String, String>,
}
#[async_trait]
impl GenericResponse for Response {
fn status(&self) -> StatusCode {
StatusCode::Ok
}
fn headers(&mut self) -> &BTreeMap<String, String> {
&self.headers
}
async fn text(&mut self) -> Result<String, Error> {
Ok(String::new())
}
async fn bytes(&mut self) -> Result<Bytes, Error> {
Ok(Bytes::new())
}
#[cfg(feature = "stream")]
fn bytes_stream(
&mut self,
) -> std::pin::Pin<Box<dyn futures_core::Stream<Item = Result<Bytes, Error>> + Send>> {
Box::pin(futures_util::stream::empty())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test_log::test]
fn test_simulator_client_builder_succeeds() {
let builder = ClientBuilder;
let result =
GenericClientBuilder::<crate::SimulatorRequestBuilder, crate::SimulatorClient>::build(
builder,
);
assert!(result.is_ok());
}
#[test_log::test]
fn test_simulator_response_returns_ok_status() {
let response = Response::default();
assert_eq!(response.status(), StatusCode::Ok);
}
#[test_log::test]
fn test_simulator_response_returns_empty_headers() {
let mut response = Response::default();
let headers = response.headers();
assert!(headers.is_empty());
}
#[test_log::test]
fn test_simulator_client_creates_request_builder() {
let client = Client::new();
let _builder = client.request(Method::Get, "http://example.com");
}
#[test_log::test(switchy_async::test)]
async fn test_simulator_full_request_response_flow() {
let client = crate::SimulatorClient::new();
let response = client
.get("http://example.com/test")
.header("Authorization", "Bearer token")
.query_param("key", "value")
.query_param_opt("optional", Some("present"))
.query_param_opt("missing", None)
.query_params(&[("page", "1"), ("limit", "10")])
.send()
.await
.unwrap();
assert_eq!(response.status(), StatusCode::Ok);
let text = response.text().await.unwrap();
assert!(text.is_empty());
}
#[test_log::test(switchy_async::test)]
async fn test_simulator_all_http_methods() {
let client = crate::SimulatorClient::new();
let get = client.get("http://example.com").send().await.unwrap();
assert_eq!(get.status(), StatusCode::Ok);
let post = client.post("http://example.com").send().await.unwrap();
assert_eq!(post.status(), StatusCode::Ok);
let put = client.put("http://example.com").send().await.unwrap();
assert_eq!(put.status(), StatusCode::Ok);
let patch = client.patch("http://example.com").send().await.unwrap();
assert_eq!(patch.status(), StatusCode::Ok);
let delete = client.delete("http://example.com").send().await.unwrap();
assert_eq!(delete.status(), StatusCode::Ok);
let head = client.head("http://example.com").send().await.unwrap();
assert_eq!(head.status(), StatusCode::Ok);
let options = client.options("http://example.com").send().await.unwrap();
assert_eq!(options.status(), StatusCode::Ok);
}
#[cfg(feature = "json")]
#[test_log::test(switchy_async::test)]
async fn test_simulator_json_body_serialization() {
{
#[derive(serde::Serialize)]
struct TestPayload {
name: String,
value: i32,
}
let client = crate::SimulatorClient::new();
let payload = TestPayload {
name: "test".to_string(),
value: 42,
};
let response = client
.post("http://example.com/api")
.json(&payload)
.send()
.await
.unwrap();
assert_eq!(response.status(), StatusCode::Ok);
}
}
#[cfg(feature = "json")]
#[test_log::test(switchy_async::test)]
async fn test_simulator_form_body_serialization() {
{
#[derive(serde::Serialize)]
struct FormData {
username: String,
password: String,
}
let client = crate::SimulatorClient::new();
let form = FormData {
username: "user".to_string(),
password: "pass".to_string(),
};
let response = client
.post("http://example.com/login")
.form(&form)
.send()
.await
.unwrap();
assert_eq!(response.status(), StatusCode::Ok);
}
}
#[cfg(feature = "stream")]
#[test_log::test(switchy_async::test)]
async fn test_simulator_bytes_stream_consumption() {
{
use futures_util::StreamExt;
let client = crate::SimulatorClient::new();
let response = client.get("http://example.com").send().await.unwrap();
let mut stream = response.bytes_stream();
let chunks: Vec<_> = stream.by_ref().collect().await;
assert!(chunks.is_empty());
}
}
#[test_log::test(switchy_async::test)]
async fn test_simulator_response_bytes() {
{
use crate::GenericResponse;
let mut response = Response::default();
let bytes = response.bytes().await.unwrap();
assert!(bytes.is_empty());
}
}
#[test_log::test(switchy_async::test)]
async fn test_simulator_response_text() {
{
use crate::GenericResponse;
let mut response = Response::default();
let text = response.text().await.unwrap();
assert!(text.is_empty());
}
}
#[test_log::test(switchy_async::test)]
async fn test_simulator_request_raw_body() {
let client = crate::SimulatorClient::new();
let body_bytes = Bytes::from_static(b"raw request body content");
let response = client
.post("http://example.com/upload")
.body(body_bytes)
.send()
.await
.unwrap();
assert_eq!(response.status(), StatusCode::Ok);
}
#[test_log::test(switchy_async::test)]
async fn test_simulator_client_default() {
let client = crate::SimulatorClient::default();
let response = client.get("http://example.com").send().await.unwrap();
assert_eq!(response.status(), StatusCode::Ok);
}
#[test_log::test(switchy_async::test)]
async fn test_simulator_response_headers_through_wrapper() {
let client = crate::SimulatorClient::new();
let mut response = client.get("http://example.com").send().await.unwrap();
let headers = response.headers();
assert!(headers.is_empty());
}
#[cfg(feature = "stream")]
#[test_log::test(switchy_async::test)]
async fn test_simulator_response_bytes_stream_trait() {
{
use crate::GenericResponse;
use futures_util::StreamExt;
let mut response = Response::default();
let mut stream = response.bytes_stream();
let chunks: Vec<_> = stream.by_ref().collect().await;
assert!(chunks.is_empty());
}
}
#[test_log::test(switchy_async::test)]
async fn test_simulator_client_request_with_different_methods() {
let client = Client::new();
let methods = [
Method::Get,
Method::Post,
Method::Put,
Method::Patch,
Method::Delete,
Method::Head,
Method::Options,
];
for method in methods {
let _builder = client.request(method, "http://example.com");
}
}
#[cfg(feature = "json")]
#[test_log::test(switchy_async::test)]
async fn test_simulator_response_json_deserialization_empty() {
let client = crate::SimulatorClient::new();
let response = client.get("http://example.com/api").send().await.unwrap();
let result: Result<serde_json::Value, _> = response.json().await;
assert!(result.is_err());
}
}