use aristo_core::config::Aggressiveness;
use super::state::ThrottleRecord;
pub fn cooldown_secs(level: Aggressiveness) -> Option<u64> {
match level {
Aggressiveness::Off => None,
Aggressiveness::Low => Some(30 * 60),
Aggressiveness::Medium => Some(10 * 60),
Aggressiveness::High => Some(3 * 60),
}
}
pub fn rearm_step(level: Aggressiveness, base: f64) -> f64 {
let k = match level {
Aggressiveness::Off => return f64::INFINITY, Aggressiveness::Low => 1.0,
Aggressiveness::Medium => 0.66,
Aggressiveness::High => 0.33,
};
(base * k).max(1.0)
}
pub fn may_surface(
record: Option<&ThrottleRecord>,
now_epoch: u64,
level: Aggressiveness,
current_metric: f64,
base: f64,
) -> bool {
let Some(cooldown) = cooldown_secs(level) else {
return false; };
let Some(record) = record else {
return true; };
let elapsed = now_epoch.saturating_sub(record.last_fired_epoch);
if elapsed >= cooldown {
return true;
}
current_metric >= record.last_surfaced_metric + rearm_step(level, base)
}
pub fn record_after_surface(now_epoch: u64, current_metric: f64) -> ThrottleRecord {
ThrottleRecord {
last_fired_epoch: now_epoch,
last_surfaced_metric: current_metric,
}
}
#[cfg(test)]
mod tests {
use super::*;
const HOUR: u64 = 3600;
#[test]
fn off_never_surfaces() {
assert!(!may_surface(None, HOUR, Aggressiveness::Off, 100.0, 3.0));
}
#[test]
fn never_surfaced_always_may() {
assert!(may_surface(None, 0, Aggressiveness::Low, 1.0, 3.0));
}
#[test]
fn waits_out_the_cooldown() {
let rec = ThrottleRecord {
last_fired_epoch: HOUR,
last_surfaced_metric: 3.0,
};
assert!(!may_surface(
Some(&rec),
HOUR + 60,
Aggressiveness::Medium,
3.0,
3.0
));
assert!(may_surface(
Some(&rec),
HOUR + 11 * 60,
Aggressiveness::Medium,
3.0,
3.0
));
}
#[test]
fn material_increase_rearms_inside_cooldown() {
let rec = ThrottleRecord {
last_fired_epoch: HOUR,
last_surfaced_metric: 3.0,
};
assert!(
!may_surface(Some(&rec), HOUR + 60, Aggressiveness::Medium, 4.0, 3.0),
"metric 4.0 is below the re-arm threshold 4.98 → still throttled"
);
assert!(
may_surface(Some(&rec), HOUR + 60, Aggressiveness::Medium, 5.0, 3.0),
"metric 5.0 clears the 4.98 re-arm threshold"
);
}
#[test]
fn higher_aggressiveness_rearms_on_smaller_increase() {
let rec = ThrottleRecord {
last_fired_epoch: HOUR,
last_surfaced_metric: 3.0,
};
assert!(may_surface(
Some(&rec),
HOUR + 60,
Aggressiveness::High,
4.5,
3.0
));
assert!(!may_surface(
Some(&rec),
HOUR + 60,
Aggressiveness::Low,
4.5,
3.0
));
}
#[test]
fn cooldown_shrinks_with_aggressiveness() {
assert_eq!(cooldown_secs(Aggressiveness::Off), None);
let low = cooldown_secs(Aggressiveness::Low).unwrap();
let med = cooldown_secs(Aggressiveness::Medium).unwrap();
let high = cooldown_secs(Aggressiveness::High).unwrap();
assert!(low > med && med > high, "{low} > {med} > {high}");
}
}