use std::hint::black_box;
use std::time::{Duration, Instant};
use aa_proto::assembly::audit::v1::{audit_event::Detail, AuditEvent, ToolCallDetail};
use aa_runtime::pipeline::enforcement::{RuntimeScanner, DEFAULT_MAX_FIELD_BYTES};
use aa_runtime::pipeline::{EnrichedEvent, EventSource};
fn percentile(sorted: &[Duration], pct: f64) -> Duration {
let idx = ((sorted.len() as f64) * pct / 100.0).ceil() as usize;
sorted[idx.min(sorted.len() - 1)]
}
#[test]
fn percentile_picks_expected_rank() {
let sorted: Vec<Duration> = (1..=100).map(Duration::from_millis).collect();
assert_eq!(percentile(&sorted, 50.0), Duration::from_millis(51));
assert_eq!(percentile(&sorted, 99.0), Duration::from_millis(100));
assert_eq!(percentile(&sorted, 100.0), Duration::from_millis(100));
}
const AWS_KEY: &str = "AKIAIOSFODNN7EXAMPLE";
const FIXTURE_SIZES: &[(&str, usize)] = &[("small", 256), ("medium", 4 * 1024), ("near_cap", 60 * 1024)];
fn args_json_payload(target_bytes: usize) -> Vec<u8> {
const FILLER: &str = "lorem ipsum dolor sit amet consectetur adipiscing elit sed do; ";
let prefix = format!(r#"{{"api_key":"{AWS_KEY}","note":""#);
let suffix = r#""}"#;
let mut payload = String::with_capacity(target_bytes + FILLER.len());
payload.push_str(&prefix);
while payload.len() + suffix.len() < target_bytes {
payload.push_str(FILLER);
}
payload.push_str(suffix);
payload.into_bytes()
}
#[test]
fn fixtures_cover_representative_sizes() {
for (name, target) in FIXTURE_SIZES {
let payload = args_json_payload(*target);
assert!(
payload.len() >= *target,
"{name} fixture ({}) under target {target}",
payload.len()
);
assert!(
payload.windows(AWS_KEY.len()).any(|w| w == AWS_KEY.as_bytes()),
"{name} fixture must embed the credential to exercise redaction"
);
}
let near_cap = args_json_payload(60 * 1024);
assert!(near_cap.len() > 32 * 1024, "near_cap fixture should be large");
assert!(
near_cap.len() < DEFAULT_MAX_FIELD_BYTES,
"near_cap fixture must stay under the {DEFAULT_MAX_FIELD_BYTES}-byte cap"
);
}
fn sla_p99() -> Duration {
if let Some(ms) = std::env::var("AA_BENCH_SLA_P99_MS").ok().and_then(|v| v.parse().ok()) {
return Duration::from_millis(ms);
}
let default_ms = if cfg!(debug_assertions) { 50 } else { 5 };
Duration::from_millis(default_ms)
}
fn iterations() -> usize {
std::env::var("AA_BENCH_ITERS")
.ok()
.and_then(|v| v.parse().ok())
.unwrap_or(2_000)
}
fn tool_call_event(args_json: Vec<u8>) -> EnrichedEvent {
EnrichedEvent {
inner: AuditEvent {
detail: Some(Detail::ToolCall(ToolCallDetail {
args_json,
..Default::default()
})),
..Default::default()
},
received_at_ms: 0,
source: EventSource::Sdk,
agent_id: "bench-agent".to_string(),
connection_id: 0,
sequence_number: 0,
}
}
#[test]
fn enforce_scan_p99_within_budget() {
let scanner = RuntimeScanner::new();
let iters = iterations();
let mut latencies: Vec<Duration> = Vec::with_capacity(iters * FIXTURE_SIZES.len());
for (_, size) in FIXTURE_SIZES {
let payload = args_json_payload(*size);
for _ in 0..64 {
let mut event = tool_call_event(payload.clone());
black_box(scanner.enforce(&mut event));
}
for _ in 0..iters {
let mut event = tool_call_event(payload.clone());
let started = Instant::now();
let outcome = scanner.enforce(&mut event);
latencies.push(started.elapsed());
assert!(!outcome.is_clean(), "seeded credential must be redacted");
}
}
latencies.sort();
let total = latencies.len();
let p50 = percentile(&latencies, 50.0);
let p95 = percentile(&latencies, 95.0);
let p99 = percentile(&latencies, 99.0);
let p999 = percentile(&latencies, 99.9);
let max = latencies[total - 1];
eprintln!();
eprintln!("=== RuntimeScanner::enforce() Scan-Latency Test ===");
eprintln!(" Fixtures: {FIXTURE_SIZES:?}");
eprintln!(" Iters/size: {iters}");
eprintln!(" Total scans: {total}");
eprintln!();
eprintln!(" p50: {p50:>10.3?}");
eprintln!(" p95: {p95:>10.3?}");
eprintln!(" p99: {p99:>10.3?}");
eprintln!(" p999: {p999:>10.3?}");
eprintln!(" max: {max:>10.3?}");
eprintln!();
let sla = sla_p99();
assert!(p99 < sla, "scan p99 latency {p99:?} exceeds budget (target: {sla:?})");
}