use std::fs;
use std::os::unix::fs::symlink;
use std::path::PathBuf;
use std::sync::Mutex;
use super::{FromCheckout, link_into, to_link};
use crate::error::Error;
use crate::fixtures;
static CWD_GUARD: Mutex<()> = Mutex::new(());
struct RestoreCwd(PathBuf);
impl Drop for RestoreCwd {
fn drop(&mut self) {
let _ = std::env::set_current_dir(&self.0);
}
}
#[test]
fn link_into_creates_symlinks_that_resolve_into_the_checkout() {
let repo = fixtures::checkout(&[
(".gitignore", "config/local.json\n"),
("config/local.json", r#"{"real":true}"#),
]);
let release = tempfile::tempdir().expect("release tempdir");
let paths = to_link(repo.path()).expect("computes");
link_into(release.path(), repo.path(), &paths).expect("links");
let linked_path = release.path().join("config").join("local.json");
assert!(linked_path.is_symlink());
let contents = fs::read_to_string(&linked_path).expect("read through symlink");
assert_eq!(contents, r#"{"real":true}"#);
}
#[test]
fn link_into_accepts_links_an_earlier_attempt_already_made() {
let repo = fixtures::checkout(&[
(".gitignore", "config/local.json\n"),
("config/local.json", r#"{"real":true}"#),
]);
let release = tempfile::tempdir().expect("release tempdir");
let paths = to_link(repo.path()).expect("computes");
link_into(release.path(), repo.path(), &paths).expect("first attempt links");
link_into(release.path(), repo.path(), &paths).expect("second attempt accepts its own links");
let contents =
fs::read_to_string(release.path().join("config/local.json")).expect("read through symlink");
assert_eq!(contents, r#"{"real":true}"#);
}
#[test]
fn link_into_refuses_a_link_that_points_elsewhere() {
let repo = fixtures::checkout(&[
(".gitignore", "config/local.json\n"),
("config/local.json", r#"{"real":true}"#),
]);
let release = tempfile::tempdir().expect("release tempdir");
let elsewhere = tempfile::tempdir().expect("elsewhere tempdir");
fs::create_dir_all(release.path().join("config")).expect("config dir");
symlink(elsewhere.path(), release.path().join("config/local.json")).expect("stray link");
let paths = to_link(repo.path()).expect("computes");
let err = link_into(release.path(), repo.path(), &paths)
.expect_err("a link to another target is a collision");
assert!(matches!(err, Error::Config(_)));
assert!(err.to_string().contains(".shepignore"));
}
#[test]
fn link_into_refuses_a_real_file_in_the_release() {
let repo = fixtures::checkout(&[
(".gitignore", "config/local.json\n"),
("config/local.json", r#"{"real":true}"#),
]);
let release = tempfile::tempdir().expect("release tempdir");
fs::create_dir_all(release.path().join("config")).expect("config dir");
fs::write(release.path().join("config/local.json"), "built").expect("real file");
let paths = to_link(repo.path()).expect("computes");
let err =
link_into(release.path(), repo.path(), &paths).expect_err("a real file is a collision");
assert!(matches!(err, Error::Config(_)));
}
#[test]
fn link_into_resolves_even_when_checkout_is_relative() {
let repo = fixtures::checkout(&[
(".gitignore", "config/local.json\n"),
("config/local.json", r#"{"real":true}"#),
]);
let release = tempfile::tempdir().expect("release tempdir");
let paths = vec![PathBuf::from("config/local.json")];
let _guard = CWD_GUARD.lock().expect("cwd guard poisoned");
let _restore = RestoreCwd(std::env::current_dir().expect("read cwd"));
std::env::set_current_dir(repo.path().parent().expect("repo has a parent"))
.expect("chdir into repo's parent");
let relative_checkout = PathBuf::from(repo.path().file_name().expect("repo has a name"));
link_into(release.path(), &relative_checkout, &FromCheckout::of(paths))
.expect("links despite a relative checkout");
let linked_path = release.path().join("config").join("local.json");
let contents = fs::read_to_string(&linked_path).expect("read through symlink");
assert_eq!(contents, r#"{"real":true}"#);
}
#[test]
fn link_into_fails_loudly_when_checkout_does_not_exist() {
let release = tempfile::tempdir().expect("release tempdir");
let missing_checkout = release.path().join("no-such-checkout");
let paths = vec![PathBuf::from("config/local.json")];
let err = link_into(release.path(), &missing_checkout, &FromCheckout::of(paths))
.expect_err("a checkout that does not exist cannot be canonicalised");
assert!(matches!(err, Error::Io { .. }));
}