use super::*;
use std::fs;
use tempfile::TempDir;
#[test]
fn validate_enforces_the_shape_the_wordlist_used_to_guarantee() {
let cases: &[(&str, Result<&str, NameError>)] = &[
("ops", Ok("ops")),
("dev", Ok("dev")),
("acme-corp", Ok("acme-corp")),
("s3", Ok("s3")),
(" ops ", Ok("ops")),
("", Err(NameError::Shape)),
(" ", Err(NameError::Shape)),
("Ops!", Err(NameError::Shape)),
("Ops", Err(NameError::Shape)),
("two words", Err(NameError::Shape)),
("a/b", Err(NameError::Shape)),
("..", Err(NameError::Shape)),
("-ops", Err(NameError::Shape)),
("ops-", Err(NameError::Shape)),
("a--b", Err(NameError::Shape)),
("unknown", Err(NameError::Reserved)),
];
for (typed, want) in cases {
let got = validate(typed, &[]);
assert_eq!(got, want.clone().map(str::to_owned), "typed={typed:?}");
}
let at_cap = "a".repeat(MAX_BYTES);
assert_eq!(validate(&at_cap, &[]), Ok(at_cap.clone()));
assert_eq!(
validate(&"a".repeat(MAX_BYTES + 1), &[]),
Err(NameError::TooLong)
);
assert_eq!(validate(DEFAULT_NAME, &[]), Ok(DEFAULT_NAME.to_owned()));
}
#[test]
fn validate_refuses_a_leaf_that_exists_under_any_root() {
let (yog, litany) = (TempDir::new().unwrap(), TempDir::new().unwrap());
let foreign = litany.path().join("workspaces");
let replay = litany.path().join("replays");
fs::create_dir_all(yog.path().join("ops")).unwrap();
fs::create_dir_all(foreign.join("acme")).unwrap();
fs::create_dir_all(foreign.join("20260101T-aa")).unwrap();
fs::create_dir_all(&replay).unwrap();
fs::write(replay.join("notes"), "x").unwrap();
let roots = [yog.path().to_path_buf(), foreign, replay];
assert_eq!(
validate("ops", &roots),
Err(NameError::Taken("ops".to_owned()))
);
assert_eq!(
validate("acme", &roots).unwrap_err(),
NameError::Taken("acme".to_owned()),
"a leaf under litany's own root occupies the name too"
);
assert_eq!(
validate("20260101T-aa", &roots),
Err(NameError::Shape),
"shape is asked first: a litany auto-id is not a name a human may type"
);
assert_eq!(
validate("notes", &roots),
Err(NameError::Taken("notes".to_owned()))
);
assert_eq!(validate("dev", &roots), Ok("dev".to_owned()));
assert_eq!(
validate("ops", &[yog.path().join("gone")]),
Ok("ops".to_owned())
);
}
#[test]
fn every_refusal_carries_an_operator_facing_reason() {
for err in [
NameError::Shape,
NameError::TooLong,
NameError::Reserved,
NameError::Taken("ops".to_owned()),
] {
assert!(!err.to_string().is_empty());
}
assert_eq!(
NameError::Taken("ops".to_owned()).to_string(),
"`ops` already exists — pick another name"
);
assert_eq!(NameError::TooLong.to_string(), "a name is at most 32 bytes");
assert!(NameError::Reserved.to_string().starts_with("`unknown`"));
}