diff --git a/.gitignore b/.gitignore index ae22cc7..a483cf1 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,5 @@ **/.rpt2_cache **/build /web/data +# created by unit tests +/cli/currency.json diff --git a/Cargo.lock b/Cargo.lock index 54f58df..da68a7c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -101,6 +101,12 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b" +[[package]] +name = "ascii" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d92bec98840b8f03a5ff5413de5293bfcd8bf96467cf5452609f939ec6f5de16" + [[package]] name = "assert-json-diff" version = "2.0.2" @@ -479,6 +485,12 @@ dependencies = [ "parse-zoneinfo", ] +[[package]] +name = "chunked_transfer" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e4de3bc4ea267985becf712dc6d9eed8b04c953b3fcfb339ebc87acd9804901" + [[package]] name = "clap" version = "4.5.4" @@ -1085,6 +1097,12 @@ version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" +[[package]] +name = "httpdate" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" + [[package]] name = "humantime" version = "2.1.0" @@ -1751,6 +1769,7 @@ dependencies = [ "serde_json", "similar-asserts", "tempfile", + "tiny_http", "toml 0.5.11", "ubyte", ] @@ -2149,6 +2168,18 @@ dependencies = [ "once_cell", ] +[[package]] +name = "tiny_http" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "389915df6413a2e74fb181895f933386023c71110878cd0825588928e64cdc82" +dependencies = [ + "ascii", + "chunked_transfer", + "httpdate", + "log", +] + [[package]] name = "tokio" version = "1.37.0" diff --git a/cli/Cargo.toml b/cli/Cargo.toml index b968fa2..fc498ec 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -42,6 +42,7 @@ path = "../sandbox" [dev-dependencies] similar-asserts = "1.1.0" +tiny_http = "0.12" [package.metadata.wasm-pack.profile.profiling] wasm-opt = ['-g', '-O'] diff --git a/cli/src/config.rs b/cli/src/config.rs index d32f70e..ed46e97 100644 --- a/cli/src/config.rs +++ b/cli/src/config.rs @@ -471,3 +471,107 @@ fn cached( Err(err).wrap_err_with(|| format!("Failed to fetch {}", url)) } } + +#[cfg(test)] +mod tests { + use std::{ + io::Read, + path::PathBuf, + sync::{Arc, Mutex}, + time::Duration, + }; + + use tiny_http::{Response, Server, StatusCode}; + + static MUTEX: Mutex<()> = Mutex::new(()); + + #[test] + fn test_download_timeout() { + let lock = MUTEX.lock().unwrap(); + let server = + Arc::new(Server::http("127.0.0.1:3090").expect("port 3090 is needed to do http tests")); + let server2 = server.clone(); + + let thread_handle = std::thread::spawn(move || { + let request = server.recv().expect("the request should not fail"); + assert_eq!(request.url(), "/data/currency.json"); + std::thread::sleep(Duration::from_millis(100)); + }); + let result = super::download_to_file( + &PathBuf::from("currency.json"), + "http://127.0.0.1:3090/data/currency.json", + Duration::from_millis(5), + ); + let result = result.expect_err("this should always fail"); + assert_eq!(result.to_string(), "[28] Timeout was reached (Operation timed out after 5 milliseconds with 0 bytes received)"); + server2.unblock(); + thread_handle.join().unwrap(); + drop(server2); + drop(lock); + } + + #[test] + fn test_download_404() { + let lock = MUTEX.lock().unwrap(); + let server = + Arc::new(Server::http("127.0.0.1:3090").expect("port 3090 is needed to do http tests")); + let server2 = server.clone(); + + let thread_handle = std::thread::spawn(move || { + let request = server.recv().expect("the request should not fail"); + assert_eq!(request.url(), "/data/currency.json"); + let mut data = b"404 not found".to_owned(); + let cursor = std::io::Cursor::new(&mut data); + request + .respond(Response::new(StatusCode(404), vec![], cursor, None, None)) + .expect("the response should go through"); + }); + let result = super::download_to_file( + &PathBuf::from("currency.json"), + "http://127.0.0.1:3090/data/currency.json", + Duration::from_millis(2000), + ); + let result = result.expect_err("this should always fail"); + assert_eq!( + result.to_string(), + "Received status 404 while downloading http://127.0.0.1:3090/data/currency.json" + ); + server2.unblock(); + thread_handle.join().unwrap(); + drop(server2); + drop(lock); + } + + #[test] + fn test_download_success() { + let lock = MUTEX.lock().unwrap(); + let server = + Arc::new(Server::http("127.0.0.1:3090").expect("port 3090 is needed to do http tests")); + let server2 = server.clone(); + + let thread_handle = std::thread::spawn(move || { + let request = server.recv().expect("the request should not fail"); + assert_eq!(request.url(), "/data/currency.json"); + let mut data = b"{}".to_owned(); + let cursor = std::io::Cursor::new(&mut data); + request + .respond(Response::new(StatusCode(200), vec![], cursor, None, None)) + .expect("the response should go through"); + }); + let result = super::download_to_file( + &PathBuf::from("currency.json"), + "http://127.0.0.1:3090/data/currency.json", + Duration::from_millis(2000), + ); + let mut result = result.expect("this should succeed"); + let mut string = String::new(); + result + .read_to_string(&mut string) + .expect("the file should exist"); + assert_eq!(string, "{}"); + server2.unblock(); + thread_handle.join().unwrap(); + drop(server2); + drop(lock); + } +}