Skip to main content

actionqueue_engine/lease/
expiry.rs

1//! Lease expiry evaluation logic.
2//!
3//! This module implements lease expiry determination:
4//! - Evaluates whether a lease is still active or expired.
5//! - Exposes an explicit eligibility signal for re-entering the Ready state.
6//!
7//! For identical inputs, expiry evaluation must always return the same result.
8
9use crate::lease::model::{Lease, LeaseExpiry};
10
11/// Result of lease expiry evaluation.
12#[derive(Debug, Clone, PartialEq, Eq)]
13#[must_use]
14pub enum ExpiryResult {
15    /// Lease is still active (not yet expired).
16    Active,
17    /// Lease has expired and the run can re-enter eligibility.
18    Expired,
19}
20
21impl ExpiryResult {
22    /// Returns true when the evaluated lease is expired.
23    pub const fn is_expired(&self) -> bool {
24        matches!(self, Self::Expired)
25    }
26
27    /// Returns true when the run may re-enter eligibility.
28    ///
29    /// In Phase 5 lease semantics, this is true if and only if the lease is
30    /// expired.
31    pub const fn can_reenter_eligibility(&self) -> bool {
32        self.is_expired()
33    }
34}
35
36/// Evaluates whether a lease is still active or has expired.
37///
38/// A lease is considered expired if its `expires_at` timestamp is less than
39/// or equal to the current time.
40pub fn evaluate_expires_at(current_time: u64, lease_expiry: LeaseExpiry) -> ExpiryResult {
41    if current_time >= lease_expiry.expires_at() {
42        ExpiryResult::Expired
43    } else {
44        ExpiryResult::Active
45    }
46}
47
48/// Evaluates whether a lease is still active or has expired.
49///
50/// A lease is considered expired if its `expires_at` timestamp is less than
51/// or equal to the current time.
52///
53/// Returns `ExpiryResult::Expired` if the lease is expired, otherwise
54/// `ExpiryResult::Active`.
55pub fn evaluate(current_time: u64, lease: &Lease) -> ExpiryResult {
56    evaluate_expires_at(current_time, lease.expiry())
57}
58
59#[cfg(test)]
60mod tests {
61    use super::*;
62
63    #[test]
64    fn lease_is_active_when_current_time_is_before_expiry() {
65        let current_time = 1500;
66        let expiry = LeaseExpiry::at(2000);
67
68        let result = evaluate_expires_at(current_time, expiry);
69
70        assert_eq!(result, ExpiryResult::Active);
71        assert!(!result.can_reenter_eligibility());
72    }
73
74    #[test]
75    fn lease_is_expired_when_current_time_equals_expiry() {
76        let current_time = 2000;
77        let expiry = LeaseExpiry::at(2000);
78
79        let result = evaluate_expires_at(current_time, expiry);
80
81        assert_eq!(result, ExpiryResult::Expired);
82        assert!(result.can_reenter_eligibility());
83    }
84
85    #[test]
86    fn lease_is_expired_when_current_time_is_after_expiry() {
87        let current_time = 2500;
88        let expiry = LeaseExpiry::at(2000);
89
90        let result = evaluate_expires_at(current_time, expiry);
91
92        assert_eq!(result, ExpiryResult::Expired);
93        assert!(result.can_reenter_eligibility());
94    }
95
96    #[test]
97    fn evaluate_lease_struct() {
98        let current_time = 2500;
99        let run_id = actionqueue_core::ids::RunId::new();
100        let owner = crate::lease::model::LeaseOwner::new("worker-1");
101        let expiry = LeaseExpiry::at(2000);
102        let lease = Lease::new(run_id, owner, expiry);
103
104        let result = evaluate(current_time, &lease);
105
106        assert_eq!(result, ExpiryResult::Expired);
107    }
108
109    #[test]
110    fn evaluation_is_deterministic() {
111        let current_time = 2500;
112        let expiry = LeaseExpiry::at(2000);
113
114        let first = evaluate_expires_at(current_time, expiry);
115        let second = evaluate_expires_at(current_time, expiry);
116
117        assert_eq!(first, second);
118    }
119}