use armature_core::{Error, HttpMethod, HttpRequest, HttpResponse, Router};
use std::collections::HashMap;
use std::sync::Arc;
pub struct TestClient {
router: Arc<Router>,
}
impl TestClient {
pub fn new(router: Arc<Router>) -> Self {
Self { router }
}
pub async fn get(&self, path: &str) -> TestResponse {
self.request(HttpMethod::GET, path, None).await
}
pub async fn post(&self, path: &str, body: Vec<u8>) -> TestResponse {
self.request(HttpMethod::POST, path, Some(body)).await
}
pub async fn put(&self, path: &str, body: Vec<u8>) -> TestResponse {
self.request(HttpMethod::PUT, path, Some(body)).await
}
pub async fn delete(&self, path: &str) -> TestResponse {
self.request(HttpMethod::DELETE, path, None).await
}
pub async fn patch(&self, path: &str, body: Vec<u8>) -> TestResponse {
self.request(HttpMethod::PATCH, path, Some(body)).await
}
pub async fn request(
&self,
method: HttpMethod,
path: &str,
body: Option<Vec<u8>>,
) -> TestResponse {
let mut req = HttpRequest::new(method.as_str().to_string(), path.to_string());
req.body = body.unwrap_or_default();
match self.router.route(req).await {
Ok(response) => TestResponse::Success(response),
Err(error) => TestResponse::Error(error),
}
}
}
#[allow(dead_code)]
pub struct TestRequestBuilder {
method: HttpMethod,
path: String,
headers: HashMap<String, String>,
body: Vec<u8>,
query_params: HashMap<String, String>,
}
impl TestRequestBuilder {
#[allow(dead_code)]
pub fn new(method: HttpMethod, path: &str) -> Self {
Self {
method,
path: path.to_string(),
headers: HashMap::new(),
body: Vec::new(),
query_params: HashMap::new(),
}
}
#[allow(dead_code)]
pub fn header(mut self, key: &str, value: &str) -> Self {
self.headers.insert(key.to_string(), value.to_string());
self
}
#[allow(dead_code)]
pub fn body(mut self, body: Vec<u8>) -> Self {
self.body = body;
self
}
#[allow(dead_code)]
pub fn json<T: serde::Serialize>(mut self, data: &T) -> Result<Self, Error> {
self.body = serde_json::to_vec(data).map_err(|e| Error::Serialization(e.to_string()))?;
self.headers
.insert("Content-Type".to_string(), "application/json".to_string());
Ok(self)
}
#[allow(dead_code)]
pub fn query(mut self, key: &str, value: &str) -> Self {
self.query_params.insert(key.to_string(), value.to_string());
self
}
#[allow(dead_code)]
pub fn build(self) -> HttpRequest {
let query_string = if !self.query_params.is_empty() {
let params: Vec<String> = self
.query_params
.iter()
.map(|(k, v)| format!("{}={}", k, v))
.collect();
format!("?{}", params.join("&"))
} else {
String::new()
};
HttpRequest::from_parts(
self.method.as_str().to_string(),
format!("{}{}", self.path, query_string),
self.headers,
self.body,
HashMap::new(),
self.query_params,
)
}
}
#[derive(Debug)]
pub enum TestResponse {
Success(HttpResponse),
Error(Error),
}
impl TestResponse {
pub fn assert_success(&self) -> &HttpResponse {
match self {
TestResponse::Success(response) => response,
TestResponse::Error(error) => {
panic!("Expected success response, got error: {:?}", error)
}
}
}
pub fn assert_error(&self) -> &Error {
match self {
TestResponse::Error(error) => error,
TestResponse::Success(_) => {
panic!("Expected error response, got success")
}
}
}
pub fn status(&self) -> Option<u16> {
match self {
TestResponse::Success(response) => Some(response.status),
TestResponse::Error(_) => None,
}
}
pub fn body_string(&self) -> Option<String> {
match self {
TestResponse::Success(response) => String::from_utf8(response.body.clone()).ok(),
TestResponse::Error(_) => None,
}
}
pub fn body_json<T: serde::de::DeserializeOwned>(&self) -> Result<T, String> {
match self {
TestResponse::Success(response) => serde_json::from_slice(&response.body)
.map_err(|e| format!("Serialization error: {}", e)),
TestResponse::Error(error) => Err(format!("{:?}", error)),
}
}
pub fn header(&self, key: &str) -> Option<&String> {
match self {
TestResponse::Success(response) => response.headers.get(key),
TestResponse::Error(_) => None,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_request_builder() {
let req = TestRequestBuilder::new(HttpMethod::GET, "/test")
.header("Authorization", "Bearer token")
.query("foo", "bar")
.build();
assert_eq!(req.method, "GET");
assert!(req.path.contains("/test"));
assert_eq!(
req.headers.get("Authorization"),
Some(&"Bearer token".to_string())
);
}
}