use assay_evidence::{BundleWriter, EvidenceEvent};
use assert_cmd::Command;
use chrono::{TimeZone, Utc};
use predicates::prelude::*;
use serde_json::json;
use std::fs;
use tempfile::tempdir;
fn make_event(type_: &str, run_id: &str, seq: u64, payload: serde_json::Value) -> EvidenceEvent {
let mut event = EvidenceEvent::new(
type_,
"urn:assay:test:trust-basis-cli",
run_id,
seq,
payload,
);
event.time = Utc.timestamp_opt(1_700_000_000 + seq as i64, 0).unwrap();
event
}
fn write_bundle(path: &std::path::Path, events: Vec<EvidenceEvent>) {
let file = fs::File::create(path).unwrap();
let mut writer = BundleWriter::new(file);
for event in events {
writer.add_event(event);
}
writer.finish().unwrap();
}
fn claim<'a>(claims: &'a [serde_json::Value], id: &str) -> &'a serde_json::Value {
claims
.iter()
.find(|claim| claim["id"] == id)
.expect("claim should exist")
}
fn write_trust_basis_json(path: &std::path::Path, external_eval_level: &str) {
let value = json!({
"claims": [
{
"id": "bundle_verified",
"level": "verified",
"source": "bundle_verification",
"boundary": "bundle-wide"
},
{
"id": "external_eval_receipt_boundary_visible",
"level": external_eval_level,
"source": "external_evidence_receipt",
"boundary": "supported-external-eval-receipt-events-only"
}
]
});
fs::write(path, serde_json::to_vec_pretty(&value).unwrap()).unwrap();
}
fn write_duplicate_claim_trust_basis_json(path: &std::path::Path) {
let value = json!({
"claims": [
{
"id": "bundle_verified",
"level": "verified",
"source": "bundle_verification",
"boundary": "bundle-wide"
},
{
"id": "bundle_verified",
"level": "verified",
"source": "bundle_verification",
"boundary": "bundle-wide"
}
]
});
fs::write(path, serde_json::to_vec_pretty(&value).unwrap()).unwrap();
}
fn write_minimal_trust_basis_json(path: &std::path::Path) {
let value = json!({
"claims": [
{
"id": "bundle_verified",
"level": "verified",
"source": "bundle_verification",
"boundary": "bundle-wide"
}
]
});
fs::write(path, serde_json::to_vec_pretty(&value).unwrap()).unwrap();
}
#[test]
fn trust_basis_generate_stdout_emits_all_frozen_claims() {
let dir = tempdir().unwrap();
let bundle = dir.path().join("trust-basis.tar.gz");
write_bundle(
&bundle,
vec![
make_event(
"assay.tool.decision",
"run_stdout",
0,
json!({
"tool": "tool.commit",
"decision": "allow",
"delegated_from": "agent:planner"
}),
),
make_event(
"assay.sandbox.degraded",
"run_stdout",
1,
json!({
"reason_code": "policy_conflict",
"degradation_mode": "audit_fallback",
"component": "landlock"
}),
),
],
);
let output = Command::cargo_bin("assay")
.unwrap()
.arg("trust-basis")
.arg("generate")
.arg(&bundle)
.output()
.unwrap();
assert!(output.status.success());
let json: serde_json::Value = serde_json::from_slice(&output.stdout).unwrap();
let claims = json["claims"].as_array().unwrap();
assert_eq!(
claims.len(),
10,
"all frozen claims should always be present"
);
assert_eq!(claim(claims, "bundle_verified")["level"], "verified");
assert_eq!(
claim(claims, "delegation_context_visible")["level"],
"verified"
);
assert_eq!(
claim(claims, "authorization_context_visible")["level"],
"absent"
);
assert_eq!(
claim(claims, "containment_degradation_observed")["level"],
"verified"
);
assert_eq!(claim(claims, "signing_evidence_present")["level"], "absent");
assert_eq!(
claim(claims, "provenance_backed_claims_present")["level"],
"absent"
);
assert_eq!(
claim(claims, "applied_pack_findings_present")["level"],
"absent"
);
assert_eq!(
claim(claims, "external_eval_receipt_boundary_visible")["level"],
"absent"
);
assert_eq!(
claim(claims, "external_decision_receipt_boundary_visible")["level"],
"absent"
);
assert_eq!(
claim(claims, "external_inventory_receipt_boundary_visible")["level"],
"absent"
);
}
#[test]
fn trust_basis_generate_is_byte_stable_and_pack_aware() {
let dir = tempdir().unwrap();
let bundle = dir.path().join("trust-basis-pack.tar.gz");
write_bundle(
&bundle,
vec![make_event(
"assay.tool.decision",
"run_pack",
0,
json!({
"tool": "tool.commit",
"decision": "allow",
"principal": "user:alice"
}),
)],
);
let run = || {
Command::cargo_bin("assay")
.unwrap()
.arg("trust-basis")
.arg("generate")
.arg(&bundle)
.arg("--pack")
.arg("owasp-agentic-a3-a5-signal-followup")
.output()
.unwrap()
};
let first = run();
let second = run();
assert!(first.status.success());
assert!(second.status.success());
assert_eq!(
first.stdout, second.stdout,
"canonical trust basis should regenerate byte-for-byte"
);
let json: serde_json::Value = serde_json::from_slice(&first.stdout).unwrap();
let claims = json["claims"].as_array().unwrap();
assert_eq!(
claim(claims, "applied_pack_findings_present")["level"],
"verified"
);
assert_eq!(
claim(claims, "applied_pack_findings_present")["source"],
"pack_execution_results"
);
assert_eq!(
claim(claims, "applied_pack_findings_present")["boundary"],
"pack-execution-only"
);
}
#[test]
fn trust_basis_diff_fails_on_external_receipt_claim_regression_when_requested() {
let dir = tempdir().unwrap();
let baseline = dir.path().join("baseline.trust-basis.json");
let candidate = dir.path().join("candidate.trust-basis.json");
write_trust_basis_json(&baseline, "verified");
write_trust_basis_json(&candidate, "absent");
let output = Command::cargo_bin("assay")
.unwrap()
.arg("trust-basis")
.arg("diff")
.arg(&baseline)
.arg(&candidate)
.arg("--format")
.arg("json")
.arg("--fail-on-regression")
.output()
.unwrap();
assert_eq!(output.status.code(), Some(1));
let stdout: serde_json::Value = serde_json::from_slice(&output.stdout).unwrap();
assert_eq!(stdout["schema"], "assay.trust-basis.diff.v1");
assert_eq!(stdout["claim_identity"], "claim.id");
assert_eq!(stdout["summary"]["has_regressions"], true);
assert_eq!(stdout["summary"]["regressed_claims"], 1);
assert_eq!(
stdout["regressed_claims"][0]["claim_id"],
"external_eval_receipt_boundary_visible"
);
assert_eq!(stdout["regressed_claims"][0]["baseline_level"], "verified");
assert_eq!(stdout["regressed_claims"][0]["candidate_level"], "absent");
let stderr = String::from_utf8(output.stderr).unwrap();
assert!(stderr.contains("Trust Basis regression check failed"));
}
#[test]
fn trust_basis_diff_reports_external_receipt_claim_improvement_without_failing() {
let dir = tempdir().unwrap();
let baseline = dir.path().join("baseline.trust-basis.json");
let candidate = dir.path().join("candidate.trust-basis.json");
write_trust_basis_json(&baseline, "absent");
write_trust_basis_json(&candidate, "verified");
Command::cargo_bin("assay")
.unwrap()
.arg("trust-basis")
.arg("diff")
.arg(&baseline)
.arg(&candidate)
.arg("--fail-on-regression")
.assert()
.success()
.stdout(predicate::str::contains("Improvements:"))
.stdout(predicate::str::contains(
"external_eval_receipt_boundary_visible: absent -> verified",
));
}
#[test]
fn trust_basis_diff_rejects_duplicate_claim_identity() {
let dir = tempdir().unwrap();
let baseline = dir.path().join("baseline.trust-basis.json");
let candidate = dir.path().join("candidate.trust-basis.json");
write_duplicate_claim_trust_basis_json(&baseline);
write_trust_basis_json(&candidate, "verified");
Command::cargo_bin("assay")
.unwrap()
.arg("trust-basis")
.arg("diff")
.arg(&baseline)
.arg(&candidate)
.assert()
.failure()
.code(2)
.stderr(predicate::str::contains(
"baseline Trust Basis contains duplicate claim id(s): bundle_verified",
));
}
#[test]
fn trust_basis_assert_passes_required_claim_level() {
let dir = tempdir().unwrap();
let input = dir.path().join("trust-basis.json");
write_trust_basis_json(&input, "verified");
Command::cargo_bin("assay")
.unwrap()
.arg("trust-basis")
.arg("assert")
.arg("--input")
.arg(&input)
.arg("--require")
.arg("external_eval_receipt_boundary_visible=verified")
.assert()
.success()
.stdout(predicate::str::contains("Trust Basis assertions passed"))
.stdout(predicate::str::contains(
"external_eval_receipt_boundary_visible: expected verified, actual verified",
));
}
#[test]
fn trust_basis_assert_exits_one_on_mismatch() {
let dir = tempdir().unwrap();
let input = dir.path().join("trust-basis.json");
write_trust_basis_json(&input, "absent");
Command::cargo_bin("assay")
.unwrap()
.arg("trust-basis")
.arg("assert")
.arg("--input")
.arg(&input)
.arg("--require")
.arg("external_eval_receipt_boundary_visible=verified")
.assert()
.code(1)
.stdout(predicate::str::contains("Trust Basis assertions failed"))
.stdout(predicate::str::contains(
"external_eval_receipt_boundary_visible: expected verified, actual absent",
));
}
#[test]
fn trust_basis_assert_json_reports_missing_claim_as_failed_requirement() {
let dir = tempdir().unwrap();
let input = dir.path().join("trust-basis.json");
write_minimal_trust_basis_json(&input);
let output = Command::cargo_bin("assay")
.unwrap()
.arg("trust-basis")
.arg("assert")
.arg("--input")
.arg(&input)
.arg("--require")
.arg("external_eval_receipt_boundary_visible=verified")
.arg("--format")
.arg("json")
.output()
.unwrap();
assert_eq!(output.status.code(), Some(1));
let stdout: serde_json::Value = serde_json::from_slice(&output.stdout).unwrap();
assert_eq!(stdout["schema"], "assay.trust-basis.assert.v1");
assert_eq!(stdout["claim_identity"], "claim.id");
assert_eq!(stdout["summary"]["total_requirements"], 1);
assert_eq!(stdout["summary"]["passed_requirements"], 0);
assert_eq!(stdout["summary"]["failed_requirements"], 1);
assert_eq!(
stdout["requirements"][0]["claim_id"],
"external_eval_receipt_boundary_visible"
);
assert_eq!(stdout["requirements"][0]["expected_level"], "verified");
assert_eq!(
stdout["requirements"][0]["actual_level"],
serde_json::Value::Null
);
assert_eq!(stdout["requirements"][0]["status"], "failed");
}
#[test]
fn trust_basis_assert_rejects_unknown_claim_id_as_input_error() {
let dir = tempdir().unwrap();
let input = dir.path().join("trust-basis.json");
write_trust_basis_json(&input, "verified");
Command::cargo_bin("assay")
.unwrap()
.arg("trust-basis")
.arg("assert")
.arg("--input")
.arg(&input)
.arg("--require")
.arg("not_a_claim=verified")
.assert()
.code(2)
.stderr(predicate::str::contains(
"unknown Trust Basis claim id \"not_a_claim\"",
));
}
#[test]
fn trust_basis_assert_rejects_planning_only_score_receipt_claim_candidate() {
let dir = tempdir().unwrap();
let input = dir.path().join("trust-basis.json");
write_trust_basis_json(&input, "verified");
Command::cargo_bin("assay")
.unwrap()
.arg("trust-basis")
.arg("assert")
.arg("--input")
.arg(&input)
.arg("--require")
.arg("external_score_receipt_boundary_visible=verified")
.assert()
.code(2)
.stderr(predicate::str::contains(
"unknown Trust Basis claim id \"external_score_receipt_boundary_visible\"",
));
}
#[test]
fn trust_basis_assert_rejects_duplicate_claim_identity() {
let dir = tempdir().unwrap();
let input = dir.path().join("trust-basis.json");
write_duplicate_claim_trust_basis_json(&input);
Command::cargo_bin("assay")
.unwrap()
.arg("trust-basis")
.arg("assert")
.arg("--input")
.arg(&input)
.arg("--require")
.arg("bundle_verified=verified")
.assert()
.code(2)
.stderr(predicate::str::contains(
"input Trust Basis contains duplicate claim id(s): bundle_verified",
));
}