use std::path::{Path, PathBuf};
use std::process::Command;
fn run(cwd: &Path, args: &[&str]) -> (i32, String) {
let out = Command::new(env!("CARGO_BIN_EXE_str"))
.args(args)
.current_dir(cwd)
.output()
.unwrap();
(
out.status.code().unwrap_or(-1),
format!(
"{}{}",
String::from_utf8_lossy(&out.stdout),
String::from_utf8_lossy(&out.stderr)
),
)
}
fn tmp(name: &str) -> PathBuf {
let p = std::env::temp_dir().join(format!("strdir-{name}-{}", std::process::id()));
let _ = std::fs::remove_dir_all(&p);
p
}
#[test]
fn dir_argument_defaults_to_current_directory() {
let base = tmp("dirdefault");
let cwd = base.join("proj");
std::fs::create_dir_all(&cwd).unwrap();
let (code, out) = run(&cwd, &["init"]);
assert_eq!(code, 0, "{out}");
let bundle = base.join("proj.str");
assert!(bundle.join("._meta").exists(), "{out}");
assert!(bundle.join("._schema").exists(), "{out}");
let (code, out) = run(
&bundle,
&["node", "add", "--type", "a.b", "--title", "T", "--summary", "s"],
);
assert_eq!(code, 0, "{out}");
for args in [
vec!["tree"],
vec!["show"],
vec!["ls"],
vec!["context"],
vec!["norm"],
vec!["validate", "--strict"],
vec!["fmt", "--check"],
vec!["sync"],
] {
let (code, out) = run(&bundle, &args);
assert_eq!(code, 0, "{args:?}: {out}");
}
let (code, out) = run(&bundle, &["spec", "set", "1.7.0"]);
assert_eq!(code, 0, "{out}");
let (code, out) = run(&bundle, &["validate", "--strict"]);
assert_eq!(code, 0, "{out}");
let (code, out) = run(&cwd, &["tree", bundle.to_str().unwrap()]);
assert_eq!(code, 0, "{out}");
let _ = std::fs::remove_dir_all(&base);
}
#[test]
fn uuid_argument_defaults_to_current_branch_in_branch_directory() {
let base = tmp("curbranch");
let cwd = base.join("proj");
std::fs::create_dir_all(&cwd).unwrap();
let (code, out) = run(&cwd, &["init"]);
assert_eq!(code, 0, "{out}");
let bundle = base.join("proj.str");
let (code, out) = run(
&bundle,
&["node", "add", "--type", "a.b", "--title", "NODE", "--summary", "s"],
);
assert_eq!(code, 0, "{out}");
let node = {
let text = std::fs::read_to_string(bundle.join("._meta")).unwrap();
text.lines()
.find_map(|l| l.strip_prefix("path = \"").map(|s| s.trim_end_matches('"').to_string()))
.filter(|p| p.starts_with("019") || p.len() == 36)
.expect("ROOT entries 应含新节点 id")
};
let node_dir = bundle.join(&node);
let (code, out) = run(&node_dir, &["branch", "add", "--title", "SUB"]);
assert_eq!(code, 0, "{out}");
let (code, out) = run(&node_dir, &["show"]);
assert_eq!(code, 0, "{out}");
assert!(out.contains("NODE"), "`show` 应落在当前节点:{out}");
let sub = {
let text = std::fs::read_to_string(node_dir.join("._meta")).unwrap();
text.lines()
.find_map(|l| l.strip_prefix("path = \"").map(|s| s.trim_end_matches('"').to_string()))
.filter(|p| p != &node)
.expect("节点 entries 应含子分支 id")
};
let (code, out) = run(&node_dir.join(&sub), &["branch", "rm", "--force"]);
assert_eq!(code, 0, "{out}");
assert!(!node_dir.join(&sub).exists(), "{out}");
let (code, out) = run(&bundle, &["validate", "--strict"]);
assert_eq!(code, 0, "{out}");
let _ = std::fs::remove_dir_all(&base);
}
#[test]
fn init_without_dir_in_a_str_named_directory_is_rejected() {
let base = tmp("initstr");
let cwd = base.join("already.str");
std::fs::create_dir_all(&cwd).unwrap();
let (code, out) = run(&cwd, &["init"]);
assert_ne!(code, 0, "{out}");
assert!(out.contains("已存在"), "{out}");
let _ = std::fs::remove_dir_all(&base);
}