use std::time::Duration;
use chrono::{DateTime, Utc};
use crate::sandbox::SandboxState;
pub fn ttl_due(state: SandboxState, deadline: Option<DateTime<Utc>>) -> Option<DateTime<Utc>> {
match state {
SandboxState::Stopped | SandboxState::Failed => None,
_ => deadline,
}
}
pub fn idle_due(state: SandboxState, timeout_seconds: u32) -> Option<Duration> {
(state == SandboxState::Ready && timeout_seconds != 0)
.then(|| Duration::from_secs(u64::from(timeout_seconds)))
}
pub fn remaining(deadline: DateTime<Utc>, now: DateTime<Utc>) -> Duration {
(deadline - now).to_std().unwrap_or(Duration::ZERO)
}
#[cfg(test)]
mod tests {
use super::*;
const ALL_STATES: [SandboxState; 8] = [
SandboxState::Starting,
SandboxState::Ready,
SandboxState::Running,
SandboxState::Stopping,
SandboxState::Stopped,
SandboxState::Failed,
SandboxState::Pausing,
SandboxState::Paused,
];
fn at(seconds: i64) -> DateTime<Utc> {
DateTime::from_timestamp(seconds, 0).unwrap()
}
#[test]
fn only_a_terminal_sandbox_drops_its_ttl() {
let deadline = at(1_700_000_000);
for state in ALL_STATES {
let terminal = matches!(state, SandboxState::Stopped | SandboxState::Failed);
assert_eq!(
ttl_due(state, Some(deadline)),
(!terminal).then_some(deadline),
"{state}"
);
assert_eq!(ttl_due(state, None), None, "{state} without a deadline");
}
}
#[test]
fn only_a_ready_sandbox_with_a_timeout_goes_idle() {
for state in ALL_STATES {
let ready = state == SandboxState::Ready;
assert_eq!(
idle_due(state, 30),
ready.then(|| Duration::from_secs(30)),
"{state}"
);
assert_eq!(idle_due(state, 0), None, "{state} with idle disabled");
}
}
#[test]
fn a_past_deadline_is_due_now() {
assert_eq!(remaining(at(100), at(40)), Duration::from_secs(60));
assert_eq!(remaining(at(100), at(100)), Duration::ZERO);
assert_eq!(remaining(at(100), at(101)), Duration::ZERO);
}
}