use aion_package::AwlSource;
use super::{DocumentStoreError, prune, stage};
type TestResult = Result<(), Box<dyn std::error::Error>>;
fn root_of(home: &tempfile::TempDir) -> std::path::PathBuf {
home.path().join("workers/documents")
}
fn source(document: &str) -> AwlSource {
AwlSource::new(
"probe.awl",
document,
[(
"schemas/brief.schema.json",
b"{\"type\":\"object\"}".to_vec(),
)],
)
}
#[test]
fn the_document_and_its_schema_imports_land_at_their_declared_paths() -> TestResult {
let home = tempfile::tempdir()?;
let staged = stage(&root_of(&home), &source("workflow probe\n"), "probe")?;
assert!(staged.path.is_absolute());
assert_eq!(
staged.path.file_name().and_then(|n| n.to_str()),
Some("probe.awl")
);
assert_eq!(std::fs::read_to_string(&staged.path)?, "workflow probe\n");
let root = staged
.path
.parent()
.ok_or("the document must have a parent")?;
assert_eq!(
std::fs::read(root.join("schemas/brief.schema.json"))?,
b"{\"type\":\"object\"}"
);
assert!(
root.starts_with(root_of(&home)),
"{root:?} must live under the server's own document root, not the studio's tree"
);
Ok(())
}
#[test]
fn identical_bytes_share_a_path_and_changed_bytes_do_not() -> TestResult {
let home = tempfile::tempdir()?;
let first = stage(&root_of(&home), &source("workflow probe\n"), "probe")?;
let again = stage(&root_of(&home), &source("workflow probe\n"), "probe")?;
let changed = stage(
&root_of(&home),
&source("workflow probe\n// edited\n"),
"probe",
)?;
assert_eq!(first, again);
assert_ne!(first.digest, changed.digest);
assert_ne!(first.path, changed.path);
assert!(first.path.exists() && changed.path.exists());
Ok(())
}
#[test]
fn prune_reclaims_only_the_snapshots_no_record_names() -> TestResult {
let home = tempfile::tempdir()?;
let root = root_of(&home);
let kept = stage(&root, &source("workflow probe\n"), "probe")?;
let dropped = stage(&root, &source("workflow probe\n// superseded\n"), "probe")?;
let live = std::collections::BTreeSet::from([kept.path.clone()]);
let removed = prune(&root, &live)?;
assert_eq!(removed.len(), 1, "{removed:?}");
assert!(kept.path.exists(), "a snapshot a record still replays");
assert!(!dropped.path.exists(), "a snapshot nothing names");
assert!(
std::fs::read_to_string(&kept.path)? == "workflow probe\n",
"the surviving snapshot must be untouched"
);
Ok(())
}
#[test]
fn prune_over_an_empty_live_set_and_an_absent_root() -> TestResult {
let home = tempfile::tempdir()?;
let root = root_of(&home);
assert!(prune(&root, &std::collections::BTreeSet::new())?.is_empty());
let staged = stage(&root, &source("workflow probe\n"), "probe")?;
assert_eq!(prune(&root, &std::collections::BTreeSet::new())?.len(), 1);
assert!(!staged.path.exists());
Ok(())
}
#[test]
fn an_interrupted_stage_leaves_no_snapshot_and_is_cleaned_by_the_next() -> TestResult {
let home = tempfile::tempdir()?;
let root = root_of(&home);
let staged = stage(&root, &source("workflow probe\n"), "probe")?;
let version = staged
.path
.parent()
.and_then(|parent| parent.file_name())
.and_then(|name| name.to_str())
.ok_or("the staged document must sit in a named directory")?
.to_owned();
std::fs::remove_dir_all(staged.path.parent().ok_or("parent")?)?;
let partial = root.join(format!("{version}.partial"));
std::fs::create_dir_all(&partial)?;
std::fs::write(partial.join("probe.awl"), "half written")?;
let live = std::collections::BTreeSet::new();
let removed = prune(&root, &live)?;
assert_eq!(removed.len(), 1, "a `.partial` tree is not a snapshot");
std::fs::create_dir_all(&partial)?;
let restaged = stage(&root, &source("workflow probe\n"), "probe")?;
assert_eq!(restaged, staged);
assert!(!partial.exists(), "the stale staging tree must be cleared");
assert_eq!(std::fs::read_to_string(&restaged.path)?, "workflow probe\n");
Ok(())
}
#[test]
fn an_escaping_archive_name_is_refused_by_name() -> TestResult {
let home = tempfile::tempdir()?;
let escaping = AwlSource::new(
"probe.awl",
"workflow probe\n",
[("../../escaped.json", b"x".to_vec())],
);
match stage(&root_of(&home), &escaping, "probe") {
Err(DocumentStoreError::UnsafeName { name }) => {
assert_eq!(name, "../../escaped.json");
}
other => return Err(format!("an escaping schema name must refuse: {other:?}").into()),
}
let named_escape = AwlSource::new(
"../probe.awl",
"workflow probe\n",
Vec::<(String, Vec<u8>)>::new(),
);
match stage(&root_of(&home), &named_escape, "probe") {
Err(DocumentStoreError::UnsafeName { name }) => {
assert_eq!(name, "../probe.awl");
Ok(())
}
other => Err(format!("an escaping document name must refuse: {other:?}").into()),
}
}
#[test]
fn a_hostile_workflow_type_cannot_shape_the_directory_name() -> TestResult {
let home = tempfile::tempdir()?;
let staged = stage(&root_of(&home), &source("workflow probe\n"), "../../../etc")?;
let directory = staged
.path
.parent()
.and_then(|parent| parent.file_name())
.and_then(|name| name.to_str())
.ok_or("the staged document must sit in a named directory")?;
assert!(!directory.contains('/'), "{directory}");
assert!(!directory.contains(".."), "{directory}");
assert!(directory.ends_with(&staged.digest), "{directory}");
assert!(staged.path.starts_with(root_of(&home)), "{staged:?}");
Ok(())
}