use std::cell::{Cell, RefCell};
use std::fmt;
use std::time::{Duration, Instant};
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn default_budget_hash_bytes_to_u32() {
let b = default_budget_for_archetype(&Archetype("hash-bytes-to-u32"));
assert_eq!(b.max_per_case_ms, 1);
assert_eq!(b.max_total_seconds, 60);
assert_eq!(b.max_input_bytes, 64 * 1024);
}
#[test]
fn default_budget_decode_bytes_to_bytes() {
let b = default_budget_for_archetype(&Archetype("decode-bytes-to-bytes"));
assert_eq!(b.max_per_case_ms, 5);
assert_eq!(b.max_total_seconds, 120);
assert_eq!(b.max_input_bytes, 64 * 1024);
}
#[test]
fn default_budget_compression_bytes_to_bytes() {
let b = default_budget_for_archetype(&Archetype("compression-bytes-to-bytes"));
assert_eq!(b.max_per_case_ms, 50);
assert_eq!(b.max_total_seconds, 300);
assert_eq!(b.max_input_bytes, 64 * 1024);
}
#[test]
fn default_budget_unknown_falls_back() {
let b = default_budget_for_archetype(&Archetype("unknown-archetype"));
assert_eq!(b.max_per_case_ms, 1);
assert_eq!(b.max_total_seconds, 30);
assert_eq!(b.max_input_bytes, 1024);
}
#[test]
fn tracker_rejects_per_case_overrun() {
let budget = ReferenceBudget {
max_per_case_ms: 5,
max_total_seconds: 60,
max_input_bytes: 1024,
};
let mut tracker = BudgetTracker::new(budget, "test.op");
let err = tracker.record_case(Duration::from_millis(6)).unwrap_err();
assert_eq!(err.case_index, 0);
assert_eq!(err.elapsed_ms, 6);
assert_eq!(err.budget_ms, 5);
}
#[test]
fn tracker_rejects_total_overrun() {
let budget = ReferenceBudget {
max_per_case_ms: 1000,
max_total_seconds: 1,
max_input_bytes: 1024,
};
let mut tracker = BudgetTracker::new(budget, "test.op");
tracker.record_case(Duration::from_millis(500)).unwrap();
let err = tracker.record_case(Duration::from_millis(600)).unwrap_err();
assert_eq!(err.budget_ms, 1000);
}
#[test]
fn tracker_rejects_oversized_input() {
let budget = ReferenceBudget {
max_per_case_ms: 1000,
max_total_seconds: 60,
max_input_bytes: 4,
};
let tracker = BudgetTracker::new(budget, "test.op");
let err = tracker.check_input(&[0; 5]).unwrap_err();
assert_eq!(err.case_index, 0);
}
#[test]
fn certify_wrapper_detects_bomb() {
let budget = ReferenceBudget {
max_per_case_ms: 1,
max_total_seconds: 60,
max_input_bytes: 1024,
};
let tracker = BudgetTracker::new(budget, "test.op");
let slow_fn: fn(&[u8]) -> Vec<u8> = |_input| {
std::thread::sleep(std::time::Duration::from_millis(10));
vec![1]
};
with_certify_budget(tracker, slow_fn, || {
certify_budget_wrapper(&[]);
})
.unwrap();
let bomb = take_last_bomb().expect("expected a bomb");
assert_eq!(bomb.op_id, "test.op");
assert!(bomb.elapsed_ms >= 10);
}
}