use assert_cmd::Command;
use std::fs;
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]
suppression_comments = "deny"
protected_paths = ["pushkin.toml", ".claude/settings.json"]
"#;
fn repo() -> std::io::Result<tempfile::TempDir> {
let dir = tempfile::tempdir()?;
fs::write(dir.path().join("pushkin.toml"), MANIFEST)?;
Ok(dir)
}
fn digest(dir: &tempfile::TempDir, envs: &[(&str, &str)]) -> (i32, String) {
let Ok(mut command) = Command::cargo_bin("pushkin") else {
return (-1, "cargo_bin resolution failed".to_owned());
};
command
.current_dir(dir.path())
.args(["instructions", "--digest"]);
for (key, value) in envs {
command.env(key, value);
}
let Ok(output) = command.output() else {
return (-1, "spawn failed".to_owned());
};
(
output.status.code().unwrap_or(-1),
String::from_utf8_lossy(&output.stdout).into_owned(),
)
}
#[test]
fn instructions_render_from_manifest_single_source() {
let dir = repo().unwrap();
let (code, output) = digest(&dir, &[]);
assert_eq!(code, 0);
assert!(
output.contains("app/api/**/*.ts"),
"gated globs come from THIS manifest: {output}"
);
assert!(output.contains("user"), "contract name present: {output}");
assert!(
output.contains(".claude/settings.json"),
"protected paths named: {output}"
);
}
#[test]
fn override_file_changes_wording_without_recompile() {
let dir = repo().unwrap();
fs::create_dir_all(dir.path().join(".pushkin")).unwrap();
fs::write(
dir.path().join(".pushkin/instructions.md"),
"TEAM-TUNED PREAMBLE: obey the gate.\n",
)
.unwrap();
let (code, output) = digest(&dir, &[]);
assert_eq!(code, 0);
assert!(
output.contains("TEAM-TUNED PREAMBLE"),
"override prose replaces the canonical wording: {output}"
);
assert!(
output.contains("app/api/**/*.ts"),
"manifest facts still rendered — prose cannot drift from enforcement: {output}"
);
}
#[test]
fn env_var_override_wins_over_file() {
let dir = repo().unwrap();
fs::create_dir_all(dir.path().join(".pushkin")).unwrap();
fs::write(
dir.path().join(".pushkin/instructions.md"),
"FILE OVERRIDE\n",
)
.unwrap();
fs::write(dir.path().join("env-override.md"), "ENV OVERRIDE WINS\n").unwrap();
let env_path = dir.path().join("env-override.md");
let (code, output) = digest(
&dir,
&[("PUSHKIN_INSTRUCTIONS", env_path.to_str().unwrap())],
);
assert_eq!(code, 0);
assert!(
output.contains("ENV OVERRIDE WINS"),
"env-named override wins: {output}"
);
assert!(
!output.contains("FILE OVERRIDE"),
"file override is superseded, not concatenated: {output}"
);
}
#[test]
fn digest_capped_at_2kb() {
let dir = repo().unwrap();
fs::create_dir_all(dir.path().join(".pushkin")).unwrap();
let huge = "VERBOSE TEAM PROSE. ".repeat(500); fs::write(dir.path().join(".pushkin/instructions.md"), huge).unwrap();
let (code, output) = digest(&dir, &[]);
assert_eq!(code, 0);
assert!(
output.len() <= 2048,
"digest capped at 2 KiB (addendum §6), got {} bytes",
output.len()
);
assert!(
output.contains("app/api/**/*.ts"),
"manifest facts survive truncation (§7.4 invariant): {output}"
);
}