use super::commit::{assert_nothing_left, git_text, keep_maintenance_in_the_foreground, remove_settled, run_commit};
use super::support::{Fixture, Observed, fixture, repository, run_wrapped, stderr_of};
use std::ffi::OsStr;
use std::path::{Path, PathBuf};
const LITERAL: &str = "{\n \"dependencies\": {\n \"zod\": \"^4.6.7\"\n }\n}\n";
const CATALOG: &str = "{\n \"dependencies\": {\n \"zod\": \"catalog:\"\n }\n}\n";
const ERROR_CONFIGURATION: &str = "{ \"policies\": { \"mono/catalog-specifiers\": \"error\" } }\n";
fn literal_finding(trigger: &str, severity: &str) -> String {
return format!(
"{{\"schemaVersion\":1,\"sequence\":0,\"type\":\"finding\",\"trigger\":\"{trigger}\",\
\"policyId\":\"mono/catalog-specifiers\",\"severity\":\"{severity}\",\
\"code\":\"mono/catalog-specifiers/disallowed-specifier\",\
\"message\":\"package.json: dependencies `zod` is \\\"^4.6.7\\\"; move the version into the `catalog:` section \
of pnpm-workspace.yaml and write `catalog:`, or write `workspace:*` for a package of this workspace\",\
\"path\":\"package.json\",\"fix\":\"none\"}}\n"
);
}
fn configured(fixture: &Fixture, configuration: Option<&str>, manifest: &str) -> PathBuf {
let repo: PathBuf = repository(fixture, OsStr::new("repo"));
keep_maintenance_in_the_foreground(fixture, repo.as_path());
if let Some(text) = configuration {
std::fs::write(repo.join("cli-git.config.jsonc"), text).expect("repository configuration");
}
std::fs::write(repo.join("package.json"), manifest).expect("manifest");
return repo;
}
fn commit_manifest(fixture: &Fixture, repo: &Path) -> Observed {
return run_commit(fixture, repo, &["commit", "--message=manifest", "--", "package.json"]);
}
#[test]
fn an_error_policy_blocks_a_literal_version_and_lets_a_catalog_one_land() {
let fixture: Fixture = fixture("catalog-commit-error");
let repo: PathBuf = configured(&fixture, Some(ERROR_CONFIGURATION), LITERAL);
let head: String = git_text(&fixture, repo.as_path(), &["rev-parse", "HEAD"]);
let blocked: Observed = commit_manifest(&fixture, repo.as_path());
assert_eq!((blocked.code, stderr_of(&blocked)), (Some(1), literal_finding("pre-forward", "error")));
assert_eq!(git_text(&fixture, repo.as_path(), &["rev-parse", "HEAD"]), head);
assert_nothing_left(&fixture, repo.as_path());
std::fs::write(repo.join("package.json"), CATALOG).expect("fixed manifest");
let landed: Observed = commit_manifest(&fixture, repo.as_path());
assert_eq!((landed.code, stderr_of(&landed)), (Some(0), String::new()));
assert_eq!(git_text(&fixture, repo.as_path(), &["show", "HEAD:package.json"]), CATALOG);
assert_nothing_left(&fixture, repo.as_path());
remove_settled(&fixture);
}
#[test]
fn an_error_policy_blocks_an_unsupported_manifest_name() {
let fixture: Fixture = fixture("catalog-commit-yaml");
let repo: PathBuf = configured(&fixture, Some(ERROR_CONFIGURATION), CATALOG);
std::fs::write(repo.join("package.yaml"), b"\xff\xfe not yaml, never read").expect("yaml");
let blocked: Observed = run_commit(&fixture, repo.as_path(), &["commit", "--message=yaml", "--", "package.yaml"]);
let stderr: String = stderr_of(&blocked);
assert_eq!(blocked.code, Some(1), "{stderr}");
assert!(stderr.contains("\"code\":\"mono/catalog-specifiers/unsupported-manifest-format\""), "{stderr}");
assert!(stderr.contains("\"path\":\"package.yaml\""), "{stderr}");
assert_eq!(stderr.matches('\n').count(), 1, "{stderr}");
assert_nothing_left(&fixture, repo.as_path());
remove_settled(&fixture);
}
#[test]
fn a_warn_policy_reports_and_lets_the_commit_land() {
let fixture: Fixture = fixture("catalog-commit-warn");
let repo: PathBuf = configured(&fixture, Some("{ \"policies\": { \"mono/catalog-specifiers\": \"warn\" } }\n"), LITERAL);
let landed: Observed = commit_manifest(&fixture, repo.as_path());
let stderr: String = stderr_of(&landed);
assert_eq!(landed.code, Some(0), "{stderr}");
assert!(stderr.starts_with(literal_finding("pre-forward", "warn").as_str()), "{stderr}");
assert!(stderr.contains("\"code\":\"warn-unsafe\""), "{stderr}");
assert_eq!(git_text(&fixture, repo.as_path(), &["show", "HEAD:package.json"]), LITERAL);
remove_settled(&fixture);
}
#[test]
fn a_configuration_that_does_not_name_the_policy_commits_as_before() {
for (name, configuration) in [
("none", None),
("empty", Some("{}\n")),
("older", Some("{ \"policies\": { \"mono/forbidden-root-context\": \"error\", \"mono/dependent-version-bump\": \"error\" } }\n")),
("off", Some("{ \"policies\": { \"mono/catalog-specifiers\": \"off\" } }\n")),
] {
let fixture: Fixture = fixture(format!("catalog-commit-before-{name}").as_str());
let repo: PathBuf = configured(&fixture, configuration, LITERAL);
let landed: Observed = commit_manifest(&fixture, repo.as_path());
assert_eq!((landed.code, stderr_of(&landed)), (Some(0), String::new()), "{name}");
assert_eq!(git_text(&fixture, repo.as_path(), &["show", "HEAD:package.json"]), LITERAL, "{name}");
remove_settled(&fixture);
}
}
#[test]
fn a_direct_check_of_the_policy_follows_its_severity() {
let fixture: Fixture = fixture("catalog-direct");
let repo: PathBuf = configured(&fixture, None, LITERAL);
let arguments: [&str; 5] = ["cli-git", "check", "--all", "--policy", "mono/catalog-specifiers"];
let off: Observed = run_wrapped(&fixture, repo.as_path(), &arguments);
let stdout: String = String::from_utf8_lossy(off.stdout.as_slice()).into_owned();
assert_eq!((off.code, stderr_of(&off)), (Some(2), String::new()));
assert!(stdout.contains("\"code\":\"config-invalid\""), "{stdout}");
assert!(stdout.contains("Policy mono/catalog-specifiers is off, so --policy mono/catalog-specifiers would check nothing"), "{stdout}");
assert!(stdout.contains("\\\"policies\\\": { \\\"mono/catalog-specifiers\\\": \\\"error\\\" } in cli-git.config.jsonc"), "{stdout}");
for (severity, code) in [("warn", 0), ("error", 1)] {
std::fs::write(
repo.join("cli-git.config.jsonc"),
format!("{{ \"policies\": {{ \"mono/catalog-specifiers\": \"{severity}\" }} }}\n"),
)
.expect("configuration");
let ran: Observed = run_wrapped(&fixture, repo.as_path(), &arguments);
let printed: String = String::from_utf8_lossy(ran.stdout.as_slice()).into_owned();
assert_eq!((ran.code, stderr_of(&ran)), (Some(code), String::new()), "{severity}");
assert!(printed.starts_with(literal_finding("direct-check", severity).as_str()), "{severity}: {printed}");
}
let fix: Observed = run_wrapped(
&fixture,
repo.as_path(),
&["cli-git", "fix", "--all", "--policy", "mono/catalog-specifiers"],
);
let refusal: String = String::from_utf8_lossy(fix.stdout.as_slice()).into_owned();
assert_eq!((fix.code, stderr_of(&fix)), (Some(2), String::new()), "{refusal}");
assert!(refusal.contains("does not run for cli-git fix"), "{refusal}");
remove_settled(&fixture);
}
#[test]
fn the_repository_configuration_loads_and_the_catalog_policy_follows_it() {
let fixture: Fixture = fixture("catalog-real-configuration");
let repo: PathBuf = configured(&fixture, None, LITERAL);
let shipped: PathBuf = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("../../../cli-git.config.jsonc");
std::fs::copy(&shipped, repo.join("cli-git.config.jsonc")).expect("the repository's own configuration");
let shipped_text: String = std::fs::read_to_string(&shipped).expect("the repository's own configuration text");
let lists_catalog: bool = shipped_text.lines().any(|line| {
let code: &str = line.split("//").next().unwrap_or_default();
return code.contains("\"mono/catalog-specifiers\"") && !code.contains("\"off\"");
});
let listed: Observed = run_wrapped(
&fixture,
repo.as_path(),
&["cli-git", "check", "--all", "--policy", "mono/forbidden-root-context"],
);
assert_eq!((listed.code, stderr_of(&listed)), (Some(0), String::new()));
assert!(listed.stdout.is_empty(), "{}", String::from_utf8_lossy(listed.stdout.as_slice()));
let selected: Observed = run_wrapped(
&fixture,
repo.as_path(),
&["cli-git", "check", "--all", "--policy", "mono/catalog-specifiers"],
);
let stdout: String = String::from_utf8_lossy(selected.stdout.as_slice()).into_owned();
if lists_catalog {
assert_eq!(selected.code, Some(1), "{stdout}");
assert!(stdout.contains("mono/catalog-specifiers/disallowed-specifier"), "{stdout}");
} else {
assert_eq!(selected.code, Some(2), "{stdout}");
assert!(stdout.contains("mono/catalog-specifiers"), "{stdout}");
}
remove_settled(&fixture);
}