integration.rs 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748
  1. //! Integration tests for cdk-http-client using mockito
  2. use cdk_http_client::{HttpClient, HttpError};
  3. use serde::{Deserialize, Serialize};
  4. #[derive(Debug, Serialize, Deserialize, PartialEq)]
  5. struct TestPayload {
  6. name: String,
  7. value: i32,
  8. }
  9. #[derive(Debug, Serialize, Deserialize, PartialEq)]
  10. struct TestResponse {
  11. success: bool,
  12. data: String,
  13. }
  14. // === HttpClient::fetch tests ===
  15. #[tokio::test]
  16. async fn test_fetch_success() {
  17. let mut server = mockito::Server::new_async().await;
  18. let mock = server
  19. .mock("GET", "/api/data")
  20. .with_status(200)
  21. .with_header("content-type", "application/json")
  22. .with_body(r#"{"success": true, "data": "hello"}"#)
  23. .create_async()
  24. .await;
  25. let client = HttpClient::new();
  26. let url = format!("{}/api/data", server.url());
  27. let result: Result<TestResponse, _> = client.fetch(&url).await;
  28. assert!(result.is_ok());
  29. let response = result.expect("Fetch should succeed");
  30. assert!(response.success);
  31. assert_eq!(response.data, "hello");
  32. mock.assert_async().await;
  33. }
  34. #[tokio::test]
  35. async fn test_fetch_error_status() {
  36. let mut server = mockito::Server::new_async().await;
  37. let mock = server
  38. .mock("GET", "/api/error")
  39. .with_status(404)
  40. .with_body("Not Found")
  41. .create_async()
  42. .await;
  43. let client = HttpClient::new();
  44. let url = format!("{}/api/error", server.url());
  45. let result: Result<TestResponse, _> = client.fetch(&url).await;
  46. assert!(result.is_err());
  47. if let Err(HttpError::Status { status, message }) = result {
  48. assert_eq!(status, 404);
  49. assert_eq!(message, "Not Found");
  50. } else {
  51. panic!("Expected HttpError::Status");
  52. }
  53. mock.assert_async().await;
  54. }
  55. #[tokio::test]
  56. async fn test_fetch_server_error() {
  57. let mut server = mockito::Server::new_async().await;
  58. let mock = server
  59. .mock("GET", "/api/server-error")
  60. .with_status(500)
  61. .with_body("Internal Server Error")
  62. .create_async()
  63. .await;
  64. let client = HttpClient::new();
  65. let url = format!("{}/api/server-error", server.url());
  66. let result: Result<TestResponse, _> = client.fetch(&url).await;
  67. assert!(result.is_err());
  68. if let Err(HttpError::Status { status, .. }) = result {
  69. assert_eq!(status, 500);
  70. } else {
  71. panic!("Expected HttpError::Status");
  72. }
  73. mock.assert_async().await;
  74. }
  75. // === HttpClient::post_json tests ===
  76. #[tokio::test]
  77. async fn test_post_json_success() {
  78. let mut server = mockito::Server::new_async().await;
  79. let mock = server
  80. .mock("POST", "/api/submit")
  81. .match_header("content-type", "application/json")
  82. .match_body(mockito::Matcher::Json(serde_json::json!({
  83. "name": "test",
  84. "value": 42
  85. })))
  86. .with_status(200)
  87. .with_header("content-type", "application/json")
  88. .with_body(r#"{"success": true, "data": "received"}"#)
  89. .create_async()
  90. .await;
  91. let client = HttpClient::new();
  92. let url = format!("{}/api/submit", server.url());
  93. let payload = TestPayload {
  94. name: "test".to_string(),
  95. value: 42,
  96. };
  97. let result: Result<TestResponse, _> = client.post_json(&url, &payload).await;
  98. assert!(result.is_ok());
  99. let response = result.expect("POST JSON should succeed");
  100. assert!(response.success);
  101. assert_eq!(response.data, "received");
  102. mock.assert_async().await;
  103. }
  104. #[tokio::test]
  105. async fn test_post_json_error_status() {
  106. let mut server = mockito::Server::new_async().await;
  107. let mock = server
  108. .mock("POST", "/api/submit")
  109. .with_status(400)
  110. .with_body("Bad Request")
  111. .create_async()
  112. .await;
  113. let client = HttpClient::new();
  114. let url = format!("{}/api/submit", server.url());
  115. let payload = TestPayload {
  116. name: "test".to_string(),
  117. value: 42,
  118. };
  119. let result: Result<TestResponse, _> = client.post_json(&url, &payload).await;
  120. assert!(result.is_err());
  121. if let Err(HttpError::Status { status, message }) = result {
  122. assert_eq!(status, 400);
  123. assert_eq!(message, "Bad Request");
  124. } else {
  125. panic!("Expected HttpError::Status");
  126. }
  127. mock.assert_async().await;
  128. }
  129. // === HttpClient::get_raw tests ===
  130. #[tokio::test]
  131. async fn test_get_raw_success() {
  132. let mut server = mockito::Server::new_async().await;
  133. let mock = server
  134. .mock("GET", "/api/raw")
  135. .with_status(200)
  136. .with_body("raw content")
  137. .create_async()
  138. .await;
  139. let client = HttpClient::new();
  140. let url = format!("{}/api/raw", server.url());
  141. let result = client.get_raw(&url).await;
  142. assert!(result.is_ok());
  143. let response = result.expect("GET raw should succeed");
  144. assert_eq!(response.status(), 200);
  145. assert!(response.is_success());
  146. mock.assert_async().await;
  147. }
  148. // === RawResponse tests ===
  149. #[tokio::test]
  150. async fn test_raw_response_is_success_with_200() {
  151. let mut server = mockito::Server::new_async().await;
  152. let mock = server
  153. .mock("GET", "/")
  154. .with_status(200)
  155. .create_async()
  156. .await;
  157. let client = HttpClient::new();
  158. let response = client
  159. .get_raw(&server.url())
  160. .await
  161. .expect("Request should succeed");
  162. assert!(response.is_success());
  163. assert!(!response.is_client_error());
  164. assert!(!response.is_server_error());
  165. assert_eq!(response.status(), 200);
  166. mock.assert_async().await;
  167. }
  168. #[tokio::test]
  169. async fn test_raw_response_is_success_with_201() {
  170. let mut server = mockito::Server::new_async().await;
  171. let mock = server
  172. .mock("GET", "/")
  173. .with_status(201)
  174. .create_async()
  175. .await;
  176. let client = HttpClient::new();
  177. let response = client
  178. .get_raw(&server.url())
  179. .await
  180. .expect("Request should succeed");
  181. assert!(response.is_success());
  182. assert_eq!(response.status(), 201);
  183. mock.assert_async().await;
  184. }
  185. #[tokio::test]
  186. async fn test_raw_response_is_success_with_299() {
  187. let mut server = mockito::Server::new_async().await;
  188. let mock = server
  189. .mock("GET", "/")
  190. .with_status(299)
  191. .create_async()
  192. .await;
  193. let client = HttpClient::new();
  194. let response = client
  195. .get_raw(&server.url())
  196. .await
  197. .expect("Request should succeed");
  198. assert!(response.is_success());
  199. assert_eq!(response.status(), 299);
  200. mock.assert_async().await;
  201. }
  202. #[tokio::test]
  203. async fn test_raw_response_is_not_success_with_100() {
  204. // Note: HTTP 1xx informational responses are special and may not be
  205. // fully supported by all HTTP libraries. We test with 100 Continue.
  206. // mockito may convert some 1xx codes to 500, so we just verify the
  207. // logic works with the boundary check (200..300).
  208. let mut server = mockito::Server::new_async().await;
  209. // Use 301 redirect as a more reliable "not success" boundary test
  210. let mock = server
  211. .mock("GET", "/")
  212. .with_status(301)
  213. .create_async()
  214. .await;
  215. let client = HttpClient::new();
  216. let response = client
  217. .get_raw(&server.url())
  218. .await
  219. .expect("Request should succeed");
  220. // 301 is a redirect, not success
  221. assert!(!response.is_success());
  222. assert_eq!(response.status(), 301);
  223. mock.assert_async().await;
  224. }
  225. #[tokio::test]
  226. async fn test_raw_response_is_not_success_with_300() {
  227. let mut server = mockito::Server::new_async().await;
  228. let mock = server
  229. .mock("GET", "/")
  230. .with_status(300)
  231. .create_async()
  232. .await;
  233. let client = HttpClient::new();
  234. let response = client
  235. .get_raw(&server.url())
  236. .await
  237. .expect("Request should succeed");
  238. assert!(!response.is_success());
  239. assert_eq!(response.status(), 300);
  240. mock.assert_async().await;
  241. }
  242. #[tokio::test]
  243. async fn test_raw_response_is_client_error_with_400() {
  244. let mut server = mockito::Server::new_async().await;
  245. let mock = server
  246. .mock("GET", "/")
  247. .with_status(400)
  248. .create_async()
  249. .await;
  250. let client = HttpClient::new();
  251. let response = client
  252. .get_raw(&server.url())
  253. .await
  254. .expect("Request should succeed");
  255. assert!(response.is_client_error());
  256. assert!(!response.is_success());
  257. assert!(!response.is_server_error());
  258. assert_eq!(response.status(), 400);
  259. mock.assert_async().await;
  260. }
  261. #[tokio::test]
  262. async fn test_raw_response_is_client_error_with_499() {
  263. let mut server = mockito::Server::new_async().await;
  264. let mock = server
  265. .mock("GET", "/")
  266. .with_status(499)
  267. .create_async()
  268. .await;
  269. let client = HttpClient::new();
  270. let response = client
  271. .get_raw(&server.url())
  272. .await
  273. .expect("Request should succeed");
  274. assert!(response.is_client_error());
  275. assert_eq!(response.status(), 499);
  276. mock.assert_async().await;
  277. }
  278. #[tokio::test]
  279. async fn test_raw_response_is_not_client_error_with_399() {
  280. let mut server = mockito::Server::new_async().await;
  281. let mock = server
  282. .mock("GET", "/")
  283. .with_status(399)
  284. .create_async()
  285. .await;
  286. let client = HttpClient::new();
  287. let response = client
  288. .get_raw(&server.url())
  289. .await
  290. .expect("Request should succeed");
  291. assert!(!response.is_client_error());
  292. assert_eq!(response.status(), 399);
  293. mock.assert_async().await;
  294. }
  295. #[tokio::test]
  296. async fn test_raw_response_is_server_error_with_500() {
  297. let mut server = mockito::Server::new_async().await;
  298. let mock = server
  299. .mock("GET", "/")
  300. .with_status(500)
  301. .create_async()
  302. .await;
  303. let client = HttpClient::new();
  304. let response = client
  305. .get_raw(&server.url())
  306. .await
  307. .expect("Request should succeed");
  308. assert!(response.is_server_error());
  309. assert!(!response.is_success());
  310. assert!(!response.is_client_error());
  311. assert_eq!(response.status(), 500);
  312. mock.assert_async().await;
  313. }
  314. #[tokio::test]
  315. async fn test_raw_response_is_server_error_with_599() {
  316. let mut server = mockito::Server::new_async().await;
  317. let mock = server
  318. .mock("GET", "/")
  319. .with_status(599)
  320. .create_async()
  321. .await;
  322. let client = HttpClient::new();
  323. let response = client
  324. .get_raw(&server.url())
  325. .await
  326. .expect("Request should succeed");
  327. assert!(response.is_server_error());
  328. assert_eq!(response.status(), 599);
  329. mock.assert_async().await;
  330. }
  331. #[tokio::test]
  332. async fn test_raw_response_is_not_server_error_with_499() {
  333. let mut server = mockito::Server::new_async().await;
  334. let mock = server
  335. .mock("GET", "/")
  336. .with_status(499)
  337. .create_async()
  338. .await;
  339. let client = HttpClient::new();
  340. let response = client
  341. .get_raw(&server.url())
  342. .await
  343. .expect("Request should succeed");
  344. assert!(!response.is_server_error());
  345. assert_eq!(response.status(), 499);
  346. mock.assert_async().await;
  347. }
  348. #[tokio::test]
  349. async fn test_raw_response_text() {
  350. let mut server = mockito::Server::new_async().await;
  351. let mock = server
  352. .mock("GET", "/")
  353. .with_status(200)
  354. .with_body("Hello, World!")
  355. .create_async()
  356. .await;
  357. let client = HttpClient::new();
  358. let response = client
  359. .get_raw(&server.url())
  360. .await
  361. .expect("Request should succeed");
  362. let text = response.text().await.expect("Text extraction should succeed");
  363. assert_eq!(text, "Hello, World!");
  364. mock.assert_async().await;
  365. }
  366. #[tokio::test]
  367. async fn test_raw_response_json() {
  368. let mut server = mockito::Server::new_async().await;
  369. let mock = server
  370. .mock("GET", "/")
  371. .with_status(200)
  372. .with_header("content-type", "application/json")
  373. .with_body(r#"{"success": true, "data": "json_test"}"#)
  374. .create_async()
  375. .await;
  376. let client = HttpClient::new();
  377. let response = client
  378. .get_raw(&server.url())
  379. .await
  380. .expect("Request should succeed");
  381. let json: TestResponse = response.json().await.expect("JSON parsing should succeed");
  382. assert!(json.success);
  383. assert_eq!(json.data, "json_test");
  384. mock.assert_async().await;
  385. }
  386. #[tokio::test]
  387. async fn test_raw_response_bytes() {
  388. let mut server = mockito::Server::new_async().await;
  389. let mock = server
  390. .mock("GET", "/")
  391. .with_status(200)
  392. .with_body(vec![0x01, 0x02, 0x03, 0x04])
  393. .create_async()
  394. .await;
  395. let client = HttpClient::new();
  396. let response = client
  397. .get_raw(&server.url())
  398. .await
  399. .expect("Request should succeed");
  400. let bytes = response.bytes().await.expect("Bytes extraction should succeed");
  401. assert_eq!(bytes, vec![0x01, 0x02, 0x03, 0x04]);
  402. mock.assert_async().await;
  403. }
  404. // === RequestBuilder tests ===
  405. #[tokio::test]
  406. async fn test_request_builder_send() {
  407. let mut server = mockito::Server::new_async().await;
  408. let mock = server
  409. .mock("GET", "/api/builder")
  410. .with_status(200)
  411. .with_body("builder response")
  412. .create_async()
  413. .await;
  414. let client = HttpClient::new();
  415. let url = format!("{}/api/builder", server.url());
  416. let response = client
  417. .get(&url)
  418. .send()
  419. .await
  420. .expect("Request should succeed");
  421. assert_eq!(response.status(), 200);
  422. assert_eq!(
  423. response.text().await.expect("Text extraction should succeed"),
  424. "builder response"
  425. );
  426. mock.assert_async().await;
  427. }
  428. #[tokio::test]
  429. async fn test_request_builder_send_json() {
  430. let mut server = mockito::Server::new_async().await;
  431. let mock = server
  432. .mock("POST", "/api/json")
  433. .with_status(200)
  434. .with_header("content-type", "application/json")
  435. .with_body(r#"{"success": true, "data": "builder_json"}"#)
  436. .create_async()
  437. .await;
  438. let client = HttpClient::new();
  439. let url = format!("{}/api/json", server.url());
  440. let payload = TestPayload {
  441. name: "builder".to_string(),
  442. value: 100,
  443. };
  444. let result: TestResponse = client
  445. .post(&url)
  446. .json(&payload)
  447. .send_json()
  448. .await
  449. .expect("Request should succeed");
  450. assert!(result.success);
  451. assert_eq!(result.data, "builder_json");
  452. mock.assert_async().await;
  453. }
  454. #[tokio::test]
  455. async fn test_request_builder_with_headers() {
  456. let mut server = mockito::Server::new_async().await;
  457. let mock = server
  458. .mock("GET", "/api/headers")
  459. .match_header("X-Custom-Header", "custom-value")
  460. .match_header("Authorization", "Bearer token123")
  461. .with_status(200)
  462. .with_body("headers received")
  463. .create_async()
  464. .await;
  465. let client = HttpClient::new();
  466. let url = format!("{}/api/headers", server.url());
  467. let response = client
  468. .get(&url)
  469. .header("X-Custom-Header", "custom-value")
  470. .header("Authorization", "Bearer token123")
  471. .send()
  472. .await
  473. .expect("Request should succeed");
  474. assert_eq!(response.status(), 200);
  475. assert_eq!(
  476. response.text().await.expect("Text extraction should succeed"),
  477. "headers received"
  478. );
  479. mock.assert_async().await;
  480. }
  481. #[tokio::test]
  482. async fn test_request_builder_post_with_form() {
  483. let mut server = mockito::Server::new_async().await;
  484. let mock = server
  485. .mock("POST", "/api/form")
  486. .match_header(
  487. "content-type",
  488. mockito::Matcher::Regex("application/x-www-form-urlencoded.*".to_string()),
  489. )
  490. .with_status(200)
  491. .with_header("content-type", "application/json")
  492. .with_body(r#"{"success": true, "data": "form_received"}"#)
  493. .create_async()
  494. .await;
  495. let client = HttpClient::new();
  496. let url = format!("{}/api/form", server.url());
  497. let form_data = [("field1", "value1"), ("field2", "value2")];
  498. let response: TestResponse = client
  499. .post(&url)
  500. .form(&form_data)
  501. .send_json()
  502. .await
  503. .expect("Request should succeed");
  504. assert!(response.success);
  505. assert_eq!(response.data, "form_received");
  506. mock.assert_async().await;
  507. }
  508. #[tokio::test]
  509. async fn test_request_builder_patch() {
  510. let mut server = mockito::Server::new_async().await;
  511. let mock = server
  512. .mock("PATCH", "/api/resource")
  513. .with_status(200)
  514. .with_header("content-type", "application/json")
  515. .with_body(r#"{"success": true, "data": "patched"}"#)
  516. .create_async()
  517. .await;
  518. let client = HttpClient::new();
  519. let url = format!("{}/api/resource", server.url());
  520. let payload = TestPayload {
  521. name: "update".to_string(),
  522. value: 99,
  523. };
  524. let result: TestResponse = client
  525. .patch(&url)
  526. .json(&payload)
  527. .send_json()
  528. .await
  529. .expect("Request should succeed");
  530. assert!(result.success);
  531. assert_eq!(result.data, "patched");
  532. mock.assert_async().await;
  533. }
  534. // === Convenience function test ===
  535. #[tokio::test]
  536. async fn test_fetch_convenience_function() {
  537. let mut server = mockito::Server::new_async().await;
  538. let mock = server
  539. .mock("GET", "/api/convenience")
  540. .with_status(200)
  541. .with_header("content-type", "application/json")
  542. .with_body(r#"{"success": true, "data": "convenience"}"#)
  543. .create_async()
  544. .await;
  545. let url = format!("{}/api/convenience", server.url());
  546. let result: Result<TestResponse, _> = cdk_http_client::fetch(&url).await;
  547. assert!(result.is_ok());
  548. let response = result.expect("Fetch should succeed");
  549. assert!(response.success);
  550. assert_eq!(response.data, "convenience");
  551. mock.assert_async().await;
  552. }
  553. // === Error handling tests ===
  554. #[tokio::test]
  555. async fn test_json_deserialization_error() {
  556. let mut server = mockito::Server::new_async().await;
  557. let mock = server
  558. .mock("GET", "/api/invalid-json")
  559. .with_status(200)
  560. .with_header("content-type", "application/json")
  561. .with_body("not valid json")
  562. .create_async()
  563. .await;
  564. let client = HttpClient::new();
  565. let url = format!("{}/api/invalid-json", server.url());
  566. let result: Result<TestResponse, _> = client.fetch(&url).await;
  567. assert!(result.is_err());
  568. // The error should be about JSON parsing, which becomes HttpError::Other from reqwest
  569. let err = result.expect_err("Should be a deserialization error");
  570. let err_str = format!("{}", err);
  571. assert!(
  572. err_str.contains("expected") || err_str.contains("JSON") || err_str.contains("error"),
  573. "Error should mention parsing issue: {}",
  574. err_str
  575. );
  576. mock.assert_async().await;
  577. }
  578. #[tokio::test]
  579. async fn test_raw_response_json_deserialization_error() {
  580. let mut server = mockito::Server::new_async().await;
  581. let mock = server
  582. .mock("GET", "/")
  583. .with_status(200)
  584. .with_body("invalid json")
  585. .create_async()
  586. .await;
  587. let client = HttpClient::new();
  588. let response = client
  589. .get_raw(&server.url())
  590. .await
  591. .expect("Request should succeed");
  592. let result: Result<TestResponse, _> = response.json().await;
  593. assert!(result.is_err());
  594. mock.assert_async().await;
  595. }