#![cfg(unix)] use sha2::{Digest, Sha256}; use std::fs; use std::os::unix::fs::PermissionsExt; use std::path::{Path, PathBuf}; use std::process::Command; use tempfile::TempDir; const VERSION: &str = "9.8.7-rc3"; fn write_executable(path: &Path, body: &str) { fs::write(path, body).unwrap(); let mut permissions = fs::metadata(path).unwrap().permissions(); permissions.set_mode(0o755); fs::set_permissions(path, permissions).unwrap(); } struct InstallerFixture { root: TempDir, fake_bin: PathBuf, archive: PathBuf, checksum: PathBuf, curl_log: PathBuf, git_log: PathBuf, version: String, } impl InstallerFixture { fn new(version: &str) -> Self { let root = tempfile::tempdir().unwrap(); let fake_bin = root.path().join("fake-bin"); let stage = root.path().join("stage/dosh"); fs::create_dir_all(fake_bin.as_path()).unwrap(); fs::create_dir_all(stage.join("bin")).unwrap(); fs::write(stage.join("VERSION"), format!("{version}\n")).unwrap(); write_executable( &stage.join("bin/dosh-client"), "#!/bin/sh\nprintf 'fixture dosh\\n'\n", ); let archive = root.path().join("dosh-macos-aarch64.tar.gz"); let status = Command::new("tar") .args(["-C", root.path().join("stage").to_str().unwrap(), "-czf"]) .arg(&archive) .arg("dosh") .status() .unwrap(); assert!(status.success()); let digest = Sha256::digest(fs::read(&archive).unwrap()); let checksum = root.path().join("dosh-macos-aarch64.tar.gz.sha256"); fs::write( &checksum, format!("{digest:x} dosh-macos-aarch64.tar.gz\n"), ) .unwrap(); write_executable( &fake_bin.join("uname"), "#!/bin/sh\ncase \"${1:-}\" in -s) echo Darwin;; -m) echo arm64;; *) echo Darwin;; esac\n", ); let curl_log = root.path().join("curl.log"); let git_log = root.path().join("git.log"); write_executable( &fake_bin.join("curl"), r#"#!/bin/sh printf '%s\n' "$*" >>"$TEST_CURL_LOG" out= previous= url= for arg in "$@"; do if [ "$previous" = "-o" ]; then out="$arg"; fi previous="$arg" case "$arg" in http://*|https://*) url="$arg";; esac done case "$url" in */raw/branch/main/Cargo.toml) printf '[package]\nname = "dosh"\nversion = "%s"\n' "$TEST_RELEASE_VERSION" ;; *.sha256) cp "$TEST_ARCHIVE_CHECKSUM" "$out" ;; */releases/download/*) [ "${TEST_FAIL_ARCHIVE:-0}" = 1 ] && exit 22 cp "$TEST_ARCHIVE" "$out" ;; *) exit 22;; esac "#, ); Self { root, fake_bin, archive, checksum, curl_log, git_log, version: version.to_string(), } } fn command(&self, cwd: &Path) -> Command { let mut command = Command::new("sh"); command .arg(format!("{}/install.sh", env!("CARGO_MANIFEST_DIR"))) .args([ "client", "--repo", "https://example.invalid/Palav/dosh.git", "--prefix", ]) .arg(self.root.path().join("prefix")) .current_dir(cwd) .env("HOME", self.root.path().join("home")) .env( "PATH", format!( "{}:{}", self.fake_bin.display(), std::env::var("PATH").unwrap_or_default() ), ) .env("DOSH_BINARY_REQUIRED", "1") .env("TEST_RELEASE_VERSION", &self.version) .env("TEST_ARCHIVE", &self.archive) .env("TEST_ARCHIVE_CHECKSUM", &self.checksum) .env("TEST_CURL_LOG", &self.curl_log) .env("TEST_GIT_LOG", &self.git_log); command } fn installed_client(&self) -> PathBuf { self.root.path().join("prefix/bin/dosh-client") } } #[test] fn unix_installer_uses_repository_version_even_inside_an_unrelated_rust_project() { let fixture = InstallerFixture::new(VERSION); let unrelated = fixture.root.path().join("unrelated"); fs::create_dir_all(&unrelated).unwrap(); fs::write( unrelated.join("Cargo.toml"), "[package]\nname = \"not-dosh\"\nversion = \"99.0.0\"\n", ) .unwrap(); let output = fixture.command(&unrelated).output().unwrap(); assert!( output.status.success(), "installer failed: {}", String::from_utf8_lossy(&output.stderr) ); assert!(fixture.installed_client().is_file()); let requests = fs::read_to_string(&fixture.curl_log).unwrap(); assert!(requests.contains("/raw/branch/main/Cargo.toml")); assert!(requests.contains(&format!( "/releases/download/v{VERSION}/dosh-macos-aarch64.tar.gz" ))); assert!(!requests.contains("/releases/latest")); assert!(!requests.contains("99.0.0")); } #[test] fn unix_installer_source_fallback_checks_out_the_same_release_tag() { let fixture = InstallerFixture::new(VERSION); write_executable( &fixture.fake_bin.join("git"), r#"#!/bin/sh printf '%s\n' "$*" >>"$TEST_GIT_LOG" if [ "${1:-}" = clone ]; then for destination in "$@"; do :; done mkdir -p "$destination/.git" fi "#, ); write_executable( &fixture.fake_bin.join("cargo"), "#!/bin/sh\nmkdir -p target/release\nprintf '#!/bin/sh\\n' >target/release/dosh-client\nchmod +x target/release/dosh-client\n", ); let cwd = fixture.root.path().join("plain"); fs::create_dir_all(&cwd).unwrap(); let mut command = fixture.command(&cwd); command .env("DOSH_BINARY_REQUIRED", "0") .env("TEST_FAIL_ARCHIVE", "1"); let output = command.output().unwrap(); assert!( output.status.success(), "installer failed: {}", String::from_utf8_lossy(&output.stderr) ); assert!(fixture.installed_client().is_file()); let git = fs::read_to_string(&fixture.git_log).unwrap(); assert!(git.contains(&format!("clone --depth 1 --branch v{VERSION}"))); assert!(!git.contains("--branch main")); } #[test] fn unix_installer_resolves_a_stable_repository_version_without_latest_redirects() { let version = "1.2.3"; let fixture = InstallerFixture::new(version); let cwd = fixture.root.path().join("plain"); fs::create_dir_all(&cwd).unwrap(); let output = fixture.command(&cwd).output().unwrap(); assert!( output.status.success(), "installer failed: {}", String::from_utf8_lossy(&output.stderr) ); let requests = fs::read_to_string(&fixture.curl_log).unwrap(); assert!(requests.contains(&format!( "/releases/download/v{version}/dosh-macos-aarch64.tar.gz" ))); assert!(!requests.contains("/releases/latest")); } #[test] fn unix_installer_repairs_a_legacy_updater_that_requests_its_old_tag() { let fixture = InstallerFixture::new(VERSION); let cwd = fixture.root.path().join("plain"); fs::create_dir_all(&cwd).unwrap(); let mut command = fixture.command(&cwd); command.env("DOSH_BINARY_VERSION", "v0.1.17"); let output = command.output().unwrap(); assert!( output.status.success(), "installer failed: {}", String::from_utf8_lossy(&output.stderr) ); let requests = fs::read_to_string(&fixture.curl_log).unwrap(); assert!(requests.contains(&format!( "/releases/download/v{VERSION}/dosh-macos-aarch64.tar.gz" ))); assert!(!requests.contains("/releases/download/v0.1.17/")); } #[test] fn unix_installer_honors_an_explicit_exact_release_pin() { let pinned = "0.1.17"; let fixture = InstallerFixture::new(pinned); let cwd = fixture.root.path().join("plain"); fs::create_dir_all(&cwd).unwrap(); let mut command = fixture.command(&cwd); command .env("TEST_RELEASE_VERSION", VERSION) .env("DOSH_BINARY_VERSION", format!("v{pinned}")) .env("DOSH_BINARY_EXACT", "1"); let output = command.output().unwrap(); assert!( output.status.success(), "installer failed: {}", String::from_utf8_lossy(&output.stderr) ); let requests = fs::read_to_string(&fixture.curl_log).unwrap(); assert!(requests.contains(&format!( "/releases/download/v{pinned}/dosh-macos-aarch64.tar.gz" ))); assert!(!requests.contains(&format!("/releases/download/v{VERSION}/"))); }