191 lines
7.0 KiB
Rust
191 lines
7.0 KiB
Rust
use std::path::PathBuf;
|
|
use std::process::Command;
|
|
use std::os::unix::fs::MetadataExt;
|
|
|
|
fn wrapper_path() -> PathBuf {
|
|
PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("scripts/cir/cir-rsync-wrapper")
|
|
}
|
|
|
|
fn real_rsync() -> Option<String> {
|
|
let candidate = "/usr/bin/rsync";
|
|
if std::path::Path::new(candidate).exists() {
|
|
return Some(candidate.to_string());
|
|
}
|
|
None
|
|
}
|
|
|
|
#[test]
|
|
fn cir_rsync_wrapper_passes_through_help() {
|
|
let Some(real) = real_rsync() else {
|
|
return;
|
|
};
|
|
let out = Command::new(wrapper_path())
|
|
.env("REAL_RSYNC_BIN", real)
|
|
.arg("-h")
|
|
.output()
|
|
.expect("run wrapper -h");
|
|
assert!(out.status.success(), "stderr={}", String::from_utf8_lossy(&out.stderr));
|
|
let stdout = String::from_utf8_lossy(&out.stdout);
|
|
let stderr = String::from_utf8_lossy(&out.stderr);
|
|
assert!(stdout.contains("rsync") || stderr.contains("rsync"));
|
|
}
|
|
|
|
#[test]
|
|
fn cir_rsync_wrapper_rewrites_rsync_source_to_mirror_tree() {
|
|
let Some(real) = real_rsync() else {
|
|
return;
|
|
};
|
|
let td = tempfile::tempdir().expect("tempdir");
|
|
let mirror_root = td.path().join("mirror");
|
|
let dest_root = td.path().join("dest");
|
|
let repo_root = mirror_root.join("example.net").join("repo");
|
|
std::fs::create_dir_all(repo_root.join("nested")).expect("mkdirs");
|
|
std::fs::write(repo_root.join("a.roa"), b"roa").expect("write roa");
|
|
std::fs::write(repo_root.join("nested").join("b.txt"), b"txt").expect("write txt");
|
|
std::fs::create_dir_all(&dest_root).expect("mkdir dest");
|
|
|
|
let out = Command::new(wrapper_path())
|
|
.env("REAL_RSYNC_BIN", real)
|
|
.env("CIR_MIRROR_ROOT", &mirror_root)
|
|
.args([
|
|
"-rt",
|
|
"--address",
|
|
"127.0.0.1",
|
|
"--contimeout=10",
|
|
"--include=*/",
|
|
"--include=*.roa",
|
|
"--exclude=*",
|
|
"rsync://example.net/repo/",
|
|
dest_root.to_string_lossy().as_ref(),
|
|
])
|
|
.output()
|
|
.expect("run wrapper rewrite");
|
|
assert!(out.status.success(), "stderr={}", String::from_utf8_lossy(&out.stderr));
|
|
assert_eq!(std::fs::read(dest_root.join("a.roa")).expect("read copied roa"), b"roa");
|
|
assert!(!dest_root.join("nested").join("b.txt").exists());
|
|
}
|
|
|
|
#[test]
|
|
fn cir_rsync_wrapper_rewrites_module_root_without_trailing_slash_as_contents() {
|
|
let Some(real) = real_rsync() else {
|
|
return;
|
|
};
|
|
let td = tempfile::tempdir().expect("tempdir");
|
|
let mirror_root = td.path().join("mirror");
|
|
let dest_root = td.path().join("dest");
|
|
let repo_root = mirror_root.join("example.net").join("repo");
|
|
std::fs::create_dir_all(repo_root.join("sub")).expect("mkdirs");
|
|
std::fs::write(repo_root.join("root.cer"), b"cer").expect("write cer");
|
|
std::fs::write(repo_root.join("sub").join("child.roa"), b"roa").expect("write roa");
|
|
std::fs::create_dir_all(&dest_root).expect("mkdir dest");
|
|
|
|
let out = Command::new(wrapper_path())
|
|
.env("REAL_RSYNC_BIN", real)
|
|
.env("CIR_MIRROR_ROOT", &mirror_root)
|
|
.args([
|
|
"-rt",
|
|
"--include=*/",
|
|
"--include=*.cer",
|
|
"--include=*.roa",
|
|
"--exclude=*",
|
|
"rsync://example.net/repo",
|
|
dest_root.to_string_lossy().as_ref(),
|
|
])
|
|
.output()
|
|
.expect("run wrapper rewrite");
|
|
assert!(out.status.success(), "stderr={}", String::from_utf8_lossy(&out.stderr));
|
|
assert_eq!(std::fs::read(dest_root.join("root.cer")).expect("read copied root cer"), b"cer");
|
|
assert_eq!(
|
|
std::fs::read(dest_root.join("sub").join("child.roa")).expect("read copied child roa"),
|
|
b"roa"
|
|
);
|
|
assert!(!dest_root.join("repo").exists(), "module root must not be nested under destination");
|
|
}
|
|
|
|
#[test]
|
|
fn cir_rsync_wrapper_requires_mirror_root_for_rsync_source() {
|
|
let Some(real) = real_rsync() else {
|
|
return;
|
|
};
|
|
let td = tempfile::tempdir().expect("tempdir");
|
|
let dest_root = td.path().join("dest");
|
|
std::fs::create_dir_all(&dest_root).expect("mkdir dest");
|
|
|
|
let out = Command::new(wrapper_path())
|
|
.env("REAL_RSYNC_BIN", real)
|
|
.args(["-rt", "rsync://example.net/repo/", dest_root.to_string_lossy().as_ref()])
|
|
.output()
|
|
.expect("run wrapper missing env");
|
|
assert!(!out.status.success());
|
|
assert!(String::from_utf8_lossy(&out.stderr).contains("CIR_MIRROR_ROOT"));
|
|
}
|
|
|
|
#[test]
|
|
fn cir_rsync_wrapper_leaves_local_source_untouched() {
|
|
let Some(real) = real_rsync() else {
|
|
return;
|
|
};
|
|
let td = tempfile::tempdir().expect("tempdir");
|
|
let src_root = td.path().join("src");
|
|
let dest_root = td.path().join("dest");
|
|
std::fs::create_dir_all(&src_root).expect("mkdir src");
|
|
std::fs::create_dir_all(&dest_root).expect("mkdir dest");
|
|
std::fs::write(src_root.join("x.cer"), b"x").expect("write source");
|
|
|
|
let out = Command::new(wrapper_path())
|
|
.env("REAL_RSYNC_BIN", real)
|
|
.args([
|
|
"-rt",
|
|
src_root.to_string_lossy().as_ref(),
|
|
dest_root.to_string_lossy().as_ref(),
|
|
])
|
|
.output()
|
|
.expect("run wrapper local passthrough");
|
|
assert!(out.status.success(), "stderr={}", String::from_utf8_lossy(&out.stderr));
|
|
assert_eq!(std::fs::read(dest_root.join("src").join("x.cer")).expect("read copied file"), b"x");
|
|
}
|
|
|
|
#[test]
|
|
fn cir_rsync_wrapper_local_link_mode_uses_hardlinks_for_rewritten_sources() {
|
|
let Some(real) = real_rsync() else {
|
|
return;
|
|
};
|
|
let td = tempfile::tempdir().expect("tempdir");
|
|
let mirror_root = td.path().join("mirror");
|
|
let dest_root = td.path().join("dest");
|
|
let repo_root = mirror_root.join("example.net").join("repo");
|
|
std::fs::create_dir_all(repo_root.join("nested")).expect("mkdirs");
|
|
let src_file = repo_root.join("a.roa");
|
|
let src_nested = repo_root.join("nested").join("b.cer");
|
|
std::fs::write(&src_file, b"roa").expect("write roa");
|
|
std::fs::write(&src_nested, b"cer").expect("write cer");
|
|
std::fs::create_dir_all(&dest_root).expect("mkdir dest");
|
|
|
|
let out = Command::new(wrapper_path())
|
|
.env("REAL_RSYNC_BIN", real)
|
|
.env("CIR_MIRROR_ROOT", &mirror_root)
|
|
.env("CIR_LOCAL_LINK_MODE", "1")
|
|
.args([
|
|
"-rt",
|
|
"--delete",
|
|
"--include=*/",
|
|
"--include=*.roa",
|
|
"--include=*.cer",
|
|
"--exclude=*",
|
|
"rsync://example.net/repo/",
|
|
dest_root.to_string_lossy().as_ref(),
|
|
])
|
|
.output()
|
|
.expect("run wrapper local-link mode");
|
|
assert!(out.status.success(), "stderr={}", String::from_utf8_lossy(&out.stderr));
|
|
|
|
let dst_file = dest_root.join("a.roa");
|
|
let dst_nested = dest_root.join("nested").join("b.cer");
|
|
assert_eq!(std::fs::read(&dst_file).expect("read dest roa"), b"roa");
|
|
assert_eq!(std::fs::read(&dst_nested).expect("read dest cer"), b"cer");
|
|
|
|
let src_meta = std::fs::metadata(&src_file).expect("src metadata");
|
|
let dst_meta = std::fs::metadata(&dst_file).expect("dst metadata");
|
|
assert_eq!(src_meta.ino(), dst_meta.ino(), "expected hardlinked destination file");
|
|
}
|