use assert_cmd::Command;
use std::fs;
use std::path::Path;
type TestResult = Result<(), Box<dyn std::error::Error>>;
const MANIFEST: &str = r#"
version = 1
canonical = "json-schema-2020-12"
authoring = "zod"
[[contracts]]
name = "user"
source = "contracts/user.zod.ts"
emit = ["zod"]
[[mappings]]
glob = "app/api/**/*.ts"
contracts = ["user"]
require = "boundary-validation"
[gates]
protected_paths = ["pushkin.toml"]
"#;
fn repo() -> Result<tempfile::TempDir, Box<dyn std::error::Error>> {
let dir = tempfile::tempdir()?;
fs::write(dir.path().join("pushkin.toml"), MANIFEST)?;
fs::create_dir_all(dir.path().join("contracts"))?;
fs::write(
dir.path().join("contracts/user.zod.ts"),
"export const user = 1;\n",
)?;
Ok(dir)
}
fn run(dir: &Path, args: &[&str]) -> Result<String, Box<dyn std::error::Error>> {
let output = Command::cargo_bin("pushkin")?
.current_dir(dir)
.args(args)
.output()?;
Ok(format!(
"{}{}",
String::from_utf8_lossy(&output.stdout),
String::from_utf8_lossy(&output.stderr)
))
}
#[test]
fn init_warns_that_codex_hooks_need_a_trust_grant() -> TestResult {
let dir = repo()?;
let output = run(dir.path(), &["init", "--agent", "codex"])?;
assert!(
output.to_lowercase().contains("trust"),
"installing codex hooks must name the trust requirement: {output}"
);
assert!(
output.contains("F56"),
"and cite the finding, so the notice is traceable: {output}"
);
Ok(())
}
#[test]
fn the_init_notice_says_hooks_do_not_fire_until_granted() -> TestResult {
let dir = repo()?;
let output = run(dir.path(), &["init", "--agent", "codex"])?;
let lower = output.to_lowercase();
assert!(
lower.contains("will not fire")
|| lower.contains("does not fire")
|| lower.contains("not run"),
"say plainly that the hook is inert until trusted: {output}"
);
assert!(
output.contains("--dangerously-bypass-hook-trust") || lower.contains("interactively"),
"name at least one way to establish or bypass trust: {output}"
);
Ok(())
}
#[test]
fn doctor_reports_the_codex_trust_condition() -> TestResult {
let dir = repo()?;
run(dir.path(), &["init", "--agent", "codex"])?;
let output = run(dir.path(), &["doctor"])?;
assert!(
output.to_lowercase().contains("codex"),
"doctor must mention codex at all: {output}"
);
assert!(
output.to_lowercase().contains("trust"),
"and name the trust condition: {output}"
);
Ok(())
}
#[test]
fn doctor_does_not_claim_to_have_verified_trust() -> TestResult {
let dir = repo()?;
run(dir.path(), &["init", "--agent", "codex"])?;
let output = run(dir.path(), &["doctor"])?;
let lower = output.to_lowercase();
assert!(
lower.contains("cannot verify")
|| lower.contains("unverifiable")
|| lower.contains("not verifiable"),
"doctor must say the trust state is unverifiable from here: {output}"
);
Ok(())
}
#[test]
fn the_codex_trust_notice_does_not_change_doctors_exit_code() -> TestResult {
let without = repo()?;
let before = Command::cargo_bin("pushkin")?
.current_dir(without.path())
.arg("doctor")
.output()?;
let with = repo()?;
run(with.path(), &["init", "--agent", "codex"])?;
let after = Command::cargo_bin("pushkin")?
.current_dir(with.path())
.arg("doctor")
.output()?;
assert_eq!(
before.status.code(),
after.status.code(),
"the trust notice is advisory: installing codex must not change the verdict"
);
Ok(())
}