//! Tests for the HTTP file download utility with mockito mock server. use std::fs; use mockito::{Server, ServerGuard, Mock}; use top_drives::get_file; /// Helper: create a mock that returns a successful response with the given body. fn mock_success(server: &mut ServerGuard, body: &str) -> Mock { server.mock("GET", "/data.json") .with_status(200) .with_body(body) .create() } /// Helper: create a mock that returns the given HTTP status. fn mock_status(server: &mut ServerGuard, status: usize) -> Mock { server.mock("GET", "/data.json") .with_status(status) .create() } #[tokio::test] async fn test_get_file_success() { let mut server = Server::new_async().await; let m = mock_success(&mut server, "hello world"); let dir = tempfile::tempdir().unwrap(); let out_path = dir.path().join("output.txt"); let url = format!("{}/data.json", server.url()); get_file(&url, out_path.to_str().unwrap()).await.unwrap(); assert_eq!(fs::read_to_string(&out_path).unwrap(), "hello world"); m.assert(); } #[tokio::test] async fn test_get_file_empty_response_retries_and_fails() { let mut server = Server::new_async().await; // Return 200 with empty body — triggers retry, then fails let m = server.mock("GET", "/data.json") .with_status(200) .with_body("") .expect(4) // 1 initial + 3 retries .create(); let dir = tempfile::tempdir().unwrap(); let out_path = dir.path().join("output.txt"); let url = format!("{}/data.json", server.url()); let result = get_file(&url, out_path.to_str().unwrap()).await; assert!(result.is_err(), "Empty response should fail after retries"); m.assert(); } #[tokio::test] async fn test_get_file_server_error_retries() { let mut server = Server::new_async().await; // Return 500 three times, then 200 let m_fail = server.mock("GET", "/data.json") .with_status(500) .expect(3) .create(); let m_ok = mock_success(&mut server, "recovered"); let dir = tempfile::tempdir().unwrap(); let out_path = dir.path().join("output.txt"); let url = format!("{}/data.json", server.url()); get_file(&url, out_path.to_str().unwrap()).await.unwrap(); assert_eq!(fs::read_to_string(&out_path).unwrap(), "recovered"); m_fail.assert(); m_ok.assert(); } #[tokio::test] async fn test_get_file_client_error_fails_immediately() { let mut server = Server::new_async().await; // 404 should fail immediately without retries let m = mock_status(&mut server, 404).expect(1).create(); let dir = tempfile::tempdir().unwrap(); let out_path = dir.path().join("output.txt"); let url = format!("{}/data.json", server.url()); let result = get_file(&url, out_path.to_str().unwrap()).await; assert!(result.is_err(), "404 should fail immediately"); let err_msg = format!("{}", result.unwrap_err()); assert!(err_msg.contains("404"), "Error should mention HTTP 404: {err_msg}"); m.assert(); } #[tokio::test] async fn test_get_file_persistent_server_error_fails() { let mut server = Server::new_async().await; // 503 every time — should retry 3 times then fail let m = server.mock("GET", "/data.json") .with_status(503) .expect(4) // 1 initial + 3 retries .create(); let dir = tempfile::tempdir().unwrap(); let out_path = dir.path().join("output.txt"); let url = format!("{}/data.json", server.url()); let result = get_file(&url, out_path.to_str().unwrap()).await; assert!(result.is_err(), "Persistent 503 should fail after retries"); let err_msg = format!("{}", result.unwrap_err()); assert!(err_msg.contains("503"), "Error should mention HTTP 503: {err_msg}"); m.assert(); } #[tokio::test] async fn test_get_file_atomic_write_no_partial_on_error() { let mut server = Server::new_async().await; // Return 404 — immediate failure, no file written let m = mock_status(&mut server, 404).create(); let dir = tempfile::tempdir().unwrap(); let out_path = dir.path().join("output.txt"); let url = format!("{}/data.json", server.url()); let result = get_file(&url, out_path.to_str().unwrap()).await; assert!(result.is_err()); // The output file should NOT exist (atomic write via temp + rename) assert!(!out_path.exists(), "No partial file should be left on failure"); m.assert(); }