use std::fs;
use super::*;
use crate::workflow::test_support::limits;
fn block_on<T>(future: impl std::future::Future<Output = T>) -> T {
tokio::runtime::Builder::new_current_thread()
.enable_all()
.build()
.expect("test runtime")
.block_on(future)
}
#[test]
fn rejects_traversal_and_import_cycles() {
assert!(matches!(
validate_label("//../escape.star"),
Err(WorkflowError::InvalidModuleLabel { .. })
));
let root = tempfile::tempdir().unwrap();
fs::write(
root.path().join("a.star"),
"load(\"//b.star\", \"b\")\na = b",
)
.unwrap();
fs::write(
root.path().join("b.star"),
"load(\"//a.star\", \"a\")\nb = a",
)
.unwrap();
let limits = limits();
let collector = SourceCollector::new(root.path(), &limits).unwrap();
assert!(matches!(
block_on(collector.collect(Path::new("a.star"))),
Err(WorkflowError::ImportCycle { .. })
));
}
#[test]
fn collects_each_loaded_source_once_in_sorted_manifest() {
let root = tempfile::tempdir().unwrap();
fs::write(root.path().join("common.star"), "value = 1").unwrap();
fs::write(
root.path().join("entry.star"),
"load(\"//common.star\", \"value\")\nresult = value",
)
.unwrap();
let limits = limits();
let collected = block_on(
SourceCollector::new(root.path(), &limits)
.unwrap()
.collect(Path::new("entry.star")),
)
.unwrap();
assert_eq!(
collected.sources.keys().cloned().collect::<Vec<_>>(),
vec!["//common.star".to_owned(), "//entry.star".to_owned()]
);
}
#[cfg(unix)]
#[test]
fn rejects_source_symlink_substitution() {
use std::os::unix::fs::symlink;
let root = tempfile::tempdir().unwrap();
let outside = tempfile::NamedTempFile::new().unwrap();
fs::write(outside.path(), "WORKFLOW = None").unwrap();
symlink(outside.path(), root.path().join("entry.star")).unwrap();
let limits = limits();
let result = block_on(
SourceCollector::new(root.path(), &limits)
.unwrap()
.collect(Path::new("entry.star")),
);
assert!(matches!(result, Err(WorkflowError::SourceSymlink { .. })));
}