use assert_cmd::Command;
use tempfile::TempDir;
fn sdivi() -> Command {
Command::cargo_bin("sdivi").expect("sdivi binary must be built")
}
fn empty_repo() -> TempDir {
tempfile::tempdir().unwrap()
}
#[test]
fn init_exits_zero() {
let repo = empty_repo();
sdivi()
.arg("--repo")
.arg(repo.path())
.arg("init")
.assert()
.success();
}
#[test]
fn snapshot_exits_zero_on_empty_repo() {
let repo = empty_repo();
sdivi()
.arg("--repo")
.arg(repo.path())
.arg("snapshot")
.assert()
.success();
}
#[test]
fn snapshot_exits_zero_when_all_extensioned_files_are_inside_sdivi_dir() {
let repo = empty_repo();
let sdivi_dir = repo.path().join(".sdivi");
std::fs::create_dir_all(sdivi_dir.join("snapshots")).unwrap();
std::fs::write(
sdivi_dir
.join("snapshots")
.join("snapshot_20260101T000000_abcdef01.json"),
r#"{"snapshot_version":"1.0"}"#,
)
.unwrap();
sdivi()
.arg("--repo")
.arg(repo.path())
.arg("snapshot")
.assert()
.success();
}
#[test]
fn snapshot_exits_three_on_all_unknown_languages() {
let repo = empty_repo();
std::fs::write(repo.path().join("code.xyzunknown"), "not a real language\n").unwrap();
sdivi()
.arg("--repo")
.arg(repo.path())
.arg("snapshot")
.assert()
.code(3);
}
#[test]
fn check_exits_ten_when_threshold_breached() {
let repo = empty_repo();
let sdivi_dir = repo.path().join(".sdivi");
std::fs::create_dir_all(&sdivi_dir).unwrap();
std::fs::write(
sdivi_dir.join("config.toml"),
"[thresholds]\ncoupling_delta_rate = -1.0\n",
)
.unwrap();
sdivi()
.arg("--repo")
.arg(repo.path())
.arg("snapshot")
.assert()
.success();
let out = sdivi()
.arg("--repo")
.arg(repo.path())
.arg("check")
.arg("--format")
.arg("json")
.output()
.unwrap();
assert_eq!(
out.status.code(),
Some(10),
"sdivi check must exit 10 when a threshold is breached; got {:?}",
out.status.code()
);
let stdout = String::from_utf8(out.stdout).unwrap();
let parsed: serde_json::Value = serde_json::from_str(&stdout)
.expect("check --format json stdout must be valid JSON even on exit 10");
assert_eq!(
parsed["exit_code"].as_i64().unwrap(),
10,
"JSON exit_code must be 10"
);
assert!(
!parsed["exceeded"].as_array().unwrap().is_empty(),
"exceeded array must be non-empty when threshold is breached"
);
}
#[test]
fn config_error_exits_two() {
let repo = empty_repo();
let sdivi_dir = repo.path().join(".sdivi");
std::fs::create_dir_all(&sdivi_dir).unwrap();
std::fs::write(
sdivi_dir.join("config.toml"),
"[thresholds.overrides.test_category]\npattern_entropy_rate = 5.0\n",
)
.unwrap();
sdivi()
.arg("--repo")
.arg(repo.path())
.arg("snapshot")
.assert()
.code(2);
}
#[test]
fn trend_zero_snapshots_exits_zero() {
let repo = empty_repo();
sdivi()
.arg("--repo")
.arg(repo.path())
.arg("trend")
.assert()
.success();
}
#[test]
fn trend_one_snapshot_exits_zero() {
let repo = empty_repo();
sdivi()
.arg("--repo")
.arg(repo.path())
.arg("snapshot")
.assert()
.success();
sdivi()
.arg("--repo")
.arg(repo.path())
.arg("trend")
.assert()
.success();
}
#[test]
fn show_no_snapshots_exits_one() {
let repo = empty_repo();
sdivi()
.arg("--repo")
.arg(repo.path())
.arg("show")
.assert()
.code(1);
}
#[test]
fn diff_nonexistent_file_exits_one() {
let repo = empty_repo();
sdivi()
.arg("--repo")
.arg(repo.path())
.arg("diff")
.arg("/nonexistent/prev.json")
.arg("/nonexistent/curr.json")
.assert()
.code(1);
}
#[test]
fn snapshot_commit_nonexistent_exits_one() {
let repo = empty_repo();
std::process::Command::new("git")
.current_dir(repo.path())
.args(["init"])
.status()
.unwrap();
std::process::Command::new("git")
.current_dir(repo.path())
.args(["config", "user.email", "t@t.com"])
.status()
.unwrap();
std::process::Command::new("git")
.current_dir(repo.path())
.args(["config", "user.name", "T"])
.status()
.unwrap();
sdivi()
.arg("--repo")
.arg(repo.path())
.arg("snapshot")
.arg("--commit")
.arg("refs/heads/no-such-branch-xyz-99999")
.assert()
.code(1);
}
#[test]
fn snapshot_commit_non_git_dir_exits_one() {
let dir = empty_repo();
sdivi()
.arg("--repo")
.arg(dir.path())
.arg("snapshot")
.arg("--commit")
.arg("HEAD")
.assert()
.code(1);
}
#[test]
fn boundaries_infer_exits_zero() {
let repo = empty_repo();
sdivi()
.arg("--repo")
.arg(repo.path())
.arg("boundaries")
.arg("infer")
.assert()
.success();
}
#[test]
fn boundaries_ratify_exits_zero() {
let repo = empty_repo();
sdivi()
.arg("--repo")
.arg(repo.path())
.arg("boundaries")
.arg("ratify")
.assert()
.success();
}
#[test]
fn boundaries_show_exits_zero() {
let repo = empty_repo();
sdivi()
.arg("--repo")
.arg(repo.path())
.arg("boundaries")
.arg("show")
.assert()
.success();
}