Skip to main content

assay_sim/
lib.rs

1pub mod attacks;
2pub mod corpus;
3pub mod differential;
4pub mod mutators;
5pub mod report;
6pub mod subprocess;
7pub mod suite;
8
9pub use report::{AttackResult, AttackStatus, SimReport};
10pub use suite::{run_suite, SuiteConfig, SuiteTier, TimeBudget};
11
12#[cfg(test)]
13mod tests {
14    use super::*;
15    use std::path::PathBuf;
16
17    #[test]
18    fn test_quick_suite() {
19        let cfg = SuiteConfig {
20            tier: SuiteTier::Quick,
21            target_bundle: PathBuf::from("placeholder"), // dynamic generation in suite for now
22            seed: 42,
23            verify_limits: None,
24        };
25
26        let report = run_suite(cfg).expect("Suite failed to run");
27
28        // Print full report on failure for debugging
29        if report.summary.bypassed > 0 {
30            println!("{}", serde_json::to_string_pretty(&report).unwrap());
31        }
32
33        // Invariant assertions (stable across attack additions):
34        // - No attack may bypass verification (security contract)
35        assert_eq!(
36            report.summary.bypassed, 0,
37            "SECURITY: {} attacks bypassed verification",
38            report.summary.bypassed
39        );
40        // - At least 1 attack must be blocked (sanity: attacks actually ran)
41        assert!(
42            report.summary.blocked >= 1,
43            "SANITY: no attacks were blocked — suite may not have run"
44        );
45        // - At least 1 check must pass (sanity: differential tests ran)
46        assert!(
47            report.summary.passed >= 1,
48            "SANITY: no checks passed — differential tests may not have run"
49        );
50        // - Every result must have a valid status classification:
51        //   Blocked/Passed are normal outcomes.
52        //   Error is acceptable for chaos IO faults (WouldBlock, persistent EINTR)
53        //   but NOT for integrity/differential tests.
54        for r in &report.results {
55            let is_chaos_io = r.name.starts_with("chaos.io_fault.");
56            match r.status {
57                AttackStatus::Blocked | AttackStatus::Passed => {} // always ok
58                AttackStatus::Error if is_chaos_io => {}           // infra IO, acceptable
59                _ => panic!(
60                    "Unexpected status {:?} for '{}': {:?}",
61                    r.status, r.name, r.message
62                ),
63            }
64        }
65    }
66}