manifest decode & profile validate optimization
This commit is contained in:
@@ -62,3 +62,52 @@ impl Fetcher for BlockingHttpFetcher {
|
||||
self.fetch_bytes(uri)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use std::io::{Read, Write};
|
||||
use std::net::TcpListener;
|
||||
use std::thread;
|
||||
|
||||
fn spawn_one_shot_http_server(status_line: &'static str, body: &'static [u8]) -> String {
|
||||
let listener = TcpListener::bind(("127.0.0.1", 0)).expect("bind");
|
||||
let addr = listener.local_addr().expect("addr");
|
||||
thread::spawn(move || {
|
||||
let (mut stream, _) = listener.accept().expect("accept");
|
||||
let mut buf = [0u8; 1024];
|
||||
let _ = stream.read(&mut buf);
|
||||
let hdr = format!(
|
||||
"{status_line}\r\nContent-Length: {}\r\nConnection: close\r\n\r\n",
|
||||
body.len()
|
||||
);
|
||||
stream.write_all(hdr.as_bytes()).expect("write hdr");
|
||||
stream.write_all(body).expect("write body");
|
||||
});
|
||||
format!("http://{}/", addr)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fetch_bytes_returns_body_on_success() {
|
||||
let url = spawn_one_shot_http_server("HTTP/1.1 200 OK", b"hello");
|
||||
let http = BlockingHttpFetcher::new(HttpFetcherConfig {
|
||||
timeout: Duration::from_secs(2),
|
||||
..HttpFetcherConfig::default()
|
||||
})
|
||||
.expect("http");
|
||||
let got = http.fetch_bytes(&url).expect("fetch");
|
||||
assert_eq!(got, b"hello");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fetch_bytes_rejects_non_success_status() {
|
||||
let url = spawn_one_shot_http_server("HTTP/1.1 404 Not Found", b"");
|
||||
let http = BlockingHttpFetcher::new(HttpFetcherConfig {
|
||||
timeout: Duration::from_secs(2),
|
||||
..HttpFetcherConfig::default()
|
||||
})
|
||||
.expect("http");
|
||||
let err = http.fetch_bytes(&url).unwrap_err();
|
||||
assert!(err.contains("http status"), "{err}");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -80,3 +80,39 @@ fn normalize_rsync_base_uri(s: &str) -> String {
|
||||
format!("{s}/")
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn local_dir_rsync_fetcher_collects_files_and_normalizes_base_uri() {
|
||||
let tmp = tempfile::tempdir().expect("tempdir");
|
||||
std::fs::create_dir_all(tmp.path().join("nested")).expect("mkdir");
|
||||
std::fs::write(tmp.path().join("a.mft"), b"a").expect("write");
|
||||
std::fs::write(tmp.path().join("nested").join("b.roa"), b"b").expect("write");
|
||||
|
||||
let f = LocalDirRsyncFetcher::new(tmp.path());
|
||||
let mut objects = f
|
||||
.fetch_objects("rsync://example.net/repo")
|
||||
.expect("fetch_objects");
|
||||
objects.sort_by(|(a, _), (b, _)| a.cmp(b));
|
||||
|
||||
assert_eq!(objects.len(), 2);
|
||||
assert_eq!(objects[0].0, "rsync://example.net/repo/a.mft");
|
||||
assert_eq!(objects[0].1, b"a");
|
||||
assert_eq!(objects[1].0, "rsync://example.net/repo/nested/b.roa");
|
||||
assert_eq!(objects[1].1, b"b");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn local_dir_rsync_fetcher_reports_read_dir_errors() {
|
||||
let tmp = tempfile::tempdir().expect("tempdir");
|
||||
let missing = tmp.path().join("missing");
|
||||
let f = LocalDirRsyncFetcher::new(missing);
|
||||
let err = f.fetch_objects("rsync://example.net/repo").unwrap_err();
|
||||
match err {
|
||||
RsyncFetchError::Fetch(msg) => assert!(!msg.is_empty()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -142,3 +142,67 @@ fn walk_dir_collect(
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn normalize_rsync_base_uri_appends_slash_when_missing() {
|
||||
assert_eq!(
|
||||
normalize_rsync_base_uri("rsync://example.net/repo"),
|
||||
"rsync://example.net/repo/".to_string()
|
||||
);
|
||||
assert_eq!(
|
||||
normalize_rsync_base_uri("rsync://example.net/repo/"),
|
||||
"rsync://example.net/repo/".to_string()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn walk_dir_collect_collects_files_and_normalizes_backslashes_in_uri() {
|
||||
let temp = tempfile::tempdir().expect("tempdir");
|
||||
let root = temp.path();
|
||||
std::fs::create_dir_all(root.join("sub")).expect("mkdir");
|
||||
std::fs::write(root.join("sub").join("a.cer"), b"x").expect("write");
|
||||
std::fs::write(root.join("b\\c.mft"), b"y").expect("write backslash file");
|
||||
|
||||
let mut out: Vec<(String, Vec<u8>)> = Vec::new();
|
||||
walk_dir_collect(root, root, "rsync://example.net/repo/", &mut out).expect("walk");
|
||||
out.sort_by(|a, b| a.0.cmp(&b.0));
|
||||
|
||||
assert_eq!(out.len(), 2);
|
||||
assert_eq!(out[0].0, "rsync://example.net/repo/b/c.mft");
|
||||
assert_eq!(out[0].1, b"y");
|
||||
assert_eq!(out[1].0, "rsync://example.net/repo/sub/a.cer");
|
||||
assert_eq!(out[1].1, b"x");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn system_rsync_fetcher_reports_spawn_and_exit_errors() {
|
||||
let dst = tempfile::tempdir().expect("tempdir");
|
||||
|
||||
// 1) Spawn error.
|
||||
let f = SystemRsyncFetcher::new(SystemRsyncConfig {
|
||||
rsync_bin: PathBuf::from("/this/does/not/exist/rsync"),
|
||||
timeout: Duration::from_secs(1),
|
||||
extra_args: Vec::new(),
|
||||
});
|
||||
let e = f
|
||||
.run_rsync("rsync://example.net/repo/", dst.path())
|
||||
.expect_err("spawn must fail");
|
||||
assert!(e.contains("rsync spawn failed:"), "{e}");
|
||||
|
||||
// 2) Non-zero exit status.
|
||||
let f = SystemRsyncFetcher::new(SystemRsyncConfig {
|
||||
rsync_bin: PathBuf::from("false"),
|
||||
timeout: Duration::from_secs(1),
|
||||
extra_args: Vec::new(),
|
||||
});
|
||||
let e = f
|
||||
.run_rsync("rsync://example.net/repo/", dst.path())
|
||||
.expect_err("false must fail");
|
||||
assert!(e.contains("rsync failed:"), "{e}");
|
||||
assert!(e.contains("status="), "{e}");
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user