diff --git a/Cargo.lock b/Cargo.lock index 5a704c8..a719a67 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1776,6 +1776,8 @@ dependencies = [ "eyre", "flate2", "reqwest", + "serde", + "serde_json", "sha2", "tar", "tracing", diff --git a/crates/fetcher/Cargo.toml b/crates/fetcher/Cargo.toml index 5b9226b..aa21943 100644 --- a/crates/fetcher/Cargo.toml +++ b/crates/fetcher/Cargo.toml @@ -17,3 +17,5 @@ tar = "0.4" sha2 = "0.10" tracing = "0.1" tracing-subscriber = { version = "0.3", features = ["env-filter"] } +serde = { workspace = true, features = ["derive"] } +serde_json = { workspace = true } diff --git a/crates/fetcher/src/lib.rs b/crates/fetcher/src/lib.rs index 296b5b0..76d739f 100644 --- a/crates/fetcher/src/lib.rs +++ b/crates/fetcher/src/lib.rs @@ -241,34 +241,113 @@ fn extract_tar_gz(reader: R, destination: &Path) -> Result<()> { mod tests { use super::*; use std::fs; + use std::sync::{Arc, Mutex}; + + struct TestProgressTracker { + set_total_called: Arc>, + update_called: Arc>, + finish_called: Arc>, + } + + impl TestProgressTracker { + fn new() -> Self { + Self { + set_total_called: Arc::new(Mutex::new(false)), + update_called: Arc::new(Mutex::new(false)), + finish_called: Arc::new(Mutex::new(false)), + } + } + + fn assert_ok(&self) { + assert!( + *self.set_total_called.lock().unwrap(), + "set_total was not called", + ); + assert!(*self.update_called.lock().unwrap(), "update was not called",); + assert!(*self.finish_called.lock().unwrap(), "finish was not called",); + } + } + + impl ProgressTracker for TestProgressTracker { + fn set_total(&mut self, _total: u64) { + *self.set_total_called.lock().unwrap() = true; + } + + fn update(&mut self, _downloaded: u64) { + *self.update_called.lock().unwrap() = true; + } + + fn finish(&mut self) { + *self.finish_called.lock().unwrap() = true; + } + } + + fn get_fixture_path(filename: &str) -> String { + format!( + "https://raw.githubusercontent.com/ferranbt/bbuilder/refs/heads/main/crates/fetcher/fixtures/{}", + filename + ) + } + + fn ensure_fixture_file(path: &PathBuf) { + const CONTENT_TXT: &[u8] = include_bytes!("../fixtures/content.txt"); + let actual_content = fs::read(path).expect(&format!("Failed to read file at {:?}", path)); + assert_eq!(actual_content, CONTENT_TXT); + } #[test] - fn test_download_readme() { - let source = - "https://raw.githubusercontent.com/ferranbt/bbuilder/refs/heads/main/README.md"; - let destination = PathBuf::from("/tmp/fetcher_test_readme.md"); + fn test_download_content_txt() { + let filename = "content.txt"; + let checksum = "3dc7bc0209231cc61cb7d09c2efdfdf7aacb1f0b098db150780e980fa10d6b7a"; + + let source = get_fixture_path(filename); + let destination = PathBuf::from(format!("/tmp/fetcher_test_{}", filename)); - // Clean up any existing file let _ = fs::remove_file(&destination); - // Download the file with checksum verification - // Expected SHA-256 checksum for the README.md file - let checksum = "79dcadb1ef725cbc30bb3a9a877cff3702e9d3bb28baff74265a9d70a2e8874b"; - let result = fetch(source, &destination, Some(checksum.to_string())); + let mut progress = TestProgressTracker::new(); + + let result = fetch_with_progress( + &source, + &destination, + &mut progress, + Some(checksum.to_string()), + ); assert!( result.is_ok(), - "Failed to download file or verify checksum: {:?}", + "Failed to download {} or verify checksum: {:?}", + filename, result.err() ); - // Verify the file exists - assert!(destination.exists(), "Downloaded file does not exist"); + ensure_fixture_file(&destination); + progress.assert_ok(); + + let _ = fs::remove_file(&destination); + } + + #[test] + fn test_download_content_tar_gz() { + let filename = "content.tar.gz"; + + let source = get_fixture_path(filename); + let destination = PathBuf::from(format!("/tmp/fetcher_test_{}", filename)); + + let _ = fs::remove_file(&destination); + + let mut progress = TestProgressTracker::new(); + + let result = fetch_with_progress(&source, &destination, &mut progress, None); + assert!( + result.is_ok(), + "Failed to download {} or verify checksum: {:?}", + filename, + result.err() + ); - // Verify the file has content - let content = fs::read_to_string(&destination).expect("Failed to read downloaded file"); - assert!(!content.is_empty(), "Downloaded file is empty"); + ensure_fixture_file(&destination.join("content.txt")); + progress.assert_ok(); - // Clean up let _ = fs::remove_file(&destination); } }