Skip to main content

rust_supervisor/test_support/
factory.rs

1//! Deterministic helpers for supervisor tests.
2//!
3//! The module provides small reusable fixtures for event collection, paused
4//! time, and deterministic jitter.
5
6use crate::event::payload::{SupervisorEvent, What, Where};
7use crate::event::time::{CorrelationId, EventSequence, EventSequenceSource, EventTime, When};
8use crate::id::types::{Attempt, ChildId, Generation, SupervisorPath};
9use serde::{Deserialize, Serialize};
10use uuid::Uuid;
11
12/// Paused time source for deterministic tests.
13#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
14pub struct PausedTime {
15    /// Wall-clock time in nanoseconds since the Unix epoch.
16    pub unix_nanos: u128,
17    /// Monotonic time in nanoseconds.
18    pub monotonic_nanos: u128,
19    /// Supervisor uptime in milliseconds.
20    pub uptime_ms: u64,
21}
22
23impl PausedTime {
24    /// Creates a paused time source.
25    ///
26    /// # Arguments
27    ///
28    /// - `unix_nanos`: Wall-clock timestamp in nanoseconds.
29    /// - `monotonic_nanos`: Monotonic timestamp in nanoseconds.
30    /// - `uptime_ms`: Supervisor uptime in milliseconds.
31    ///
32    /// # Returns
33    ///
34    /// Returns a [`PausedTime`] value.
35    ///
36    /// # Examples
37    ///
38    /// ```
39    /// let time = rust_supervisor::test_support::factory::PausedTime::new(1, 2, 3);
40    /// assert_eq!(time.uptime_ms, 3);
41    /// ```
42    pub fn new(unix_nanos: u128, monotonic_nanos: u128, uptime_ms: u64) -> Self {
43        Self {
44            unix_nanos,
45            monotonic_nanos,
46            uptime_ms,
47        }
48    }
49
50    /// Creates deterministic event time.
51    ///
52    /// # Arguments
53    ///
54    /// - `generation`: Child generation for the event.
55    /// - `attempt`: Child attempt for the event.
56    ///
57    /// # Returns
58    ///
59    /// Returns an [`EventTime`] value.
60    pub fn event_time(&self, generation: Generation, attempt: Attempt) -> EventTime {
61        EventTime::deterministic(
62            self.unix_nanos,
63            self.monotonic_nanos,
64            self.uptime_ms,
65            generation,
66            attempt,
67        )
68    }
69}
70
71/// Deterministic jitter helper for backoff tests.
72#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
73pub struct DeterministicJitter {
74    /// Percentage points applied to the base delay.
75    pub percent: i64,
76}
77
78impl DeterministicJitter {
79    /// Creates a deterministic jitter source.
80    ///
81    /// # Arguments
82    ///
83    /// - `percent`: Signed percentage applied to the base delay.
84    ///
85    /// # Returns
86    ///
87    /// Returns a [`DeterministicJitter`] value.
88    pub fn new(percent: i64) -> Self {
89        Self { percent }
90    }
91
92    /// Applies jitter to a millisecond delay.
93    ///
94    /// # Arguments
95    ///
96    /// - `base_ms`: Base delay in milliseconds.
97    ///
98    /// # Returns
99    ///
100    /// Returns the adjusted delay in milliseconds.
101    ///
102    /// # Examples
103    ///
104    /// ```
105    /// let jitter = rust_supervisor::test_support::factory::DeterministicJitter::new(10);
106    /// assert_eq!(jitter.apply_ms(100), 110);
107    /// ```
108    pub fn apply_ms(&self, base_ms: u64) -> u64 {
109        let base = i128::from(base_ms);
110        let delta = base.saturating_mul(i128::from(self.percent)) / 100;
111        base.saturating_add(delta).max(0) as u64
112    }
113}
114
115/// Collector that stores supervisor events in memory.
116#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
117pub struct EventCollector {
118    /// Events collected in receive order.
119    pub events: Vec<SupervisorEvent>,
120}
121
122impl EventCollector {
123    /// Creates an empty collector.
124    ///
125    /// # Arguments
126    ///
127    /// This function has no arguments.
128    ///
129    /// # Returns
130    ///
131    /// Returns a new [`EventCollector`].
132    pub fn new() -> Self {
133        Self::default()
134    }
135
136    /// Pushes one event into the collector.
137    ///
138    /// # Arguments
139    ///
140    /// - `event`: Event to store.
141    ///
142    /// # Returns
143    ///
144    /// This function does not return a value.
145    pub fn push(&mut self, event: SupervisorEvent) {
146        self.events.push(event);
147    }
148
149    /// Returns collected event names.
150    ///
151    /// # Arguments
152    ///
153    /// This function has no arguments.
154    ///
155    /// # Returns
156    ///
157    /// Returns event names in receive order.
158    pub fn event_names(&self) -> Vec<&'static str> {
159        self.events.iter().map(|event| event.what.name()).collect()
160    }
161}
162
163/// Fixture that builds deterministic lifecycle events.
164#[derive(Debug)]
165pub struct EventFixture {
166    /// Paused time used for every event.
167    pub paused_time: PausedTime,
168    /// Sequence source used by the fixture.
169    pub sequences: EventSequenceSource,
170    /// Correlation identifier used by the fixture.
171    pub correlation_id: CorrelationId,
172    /// Configuration version attached to events.
173    pub config_version: u64,
174}
175
176impl EventFixture {
177    /// Creates an event fixture.
178    ///
179    /// # Arguments
180    ///
181    /// - `paused_time`: Time source for deterministic events.
182    /// - `config_version`: Configuration version attached to events.
183    ///
184    /// # Returns
185    ///
186    /// Returns an [`EventFixture`].
187    pub fn new(paused_time: PausedTime, config_version: u64) -> Self {
188        Self {
189            paused_time,
190            sequences: EventSequenceSource::new(),
191            correlation_id: CorrelationId::from_uuid(Uuid::nil()),
192            config_version,
193        }
194    }
195
196    /// Builds a deterministic event for a child.
197    ///
198    /// # Arguments
199    ///
200    /// - `child_id`: Child identifier attached to the event.
201    /// - `child_name`: Child name attached to the event.
202    /// - `what`: Event payload.
203    ///
204    /// # Returns
205    ///
206    /// Returns a [`SupervisorEvent`].
207    pub fn child_event(
208        &self,
209        child_id: ChildId,
210        child_name: impl Into<String>,
211        what: What,
212    ) -> SupervisorEvent {
213        let path = SupervisorPath::root().join(child_id.to_string());
214        let location = Where::new(path.clone()).with_child(child_id, child_name);
215        SupervisorEvent::new(
216            When::new(
217                self.paused_time
218                    .event_time(Generation::initial(), Attempt::first()),
219            ),
220            location,
221            what,
222            self.sequences.next(),
223            self.correlation_id,
224            self.config_version,
225        )
226    }
227
228    /// Builds an event sequence value.
229    ///
230    /// # Arguments
231    ///
232    /// - `value`: Sequence value.
233    ///
234    /// # Returns
235    ///
236    /// Returns an [`EventSequence`].
237    pub fn sequence(value: u64) -> EventSequence {
238        EventSequence::new(value)
239    }
240}