| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760 |
- //! Integration tests for cdk-http-client using mockito
- use cdk_http_client::{HttpClient, 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,
- }
- // === HttpClient::fetch tests ===
- #[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;
- }
- // === HttpClient::post_json tests ===
- #[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;
- }
- // === HttpClient::get_raw tests ===
- #[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;
- }
- // === RawResponse tests ===
- #[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() {
- // Note: HTTP 1xx informational responses are special and may not be
- // fully supported by all HTTP libraries. We test with 100 Continue.
- // mockito may convert some 1xx codes to 500, so we just verify the
- // logic works with the boundary check (200..300).
- let mut server = mockito::Server::new_async().await;
- // Use 301 redirect as a more reliable "not success" boundary test
- 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");
- // 301 is a redirect, not success
- 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;
- }
- // === RequestBuilder tests ===
- #[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;
- }
- // === Convenience function test ===
- #[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;
- }
- // === Error handling tests ===
- #[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());
- // The error should be about JSON parsing, which becomes HttpError::Other from reqwest
- 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;
- }
|