use std::time::Duration;
use chio_core::crypto::{sha256_hex, Keypair};
use chio_core::receipt::{
body::ChioReceipt, body::ChioReceiptBody, decision::Decision, decision::ToolCallAction,
kinds::TrustLevel, metadata::GuardEvidence,
};
use chio_kernel::dpop::DpopNonceStore;
use chio_kernel::{BudgetStore, InMemoryBudgetStore, InMemoryRevocationStore, RevocationStore};
use serde_json::json;
use chio_test_support::prelude::*;
struct RevocationProjection {
token_revoked: bool,
ancestor_revoked: bool,
}
impl RevocationProjection {
fn denies(&self) -> bool {
self.token_revoked || self.ancestor_revoked
}
}
struct BudgetProjection {
invocation_count: u32,
committed_cost_units: u64,
}
impl BudgetProjection {
fn within(&self, max_invocations: u32, max_total_cost_units: u64) -> bool {
self.invocation_count <= max_invocations
&& self.committed_cost_units <= max_total_cost_units
}
}
struct ReceiptProjection {
capability_id: String,
tool_server: String,
tool_name: String,
parameter_hash: String,
decision: Option<Decision>,
policy_hash: String,
has_guard_evidence: bool,
}
impl ReceiptProjection {
fn from_receipt(receipt: &ChioReceipt) -> Self {
Self {
capability_id: receipt.capability_id.clone(),
tool_server: receipt.tool_server.clone(),
tool_name: receipt.tool_name.clone(),
parameter_hash: receipt.action.parameter_hash.clone(),
decision: receipt.decision.clone(),
policy_hash: receipt.policy_hash.clone(),
has_guard_evidence: !receipt.evidence.is_empty(),
}
}
fn couples(
&self,
capability_id: &str,
tool_server: &str,
tool_name: &str,
parameter_hash: &str,
decision: &Decision,
policy_hash: &str,
) -> bool {
self.capability_id == capability_id
&& self.tool_server == tool_server
&& self.tool_name == tool_name
&& self.parameter_hash == parameter_hash
&& self.decision.as_ref() == Some(decision)
&& self.policy_hash == policy_hash
&& self.has_guard_evidence
}
}
#[test]
fn dpop_nonce_projection_rejects_replay() {
let store = DpopNonceStore::new(8, Duration::from_secs(60));
assert!(store.check_and_insert("nonce-1", "cap-1").test_unwrap());
assert!(!store.check_and_insert("nonce-1", "cap-1").test_unwrap());
}
#[test]
fn revocation_snapshot_projection_denies_token_and_ancestor() {
let store = InMemoryRevocationStore::new();
store.revoke("cap-child").test_unwrap();
store.revoke("cap-parent").test_unwrap();
let child_projection = RevocationProjection {
token_revoked: store.is_revoked("cap-child").test_unwrap(),
ancestor_revoked: false,
};
assert!(child_projection.denies());
let ancestor_projection = RevocationProjection {
token_revoked: false,
ancestor_revoked: store.is_revoked("cap-parent").test_unwrap(),
};
assert!(ancestor_projection.denies());
}
#[test]
fn budget_snapshot_projection_preserves_single_node_bounds() {
let store = InMemoryBudgetStore::new();
assert!(store
.try_charge_cost("cap-budget", 0, Some(2), 40, Some(50), Some(100))
.test_unwrap());
assert!(store
.try_charge_cost("cap-budget", 0, Some(2), 50, Some(50), Some(100))
.test_unwrap());
assert!(!store
.try_charge_cost("cap-budget", 0, Some(2), 1, Some(50), Some(100))
.test_unwrap());
let usage = store.get_usage("cap-budget", 0).test_unwrap().test_unwrap();
let projection = BudgetProjection {
invocation_count: usage.invocation_count,
committed_cost_units: usage.committed_cost_units().test_unwrap(),
};
assert!(projection.within(2, 100));
}
#[test]
fn receipt_projection_couples_decision_to_evidence_body() {
let keypair = Keypair::from_seed(&[42; 32]);
let action =
ToolCallAction::from_parameters(json!({"path": "/workspace/safe.txt"})).test_unwrap();
let parameter_hash = action.parameter_hash.clone();
let decision = Decision::Deny {
reason: "blocked by path guard".to_string(),
guard: "path-guard".to_string(),
};
let policy_hash = sha256_hex(b"policy-v1");
let body = ChioReceiptBody {
id: "rcpt-formal-1".to_string(),
timestamp: 1_700_000_000,
capability_id: "cap-formal-1".to_string(),
tool_server: "srv-files".to_string(),
tool_name: "read_file".to_string(),
action,
decision: Some(decision.clone()),
receipt_kind: Default::default(),
boundary_class: Default::default(),
observation_outcome: None,
tool_origin: Default::default(),
redaction_mode: Default::default(),
actor_chain: Vec::new(),
content_hash: sha256_hex(b"content"),
policy_hash: policy_hash.clone(),
evidence: vec![GuardEvidence {
guard_name: "path-guard".to_string(),
verdict: false,
details: Some("deny".to_string()),
}],
metadata: None,
trust_level: TrustLevel::Mediated,
tenant_id: None,
kernel_key: keypair.public_key(),
bbs_projection_version: None,
};
let receipt = ChioReceipt::sign(body, &keypair).test_unwrap();
assert!(receipt.verify_signature().test_unwrap());
let projection = ReceiptProjection::from_receipt(&receipt);
assert!(projection.couples(
"cap-formal-1",
"srv-files",
"read_file",
¶meter_hash,
&decision,
&policy_hash,
));
}