use thiserror::Error;
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum AdmissionDecision {
Admit {
stagger_ms: u64,
},
Reject {
live: u32,
cap: u32,
},
}
#[derive(Debug, Error, PartialEq, Eq)]
pub enum AdmissionError {
#[error(
"session cap reached: {live} live sessions, max_concurrent_sessions = {cap} (§9.2); \
stop a session or raise the cap before launching"
)]
CapReached {
live: u32,
cap: u32,
},
}
pub fn check_admission(live: u32, cap: u32, stagger_ms: u64) -> AdmissionDecision {
if live < cap {
AdmissionDecision::Admit { stagger_ms }
} else {
AdmissionDecision::Reject { live, cap }
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn admission_under_cap_admits() {
assert_eq!(
check_admission(0, 5, 2_000),
AdmissionDecision::Admit { stagger_ms: 2_000 }
);
assert_eq!(
check_admission(4, 5, 0),
AdmissionDecision::Admit { stagger_ms: 0 }
);
}
#[test]
fn admission_at_cap_rejects() {
assert_eq!(
check_admission(5, 5, 2_000),
AdmissionDecision::Reject { live: 5, cap: 5 }
);
assert_eq!(
check_admission(6, 5, 0),
AdmissionDecision::Reject { live: 6, cap: 5 }
);
}
#[test]
fn admission_zero_cap_always_rejects() {
assert_eq!(
check_admission(0, 0, 0),
AdmissionDecision::Reject { live: 0, cap: 0 }
);
}
#[test]
fn admission_admits_exactly_up_to_cap() {
for live in 0..3 {
assert!(matches!(
check_admission(live, 3, 0),
AdmissionDecision::Admit { .. }
));
}
assert!(matches!(
check_admission(3, 3, 0),
AdmissionDecision::Reject { .. }
));
}
#[test]
fn admission_error_display() {
let err = AdmissionError::CapReached { live: 5, cap: 5 };
let msg = err.to_string();
assert!(msg.contains("session cap reached"));
assert!(msg.contains("max_concurrent_sessions = 5"));
}
}