use std::io::Write;
use std::path::Path;
use assay::install::extract::{ExtractError, install_extension_binary, install_lib_tree};
use tempfile::TempDir;
fn build_tar_gz(entries: &[(&str, &[u8])]) -> Vec<u8> {
let mut gz = flate2::write::GzEncoder::new(Vec::new(), flate2::Compression::default());
{
let mut tb = tar::Builder::new(&mut gz);
for (name, bytes) in entries {
let mut header = tar::Header::new_gnu();
header.set_path(name).unwrap();
header.set_size(bytes.len() as u64);
header.set_mode(0o644);
header.set_cksum();
tb.append(&header, *bytes).unwrap();
}
tb.finish().unwrap();
}
gz.finish().unwrap()
}
fn write_archive(dir: &Path, name: &str, entries: &[(&str, &[u8])]) -> std::path::PathBuf {
let p = dir.join(name);
let mut f = std::fs::File::create(&p).unwrap();
f.write_all(&build_tar_gz(entries)).unwrap();
p
}
#[test]
fn extension_extracts_named_member_and_chmods_executable() {
let cache = TempDir::new().unwrap();
let bin_dir = TempDir::new().unwrap();
let body = b"fake assay-engine binary";
let archive = write_archive(
cache.path(),
"assay-engine-0.4.1-x86_64.tar.gz",
&[("assay-engine", body)],
);
let installed = install_extension_binary(&archive, bin_dir.path(), "assay-engine").unwrap();
assert_eq!(installed, bin_dir.path().join("assay-engine"));
assert_eq!(std::fs::read(&installed).unwrap(), body);
#[cfg(unix)]
{
use std::os::unix::fs::PermissionsExt;
let mode = std::fs::metadata(&installed).unwrap().permissions().mode() & 0o777;
assert_eq!(mode, 0o755);
}
}
#[test]
fn extension_finds_member_when_archive_has_multiple_files() {
let cache = TempDir::new().unwrap();
let bin_dir = TempDir::new().unwrap();
let archive = write_archive(
cache.path(),
"engine.tar.gz",
&[
("README.md", b"docs"),
("LICENSE", b"apache"),
("assay-engine", b"the actual binary"),
],
);
let installed = install_extension_binary(&archive, bin_dir.path(), "assay-engine").unwrap();
assert_eq!(std::fs::read(&installed).unwrap(), b"the actual binary");
}
#[test]
fn extension_errors_when_named_member_not_found() {
let cache = TempDir::new().unwrap();
let bin_dir = TempDir::new().unwrap();
let archive = write_archive(
cache.path(),
"engine.tar.gz",
&[("something-else", b"nope")],
);
let err = install_extension_binary(&archive, bin_dir.path(), "assay-engine").unwrap_err();
assert!(matches!(err, ExtractError::BinaryMemberNotFound { .. }));
let msg = err.to_string();
assert!(msg.contains("assay-engine"));
assert!(!bin_dir.path().join("assay-engine").exists());
assert!(!bin_dir.path().join(".assay-engine.tmp").exists());
}
#[test]
fn extension_replaces_existing_file_atomically() {
let cache = TempDir::new().unwrap();
let bin_dir = TempDir::new().unwrap();
let existing = bin_dir.path().join("assay-engine");
std::fs::write(&existing, b"old binary").unwrap();
let archive = write_archive(
cache.path(),
"engine.tar.gz",
&[("assay-engine", b"new binary")],
);
install_extension_binary(&archive, bin_dir.path(), "assay-engine").unwrap();
assert_eq!(std::fs::read(&existing).unwrap(), b"new binary");
assert!(!bin_dir.path().join(".assay-engine.tmp").exists());
}
#[test]
fn extension_creates_bin_dir_when_missing() {
let cache = TempDir::new().unwrap();
let bin_root = TempDir::new().unwrap();
let bin_dir = bin_root.path().join("nested/dir");
assert!(!bin_dir.exists());
let archive = write_archive(cache.path(), "engine.tar.gz", &[("assay-engine", b"bin")]);
install_extension_binary(&archive, &bin_dir, "assay-engine").unwrap();
assert!(bin_dir.join("assay-engine").exists());
}
#[test]
fn lib_extracts_tarball_tree_into_named_subdir() {
let cache = TempDir::new().unwrap();
let lib_dir = TempDir::new().unwrap();
let archive = write_archive(
cache.path(),
"assay-lib-sysops-0.1.0.tar.gz",
&[
("mount.lua", b"return {}"),
("pages/dashboard.lua", b"-- dash"),
("VERSION", b"0.1.0\n"),
],
);
let installed = install_lib_tree(&archive, lib_dir.path(), "sysops").unwrap();
assert_eq!(installed, lib_dir.path().join("sysops"));
assert_eq!(
std::fs::read(installed.join("mount.lua")).unwrap(),
b"return {}"
);
assert_eq!(
std::fs::read(installed.join("pages/dashboard.lua")).unwrap(),
b"-- dash"
);
assert_eq!(
std::fs::read(installed.join("VERSION")).unwrap(),
b"0.1.0\n"
);
}
#[test]
fn lib_replaces_existing_tree() {
let cache = TempDir::new().unwrap();
let lib_dir = TempDir::new().unwrap();
let target = lib_dir.path().join("sysops");
std::fs::create_dir_all(target.join("pages")).unwrap();
std::fs::write(target.join("STALE_FILE"), b"obsolete").unwrap();
std::fs::write(target.join("mount.lua"), b"-- old").unwrap();
let archive = write_archive(
cache.path(),
"assay-lib-sysops-0.1.1.tar.gz",
&[("mount.lua", b"-- new"), ("VERSION", b"0.1.1\n")],
);
install_lib_tree(&archive, lib_dir.path(), "sysops").unwrap();
assert_eq!(std::fs::read(target.join("mount.lua")).unwrap(), b"-- new");
assert!(!target.join("STALE_FILE").exists());
assert!(!lib_dir.path().join(".sysops.new").exists());
}
#[test]
fn lib_recovers_from_leftover_staging_dir() {
let cache = TempDir::new().unwrap();
let lib_dir = TempDir::new().unwrap();
let stale_staging = lib_dir.path().join(".sysops.new");
std::fs::create_dir_all(&stale_staging).unwrap();
std::fs::write(stale_staging.join("garbage.lua"), b"crashed").unwrap();
let archive = write_archive(
cache.path(),
"assay-lib-sysops-0.1.0.tar.gz",
&[("mount.lua", b"return {}")],
);
let installed = install_lib_tree(&archive, lib_dir.path(), "sysops").unwrap();
assert!(installed.join("mount.lua").exists());
assert!(!stale_staging.exists());
}
#[test]
fn lib_creates_lib_dir_when_missing() {
let cache = TempDir::new().unwrap();
let lib_root = TempDir::new().unwrap();
let lib_dir = lib_root.path().join("opt/assay/libs");
assert!(!lib_dir.exists());
let archive = write_archive(
cache.path(),
"assay-lib-sysops-0.1.0.tar.gz",
&[("mount.lua", b"return {}")],
);
install_lib_tree(&archive, &lib_dir, "sysops").unwrap();
assert!(lib_dir.join("sysops/mount.lua").exists());
}