use cdk_http_client::{HttpClient, HttpClientBuilder, HttpError};
use serde::{Deserialize, Serialize};
#[derive(Debug, Serialize, Deserialize, PartialEq)]
struct TestPayload {
name: String,
value: i32,
}
#[derive(Debug, Serialize, Deserialize, PartialEq)]
struct TestResponse {
success: bool,
data: String,
}
#[tokio::test]
async fn test_fetch_success() {
let mut server = mockito::Server::new_async().await;
let mock = server
.mock("GET", "/api/data")
.with_status(200)
.with_header("content-type", "application/json")
.with_body(r#"{"success": true, "data": "hello"}"#)
.create_async()
.await;
let client = HttpClient::new();
let url = format!("{}/api/data", server.url());
let result: Result<TestResponse, _> = client.fetch(&url).await;
assert!(result.is_ok());
let response = result.expect("Fetch should succeed");
assert!(response.success);
assert_eq!(response.data, "hello");
mock.assert_async().await;
}
#[tokio::test]
async fn test_fetch_error_status() {
let mut server = mockito::Server::new_async().await;
let mock = server
.mock("GET", "/api/error")
.with_status(404)
.with_body("Not Found")
.create_async()
.await;
let client = HttpClient::new();
let url = format!("{}/api/error", server.url());
let result: Result<TestResponse, _> = client.fetch(&url).await;
assert!(result.is_err());
if let Err(HttpError::Status { status, message }) = result {
assert_eq!(status, 404);
assert_eq!(message, "Not Found");
} else {
panic!("Expected HttpError::Status");
}
mock.assert_async().await;
}
#[tokio::test]
async fn test_fetch_server_error() {
let mut server = mockito::Server::new_async().await;
let mock = server
.mock("GET", "/api/server-error")
.with_status(500)
.with_body("Internal Server Error")
.create_async()
.await;
let client = HttpClient::new();
let url = format!("{}/api/server-error", server.url());
let result: Result<TestResponse, _> = client.fetch(&url).await;
assert!(result.is_err());
if let Err(HttpError::Status { status, .. }) = result {
assert_eq!(status, 500);
} else {
panic!("Expected HttpError::Status");
}
mock.assert_async().await;
}
#[tokio::test]
async fn test_fetch_no_redirects_returns_redirect_status() {
let mut server = mockito::Server::new_async().await;
let location = format!("{}/api/target", server.url());
let redirect = server
.mock("GET", "/api/redirect")
.with_status(302)
.with_header("location", &location)
.with_body("Found")
.create_async()
.await;
let target = server
.mock("GET", "/api/target")
.with_status(200)
.with_header("content-type", "application/json")
.with_body(r#"{"success": true, "data": "followed"}"#)
.expect(0)
.create_async()
.await;
let client = HttpClientBuilder::default()
.no_redirects()
.build()
.expect("no-redirect client should build");
let url = format!("{}/api/redirect", server.url());
let result: Result<TestResponse, _> = client.fetch(&url).await;
assert!(matches!(
result,
Err(HttpError::Status {
status: 302,
message
}) if message == "Found"
));
redirect.assert_async().await;
target.assert_async().await;
}
#[tokio::test]
async fn test_post_json_success() {
let mut server = mockito::Server::new_async().await;
let mock = server
.mock("POST", "/api/submit")
.match_header("content-type", "application/json")
.match_body(mockito::Matcher::Json(serde_json::json!({
"name": "test",
"value": 42
})))
.with_status(200)
.with_header("content-type", "application/json")
.with_body(r#"{"success": true, "data": "received"}"#)
.create_async()
.await;
let client = HttpClient::new();
let url = format!("{}/api/submit", server.url());
let payload = TestPayload {
name: "test".to_string(),
value: 42,
};
let result: Result<TestResponse, _> = client.post_json(&url, &payload).await;
assert!(result.is_ok());
let response = result.expect("POST JSON should succeed");
assert!(response.success);
assert_eq!(response.data, "received");
mock.assert_async().await;
}
#[tokio::test]
async fn test_post_json_error_status() {
let mut server = mockito::Server::new_async().await;
let mock = server
.mock("POST", "/api/submit")
.with_status(400)
.with_body("Bad Request")
.create_async()
.await;
let client = HttpClient::new();
let url = format!("{}/api/submit", server.url());
let payload = TestPayload {
name: "test".to_string(),
value: 42,
};
let result: Result<TestResponse, _> = client.post_json(&url, &payload).await;
assert!(result.is_err());
if let Err(HttpError::Status { status, message }) = result {
assert_eq!(status, 400);
assert_eq!(message, "Bad Request");
} else {
panic!("Expected HttpError::Status");
}
mock.assert_async().await;
}
#[tokio::test]
async fn test_get_raw_success() {
let mut server = mockito::Server::new_async().await;
let mock = server
.mock("GET", "/api/raw")
.with_status(200)
.with_body("raw content")
.create_async()
.await;
let client = HttpClient::new();
let url = format!("{}/api/raw", server.url());
let result = client.get_raw(&url).await;
assert!(result.is_ok());
let response = result.expect("GET raw should succeed");
assert_eq!(response.status(), 200);
assert!(response.is_success());
mock.assert_async().await;
}
#[tokio::test]
async fn test_raw_response_is_success_with_200() {
let mut server = mockito::Server::new_async().await;
let mock = server
.mock("GET", "/")
.with_status(200)
.create_async()
.await;
let client = HttpClient::new();
let response = client
.get_raw(&server.url())
.await
.expect("Request should succeed");
assert!(response.is_success());
assert!(!response.is_client_error());
assert!(!response.is_server_error());
assert_eq!(response.status(), 200);
mock.assert_async().await;
}
#[tokio::test]
async fn test_raw_response_is_success_with_201() {
let mut server = mockito::Server::new_async().await;
let mock = server
.mock("GET", "/")
.with_status(201)
.create_async()
.await;
let client = HttpClient::new();
let response = client
.get_raw(&server.url())
.await
.expect("Request should succeed");
assert!(response.is_success());
assert_eq!(response.status(), 201);
mock.assert_async().await;
}
#[tokio::test]
async fn test_raw_response_is_success_with_299() {
let mut server = mockito::Server::new_async().await;
let mock = server
.mock("GET", "/")
.with_status(299)
.create_async()
.await;
let client = HttpClient::new();
let response = client
.get_raw(&server.url())
.await
.expect("Request should succeed");
assert!(response.is_success());
assert_eq!(response.status(), 299);
mock.assert_async().await;
}
#[tokio::test]
async fn test_raw_response_is_not_success_with_100() {
let mut server = mockito::Server::new_async().await;
let mock = server
.mock("GET", "/")
.with_status(301)
.create_async()
.await;
let client = HttpClient::new();
let response = client
.get_raw(&server.url())
.await
.expect("Request should succeed");
assert!(!response.is_success());
assert_eq!(response.status(), 301);
mock.assert_async().await;
}
#[tokio::test]
async fn test_raw_response_is_not_success_with_300() {
let mut server = mockito::Server::new_async().await;
let mock = server
.mock("GET", "/")
.with_status(300)
.create_async()
.await;
let client = HttpClient::new();
let response = client
.get_raw(&server.url())
.await
.expect("Request should succeed");
assert!(!response.is_success());
assert_eq!(response.status(), 300);
mock.assert_async().await;
}
#[tokio::test]
async fn test_raw_response_is_client_error_with_400() {
let mut server = mockito::Server::new_async().await;
let mock = server
.mock("GET", "/")
.with_status(400)
.create_async()
.await;
let client = HttpClient::new();
let response = client
.get_raw(&server.url())
.await
.expect("Request should succeed");
assert!(response.is_client_error());
assert!(!response.is_success());
assert!(!response.is_server_error());
assert_eq!(response.status(), 400);
mock.assert_async().await;
}
#[tokio::test]
async fn test_raw_response_is_client_error_with_499() {
let mut server = mockito::Server::new_async().await;
let mock = server
.mock("GET", "/")
.with_status(499)
.create_async()
.await;
let client = HttpClient::new();
let response = client
.get_raw(&server.url())
.await
.expect("Request should succeed");
assert!(response.is_client_error());
assert_eq!(response.status(), 499);
mock.assert_async().await;
}
#[tokio::test]
async fn test_raw_response_is_not_client_error_with_399() {
let mut server = mockito::Server::new_async().await;
let mock = server
.mock("GET", "/")
.with_status(399)
.create_async()
.await;
let client = HttpClient::new();
let response = client
.get_raw(&server.url())
.await
.expect("Request should succeed");
assert!(!response.is_client_error());
assert_eq!(response.status(), 399);
mock.assert_async().await;
}
#[tokio::test]
async fn test_raw_response_is_server_error_with_500() {
let mut server = mockito::Server::new_async().await;
let mock = server
.mock("GET", "/")
.with_status(500)
.create_async()
.await;
let client = HttpClient::new();
let response = client
.get_raw(&server.url())
.await
.expect("Request should succeed");
assert!(response.is_server_error());
assert!(!response.is_success());
assert!(!response.is_client_error());
assert_eq!(response.status(), 500);
mock.assert_async().await;
}
#[tokio::test]
async fn test_raw_response_is_server_error_with_599() {
let mut server = mockito::Server::new_async().await;
let mock = server
.mock("GET", "/")
.with_status(599)
.create_async()
.await;
let client = HttpClient::new();
let response = client
.get_raw(&server.url())
.await
.expect("Request should succeed");
assert!(response.is_server_error());
assert_eq!(response.status(), 599);
mock.assert_async().await;
}
#[tokio::test]
async fn test_raw_response_is_not_server_error_with_499() {
let mut server = mockito::Server::new_async().await;
let mock = server
.mock("GET", "/")
.with_status(499)
.create_async()
.await;
let client = HttpClient::new();
let response = client
.get_raw(&server.url())
.await
.expect("Request should succeed");
assert!(!response.is_server_error());
assert_eq!(response.status(), 499);
mock.assert_async().await;
}
#[tokio::test]
async fn test_raw_response_text() {
let mut server = mockito::Server::new_async().await;
let mock = server
.mock("GET", "/")
.with_status(200)
.with_body("Hello, World!")
.create_async()
.await;
let client = HttpClient::new();
let response = client
.get_raw(&server.url())
.await
.expect("Request should succeed");
let text = response
.text()
.await
.expect("Text extraction should succeed");
assert_eq!(text, "Hello, World!");
mock.assert_async().await;
}
#[tokio::test]
async fn test_raw_response_json() {
let mut server = mockito::Server::new_async().await;
let mock = server
.mock("GET", "/")
.with_status(200)
.with_header("content-type", "application/json")
.with_body(r#"{"success": true, "data": "json_test"}"#)
.create_async()
.await;
let client = HttpClient::new();
let response = client
.get_raw(&server.url())
.await
.expect("Request should succeed");
let json: TestResponse = response.json().await.expect("JSON parsing should succeed");
assert!(json.success);
assert_eq!(json.data, "json_test");
mock.assert_async().await;
}
#[tokio::test]
async fn test_raw_response_bytes() {
let mut server = mockito::Server::new_async().await;
let mock = server
.mock("GET", "/")
.with_status(200)
.with_body(vec![0x01, 0x02, 0x03, 0x04])
.create_async()
.await;
let client = HttpClient::new();
let response = client
.get_raw(&server.url())
.await
.expect("Request should succeed");
let bytes = response
.bytes()
.await
.expect("Bytes extraction should succeed");
assert_eq!(bytes, vec![0x01, 0x02, 0x03, 0x04]);
mock.assert_async().await;
}
#[tokio::test]
async fn test_request_builder_send() {
let mut server = mockito::Server::new_async().await;
let mock = server
.mock("GET", "/api/builder")
.with_status(200)
.with_body("builder response")
.create_async()
.await;
let client = HttpClient::new();
let url = format!("{}/api/builder", server.url());
let response = client
.get(&url)
.send()
.await
.expect("Request should succeed");
assert_eq!(response.status(), 200);
assert_eq!(
response
.text()
.await
.expect("Text extraction should succeed"),
"builder response"
);
mock.assert_async().await;
}
#[tokio::test]
async fn test_request_builder_send_json() {
let mut server = mockito::Server::new_async().await;
let mock = server
.mock("POST", "/api/json")
.with_status(200)
.with_header("content-type", "application/json")
.with_body(r#"{"success": true, "data": "builder_json"}"#)
.create_async()
.await;
let client = HttpClient::new();
let url = format!("{}/api/json", server.url());
let payload = TestPayload {
name: "builder".to_string(),
value: 100,
};
let result: TestResponse = client
.post(&url)
.json(&payload)
.send_json()
.await
.expect("Request should succeed");
assert!(result.success);
assert_eq!(result.data, "builder_json");
mock.assert_async().await;
}
#[tokio::test]
async fn test_request_builder_with_headers() {
let mut server = mockito::Server::new_async().await;
let mock = server
.mock("GET", "/api/headers")
.match_header("X-Custom-Header", "custom-value")
.match_header("Authorization", "Bearer token123")
.with_status(200)
.with_body("headers received")
.create_async()
.await;
let client = HttpClient::new();
let url = format!("{}/api/headers", server.url());
let response = client
.get(&url)
.header("X-Custom-Header", "custom-value")
.header("Authorization", "Bearer token123")
.send()
.await
.expect("Request should succeed");
assert_eq!(response.status(), 200);
assert_eq!(
response
.text()
.await
.expect("Text extraction should succeed"),
"headers received"
);
mock.assert_async().await;
}
#[tokio::test]
async fn test_request_builder_post_with_form() {
let mut server = mockito::Server::new_async().await;
let mock = server
.mock("POST", "/api/form")
.match_header(
"content-type",
mockito::Matcher::Regex("application/x-www-form-urlencoded.*".to_string()),
)
.with_status(200)
.with_header("content-type", "application/json")
.with_body(r#"{"success": true, "data": "form_received"}"#)
.create_async()
.await;
let client = HttpClient::new();
let url = format!("{}/api/form", server.url());
let form_data = [("field1", "value1"), ("field2", "value2")];
let response: TestResponse = client
.post(&url)
.form(&form_data)
.send_json()
.await
.expect("Request should succeed");
assert!(response.success);
assert_eq!(response.data, "form_received");
mock.assert_async().await;
}
#[tokio::test]
async fn test_request_builder_patch() {
let mut server = mockito::Server::new_async().await;
let mock = server
.mock("PATCH", "/api/resource")
.with_status(200)
.with_header("content-type", "application/json")
.with_body(r#"{"success": true, "data": "patched"}"#)
.create_async()
.await;
let client = HttpClient::new();
let url = format!("{}/api/resource", server.url());
let payload = TestPayload {
name: "update".to_string(),
value: 99,
};
let result: TestResponse = client
.patch(&url)
.json(&payload)
.send_json()
.await
.expect("Request should succeed");
assert!(result.success);
assert_eq!(result.data, "patched");
mock.assert_async().await;
}
#[tokio::test]
async fn test_fetch_convenience_function() {
let mut server = mockito::Server::new_async().await;
let mock = server
.mock("GET", "/api/convenience")
.with_status(200)
.with_header("content-type", "application/json")
.with_body(r#"{"success": true, "data": "convenience"}"#)
.create_async()
.await;
let url = format!("{}/api/convenience", server.url());
let result: Result<TestResponse, _> = cdk_http_client::fetch(&url).await;
assert!(result.is_ok());
let response = result.expect("Fetch should succeed");
assert!(response.success);
assert_eq!(response.data, "convenience");
mock.assert_async().await;
}
#[tokio::test]
async fn test_json_deserialization_error() {
let mut server = mockito::Server::new_async().await;
let mock = server
.mock("GET", "/api/invalid-json")
.with_status(200)
.with_header("content-type", "application/json")
.with_body("not valid json")
.create_async()
.await;
let client = HttpClient::new();
let url = format!("{}/api/invalid-json", server.url());
let result: Result<TestResponse, _> = client.fetch(&url).await;
assert!(result.is_err());
let err = result.expect_err("Should be a deserialization error");
let err_str = format!("{}", err);
assert!(
err_str.contains("expected") || err_str.contains("JSON") || err_str.contains("error"),
"Error should mention parsing issue: {}",
err_str
);
mock.assert_async().await;
}
#[tokio::test]
async fn test_raw_response_json_deserialization_error() {
let mut server = mockito::Server::new_async().await;
let mock = server
.mock("GET", "/")
.with_status(200)
.with_body("invalid json")
.create_async()
.await;
let client = HttpClient::new();
let response = client
.get_raw(&server.url())
.await
.expect("Request should succeed");
let result: Result<TestResponse, _> = response.json().await;
assert!(result.is_err());
mock.assert_async().await;
}