use std::sync::Arc;
use std::sync::atomic::{AtomicUsize, Ordering as AtomicOrdering};
use std::time::Duration;
fn kernel_with_echo() -> (ChioKernel, Keypair, ChioScope) {
let mut kernel = ChioKernel::new(make_config());
kernel.register_tool_server(Box::new(EchoServer::new("srv-a", vec!["read_file"])));
let agent_kp = make_keypair();
let scope = make_scope(vec![make_grant("srv-a", "read_file")]);
(kernel, agent_kp, scope)
}
#[test]
fn emergency_stop_forces_deny_on_next_evaluate() {
let (kernel, agent_kp, scope) = kernel_with_echo();
let cap = make_capability(&kernel, &agent_kp, scope, 300);
let request = make_request("req-allow", &cap, "read_file", "srv-a");
let response = kernel.evaluate_tool_call_blocking(&request).unwrap();
assert_eq!(response.verdict, Verdict::Allow);
kernel.emergency_stop("operator halted").unwrap();
assert!(kernel.is_emergency_stopped());
let denied = make_request("req-deny", &cap, "read_file", "srv-a");
let response = kernel.evaluate_tool_call_blocking(&denied).unwrap();
assert_eq!(response.verdict, Verdict::Deny);
let reason = response.reason.as_deref().unwrap_or("");
assert_eq!(reason, EMERGENCY_STOP_DENY_REASON);
}
#[test]
fn emergency_status_exposes_since_and_reason() {
let (kernel, _, _) = kernel_with_echo();
assert!(!kernel.is_emergency_stopped());
assert!(kernel.emergency_stopped_since().is_none());
assert!(kernel.emergency_stop_reason().is_none());
kernel.emergency_stop("compromised agent detected").unwrap();
assert!(kernel.is_emergency_stopped());
assert!(kernel.emergency_stopped_since().is_some());
assert_eq!(
kernel.emergency_stop_reason().as_deref(),
Some("compromised agent detected")
);
kernel.emergency_resume().unwrap();
assert!(!kernel.is_emergency_stopped());
assert!(kernel.emergency_stopped_since().is_none());
assert!(kernel.emergency_stop_reason().is_none());
}
#[test]
fn emergency_stop_then_resume_restores_allow_path() {
let (kernel, agent_kp, scope) = kernel_with_echo();
let cap = make_capability(&kernel, &agent_kp, scope, 300);
kernel.emergency_stop("drill").unwrap();
let denied =
kernel.evaluate_tool_call_blocking(&make_request("req-1", &cap, "read_file", "srv-a"));
assert_eq!(denied.unwrap().verdict, Verdict::Deny);
kernel.emergency_resume().unwrap();
let response = kernel
.evaluate_tool_call_blocking(&make_request("req-2", &cap, "read_file", "srv-a"))
.unwrap();
assert_eq!(response.verdict, Verdict::Allow);
}
#[test]
fn concurrent_evaluate_and_emergency_stop_is_race_free() {
let (kernel, agent_kp, scope) = kernel_with_echo();
let cap = make_capability(&kernel, &agent_kp, scope, 300);
let shared = Arc::new(kernel);
let deny_count = Arc::new(AtomicUsize::new(0));
let allow_count = Arc::new(AtomicUsize::new(0));
let mut workers = Vec::new();
for i in 0..16 {
let kernel = Arc::clone(&shared);
let cap = cap.clone();
let deny = Arc::clone(&deny_count);
let allow = Arc::clone(&allow_count);
workers.push(thread::spawn(move || {
if i % 2 == 0 {
thread::sleep(Duration::from_millis(1));
}
for j in 0..8 {
let request_id = format!("race-{i}-{j}");
let request = make_request(&request_id, &cap, "read_file", "srv-a");
match kernel.evaluate_tool_call_blocking(&request) {
Ok(response) => match response.verdict {
Verdict::Allow => {
allow.fetch_add(1, AtomicOrdering::SeqCst);
}
Verdict::Deny => {
deny.fetch_add(1, AtomicOrdering::SeqCst);
assert_eq!(
response.reason.as_deref(),
Some(EMERGENCY_STOP_DENY_REASON),
"only emergency stop should cause a deny in this test"
);
}
Verdict::PendingApproval => {
panic!("unexpected pending approval in emergency test");
}
},
Err(error) => panic!("evaluate_tool_call_blocking errored: {error}"),
}
}
}));
}
thread::sleep(Duration::from_millis(2));
shared.emergency_stop("concurrent drill").unwrap();
for worker in workers {
worker.join().expect("worker thread panicked");
}
assert!(
deny_count.load(AtomicOrdering::SeqCst) > 0,
"expected at least one evaluation to observe the emergency stop"
);
let _ = allow_count.load(AtomicOrdering::SeqCst);
}
#[test]
fn emergency_stop_receipt_records_deny_decision() {
let (kernel, agent_kp, scope) = kernel_with_echo();
let cap = make_capability(&kernel, &agent_kp, scope, 300);
kernel.emergency_stop("audit trail").unwrap();
let request = make_request("req-receipt", &cap, "read_file", "srv-a");
let response = kernel.evaluate_tool_call_blocking(&request).unwrap();
assert_eq!(response.verdict, Verdict::Deny);
assert!(
response.receipt.verify_signature().unwrap(),
"emergency stop deny receipt must verify"
);
match response.receipt.decision.clone() {
Decision::Deny { reason, .. } => assert_eq!(reason, EMERGENCY_STOP_DENY_REASON),
other => panic!("expected deny decision, got {other:?}"),
}
}