use assert_cmd::Command;
use predicates::str::contains;
use tempfile::tempdir;
fn tsafe() -> Command {
Command::cargo_bin("tsafe").unwrap()
}
#[test]
fn policy_set_rejects_invalid_rotate_every() {
let dir = tempdir().unwrap();
tsafe()
.args(["init"])
.env("TSAFE_VAULT_DIR", dir.path())
.env("TSAFE_PASSWORD", "pw")
.assert()
.success();
tsafe()
.args(["set", "K", "v"])
.env("TSAFE_VAULT_DIR", dir.path())
.env("TSAFE_PASSWORD", "pw")
.assert()
.success();
tsafe()
.args([
"policy",
"set",
"K",
"--rotate-every",
"not-a-valid-duration",
])
.env("TSAFE_VAULT_DIR", dir.path())
.env("TSAFE_PASSWORD", "pw")
.assert()
.failure()
.stderr(contains("invalid rotation interval"));
}
#[test]
fn policy_set_then_rotate_due_json_shows_no_overdue() {
let dir = tempdir().unwrap();
tsafe()
.args(["init"])
.env("TSAFE_VAULT_DIR", dir.path())
.env("TSAFE_PASSWORD", "pw")
.assert()
.success();
tsafe()
.args(["set", "DB_PW", "secret"])
.env("TSAFE_VAULT_DIR", dir.path())
.env("TSAFE_PASSWORD", "pw")
.assert()
.success();
tsafe()
.args(["policy", "set", "DB_PW", "--rotate-every", "90d"])
.env("TSAFE_VAULT_DIR", dir.path())
.env("TSAFE_PASSWORD", "pw")
.assert()
.success()
.stdout(contains("Rotation policy set"));
tsafe()
.args(["rotate-due", "--json"])
.env("TSAFE_VAULT_DIR", dir.path())
.env("TSAFE_PASSWORD", "pw")
.assert()
.success()
.stdout(contains("\"overdue_count\": 0"));
}
#[test]
fn policy_remove_succeeds_after_set() {
let dir = tempdir().unwrap();
tsafe()
.args(["init"])
.env("TSAFE_VAULT_DIR", dir.path())
.env("TSAFE_PASSWORD", "pw")
.assert()
.success();
tsafe()
.args(["set", "X", "y"])
.env("TSAFE_VAULT_DIR", dir.path())
.env("TSAFE_PASSWORD", "pw")
.assert()
.success();
tsafe()
.args(["policy", "set", "X", "--rotate-every", "30d"])
.env("TSAFE_VAULT_DIR", dir.path())
.env("TSAFE_PASSWORD", "pw")
.assert()
.success();
tsafe()
.args(["policy", "remove", "X"])
.env("TSAFE_VAULT_DIR", dir.path())
.env("TSAFE_PASSWORD", "pw")
.assert()
.success()
.stdout(contains("Rotation policy removed"));
}
#[test]
fn validate_cellos_policy_rejects_access_profile_mismatch() {
let dir = tempdir().unwrap();
std::fs::write(
dir.path().join(".tsafe.yml"),
r#"
contracts:
readonly_cell:
allowed_secrets: [CELL_SECRET]
access_profile: read_only
trust_level: standard
"#,
)
.unwrap();
std::fs::write(
dir.path().join("policy.json"),
r#"{
"allowedSecretRefs": ["CELL_SECRET"],
"accessProfile": "read_write"
}"#,
)
.unwrap();
tsafe()
.args(["validate", "--cellos-policy", "policy.json"])
.current_dir(dir.path())
.assert()
.failure()
.stderr(contains("accessProfile"));
}
#[test]
fn validate_cellos_policy_rejects_required_secret_ref_mismatch() {
let dir = tempdir().unwrap();
std::fs::write(
dir.path().join(".tsafe.yml"),
r#"
contracts:
ci_cell:
allowed_secrets: [CELL_SECRET]
required_secrets: [CELL_SECRET]
trust_level: hardened
"#,
)
.unwrap();
std::fs::write(
dir.path().join("policy.json"),
r#"{
"allowedSecretRefs": ["CELL_SECRET"],
"requiredSecretRefs": []
}"#,
)
.unwrap();
tsafe()
.args(["validate", "--cellos-policy", "policy.json"])
.current_dir(dir.path())
.assert()
.failure()
.stderr(contains("requiredSecretRefs"));
}
#[test]
fn validate_policy_file_alias_works_same_as_cellos_policy() {
let dir = tempdir().unwrap();
std::fs::write(
dir.path().join(".tsafe.yml"),
r#"
contracts:
my_cell:
allowed_secrets: [SECRET_A]
access_profile: read_only
trust_level: standard
"#,
)
.unwrap();
std::fs::write(
dir.path().join("policy.json"),
r#"{
"allowedSecretRefs": ["SECRET_A"]
}"#,
)
.unwrap();
tsafe()
.args(["validate", "--policy-file", "policy.json"])
.current_dir(dir.path())
.assert()
.success();
}
#[test]
fn validate_json_output_passes_with_matching_contract() {
let dir = tempdir().unwrap();
std::fs::write(
dir.path().join(".tsafe.yml"),
r#"
contracts:
my_cell:
allowed_secrets: [SECRET_A]
access_profile: read_only
trust_level: standard
"#,
)
.unwrap();
std::fs::write(
dir.path().join("policy.json"),
r#"{
"allowedSecretRefs": ["SECRET_A"]
}"#,
)
.unwrap();
let output = tsafe()
.args(["validate", "--policy-file", "policy.json", "--json"])
.current_dir(dir.path())
.assert()
.success()
.get_output()
.stdout
.clone();
let json: serde_json::Value = serde_json::from_slice(&output).unwrap();
assert_eq!(json["passed"], true);
assert_eq!(json["conflict_fields"].as_array().unwrap().len(), 0);
}
#[test]
fn validate_json_output_fails_with_mismatch() {
let dir = tempdir().unwrap();
std::fs::write(
dir.path().join(".tsafe.yml"),
r#"
contracts:
my_cell:
allowed_secrets: [SECRET_A]
access_profile: read_only
trust_level: standard
"#,
)
.unwrap();
std::fs::write(
dir.path().join("policy.json"),
r#"{
"allowedSecretRefs": ["DIFFERENT_SECRET"]
}"#,
)
.unwrap();
let output = tsafe()
.args(["validate", "--policy-file", "policy.json", "--json"])
.current_dir(dir.path())
.assert()
.failure()
.get_output()
.stdout
.clone();
let json: serde_json::Value = serde_json::from_slice(&output).unwrap();
assert_eq!(json["passed"], false);
assert!(!json["conflict_fields"].as_array().unwrap().is_empty());
}
#[test]
fn validate_cellos_policy_accepts_matching_contract_in_heterogeneous_manifest() {
let dir = tempdir().unwrap();
std::fs::write(
dir.path().join(".tsafe.yml"),
r#"
contracts:
readonly_cell:
allowed_secrets: [CELL_SECRET]
required_secrets: [CELL_SECRET]
allowed_targets: [terraform]
access_profile: read_only
trust_level: hardened
writer_cell:
allowed_secrets: [CELL_SECRET]
required_secrets: [CELL_SECRET]
allowed_targets: [terraform]
access_profile: read_write
trust_level: standard
"#,
)
.unwrap();
std::fs::write(
dir.path().join("policy.json"),
r#"{
"allowedSecretRefs": ["CELL_SECRET"],
"requiredSecretRefs": ["CELL_SECRET"],
"allowedTargets": ["terraform"],
"accessProfile": "read_only",
"trustLevel": "hardened"
}"#,
)
.unwrap();
tsafe()
.args(["validate", "--cellos-policy", "policy.json"])
.current_dir(dir.path())
.assert()
.success();
}