#![cfg(feature = "sim")]
use actor_kit::sim::{FaultConfig, Sim, SimConfig};
fn chaos_config(_seed: u64) -> SimConfig {
SimConfig {
messages: 512,
faults: FaultConfig::chaos(0.02),
..SimConfig::default()
}
}
#[test]
fn sim_invariants_hold_across_500_seeds() {
for seed in 0..500u64 {
let mut sim = Sim::with_config(seed, chaos_config(seed));
let outcome = sim.run_to_quiescence();
assert!(
outcome.quiesced,
"seed {seed}: sim did not quiesce — replay with Sim::with_config({seed}, ..)"
);
}
}
#[test]
fn sim_determinism_same_seed_same_trace() {
for seed in [7u64, 42, 9_999] {
let mut a = Sim::with_config(seed, chaos_config(seed));
let out_a = a.run_to_quiescence();
let mut b = Sim::with_config(seed, chaos_config(seed));
let out_b = b.run_to_quiescence();
assert_eq!(
a.trace_hash(),
b.trace_hash(),
"seed {seed}: traces diverge — determinism contract violated"
);
assert_eq!(out_a.stats.processed, out_b.stats.processed);
}
}
#[test]
fn sim_delivery_accounting_closes_across_seeds() {
for seed in 500..560u64 {
let mut sim = Sim::with_config(seed, chaos_config(seed));
let outcome = sim.run_to_quiescence();
let s = &outcome.stats;
assert!(
s.processed + s.rejected + s.dropped >= s.injected,
"seed {seed}: messages vanished — injected {} vs processed {} + rejected {} + dropped {}",
s.injected,
s.processed,
s.rejected,
s.dropped
);
}
}
#[test]
#[ignore]
fn sim_long_range_seed_sweep() {
let start: u64 = std::env::var("SEED_START")
.ok()
.and_then(|v| v.parse().ok())
.unwrap_or(0);
let end: u64 = std::env::var("SEED_END")
.ok()
.and_then(|v| v.parse().ok())
.unwrap_or(start + 499);
for seed in start..=end {
let mut sim = Sim::with_config(seed, chaos_config(seed));
let outcome = sim.run_to_quiescence();
assert!(
outcome.quiesced,
"seed {seed}: did not quiesce — replay with Sim::with_config({seed}, ..)"
);
}
}