use std::path::{Path, PathBuf};
use super::load;
use crate::error::Error;
use crate::manifest::Manifest;
fn write(dir: &Path, relative: &str, body: &str) -> PathBuf {
let path = dir.join(relative);
if let Some(parent) = path.parent() {
std::fs::create_dir_all(parent).expect("mkdir");
}
std::fs::write(&path, body).expect("write");
path
}
fn names(manifest: &Manifest) -> Vec<&str> {
manifest.commands.iter().map(|c| c.name.as_str()).collect()
}
#[test]
fn a_missing_import_file_errors_naming_the_path() {
let dir = tempfile::tempdir().expect("tempdir");
let root = write(dir.path(), "root.yaml", "imports: [ghost.yaml]\n");
let err = load(&root, dir.path()).expect_err("missing import file");
let text = err.to_string();
assert!(
text.contains("ghost.yaml"),
"names the missing path: {text}"
);
assert!(matches!(err, Error::ImportMissing { .. }));
}
#[test]
fn a_missing_import_directory_errors_naming_the_path() {
let dir = tempfile::tempdir().expect("tempdir");
let root = write(dir.path(), "root.yaml", "imports: [conf.d/]\n");
let err = load(&root, dir.path()).expect_err("missing import directory");
let text = err.to_string();
assert!(text.contains("conf.d"), "names the missing path: {text}");
assert!(matches!(err, Error::ImportMissing { .. }));
}
#[test]
fn an_empty_import_directory_is_accepted() {
let dir = tempfile::tempdir().expect("tempdir");
std::fs::create_dir_all(dir.path().join("conf.d")).expect("mkdir");
let root = write(
dir.path(),
"root.yaml",
"imports: [conf.d/]\ncommands: []\n",
);
let (manifest, _) = load(&root, dir.path()).expect("an empty declared directory is fine");
assert!(manifest.commands.is_empty());
}
#[test]
fn a_directory_entry_sorts_lexically_and_ignores_non_yaml_files() {
let dir = tempfile::tempdir().expect("tempdir");
write(dir.path(), "conf.d/b.yaml", "commands:\n - name: b\n");
write(dir.path(), "conf.d/a.yml", "commands:\n - name: a\n");
write(dir.path(), "conf.d/notes.txt", "not yaml\n");
let root = write(dir.path(), "root.yaml", "imports: [conf.d/]\n");
let (manifest, _) = load(&root, dir.path()).expect("directory expands");
assert_eq!(
names(&manifest),
vec!["a", "b"],
"a.yml sorts before b.yaml; notes.txt is ignored"
);
}
#[test]
fn relative_paths_resolve_against_the_importing_file_not_the_root() {
let dir = tempfile::tempdir().expect("tempdir");
write(
dir.path(),
"sub/sibling.yaml",
"commands:\n - name: sibling\n",
);
write(
dir.path(),
"sub/frag.yaml",
"imports: [sibling.yaml]\ncommands:\n - name: frag\n",
);
let root = write(dir.path(), "root.yaml", "imports: [sub/frag.yaml]\n");
let (manifest, _) = load(&root, dir.path())
.expect("frag.yaml's own import resolves against sub/, not the root's dir");
assert_eq!(names(&manifest), vec!["frag", "sibling"]);
}
#[test]
fn an_absolute_path_outside_the_project_root_loads() {
let project = tempfile::tempdir().expect("tempdir");
let outside = tempfile::tempdir().expect("tempdir");
let fragment = write(
outside.path(),
"rules.yaml",
"commands:\n - name: outside\n",
);
let root = write(
project.path(),
"root.yaml",
&format!("imports: [{}]\n", fragment.display()),
);
let (manifest, _) = load(&root, project.path()).expect("an absolute path is used as written");
assert_eq!(names(&manifest), vec!["outside"]);
}