use std::borrow::Cow;
use monty_types::{
CallArgs, GetenvArgs, MontyObject, MontyPath, OsFunctionCall, PathStringDataArgs, normalize_virtual_path,
validate_cwd,
};
#[test]
fn virtual_paths_normalize_lexically() {
for (path, expected) in [
("", "/"),
("/", "/"),
("//", "/"),
("/data/main.py", "/data/main.py"),
("./data//sub/../main.py/", "/data/main.py"),
("/data/./sub/../main.py", "/data/main.py"),
("/../..", "/"),
("../../file", "/file"),
("/a/../../b", "/b"),
("/données/日本語/../file", "/données/file"),
("/a\\b", "/a\\b"),
] {
let normalized = normalize_virtual_path(path);
assert_eq!(normalized, expected, "{path:?}");
assert!(matches!(normalize_virtual_path(&normalized), Cow::Borrowed(_)));
}
}
#[test]
fn cwd_validation() {
for (cwd, expected) in [
("/", "/"),
("///", "/"),
("/data/", "/data"),
("/data/../x/./", "/data/../x/."),
] {
assert_eq!(validate_cwd(cwd).unwrap(), expected, "{cwd:?}");
}
for (cwd, message) in [
("", "cwd must be an absolute POSIX path: \"\""),
("data", "cwd must be an absolute POSIX path: \"data\""),
("data\0", "cwd must not contain NUL bytes: \"data\\0\""),
("/data\0", "cwd must not contain NUL bytes: \"/data\\0\""),
] {
assert_eq!(validate_cwd(cwd).unwrap_err(), message, "{cwd:?}");
}
}
#[test]
fn monty_path_preserves_unvalidated_input() {
for raw in [
"/data/bad\0/../file".to_owned(),
format!("/data/{}/../file", "a".repeat(256)),
] {
let path = MontyPath::new(raw.clone());
assert_eq!(path.as_str(), raw);
assert_eq!(normalize_virtual_path(&path), "/data/file");
assert_eq!(path.into_string(), raw);
}
}
#[test]
fn callback_normalization_only_changes_filesystem_paths() {
let raw = "/data/sub/../file";
let call = OsFunctionCall::WriteText(PathStringDataArgs {
path: MontyPath::new(raw.to_owned()),
data: raw.to_owned(),
});
assert_eq!(
positional(&call.clone().to_args()),
vec![
MontyObject::path("/data/file".to_owned()),
MontyObject::string(raw.to_owned())
]
);
assert_eq!(call.fs_primary_path(), Some(raw));
let args = positional(&OsFunctionCall::Stat(MontyPath::new(String::new())).to_args());
assert_eq!(args, vec![MontyObject::path(String::new())]);
let args = positional(
&OsFunctionCall::Getenv(GetenvArgs {
key: raw.to_owned(),
default: MontyObject::path(raw.to_owned()),
})
.to_args(),
);
assert_eq!(
args,
vec![MontyObject::string(raw.to_owned()), MontyObject::path(raw.to_owned())]
);
}
fn positional(call: &CallArgs) -> Vec<MontyObject> {
call.args().map(|arg| arg.to_owned()).collect()
}