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, fake task outcomes, and deterministic jitter.
5
6use crate::error::types::TaskFailure;
7use crate::event::payload::{SupervisorEvent, What, Where};
8use crate::event::time::{CorrelationId, EventSequence, EventSequenceSource, EventTime, When};
9use crate::id::types::{Attempt, ChildId, Generation, SupervisorPath};
10use serde::{Deserialize, Serialize};
11use uuid::Uuid;
12
13/// Paused time source for deterministic tests.
14#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
15pub struct PausedTime {
16 /// Wall-clock time in nanoseconds since the Unix epoch.
17 pub unix_nanos: u128,
18 /// Monotonic time in nanoseconds.
19 pub monotonic_nanos: u128,
20 /// Supervisor uptime in milliseconds.
21 pub uptime_ms: u64,
22}
23
24impl PausedTime {
25 /// Creates a paused time source.
26 ///
27 /// # Arguments
28 ///
29 /// - `unix_nanos`: Wall-clock timestamp in nanoseconds.
30 /// - `monotonic_nanos`: Monotonic timestamp in nanoseconds.
31 /// - `uptime_ms`: Supervisor uptime in milliseconds.
32 ///
33 /// # Returns
34 ///
35 /// Returns a [`PausedTime`] value.
36 ///
37 /// # Examples
38 ///
39 /// ```
40 /// let time = rust_supervisor::test_support::factory::PausedTime::new(1, 2, 3);
41 /// assert_eq!(time.uptime_ms, 3);
42 /// ```
43 pub fn new(unix_nanos: u128, monotonic_nanos: u128, uptime_ms: u64) -> Self {
44 Self {
45 unix_nanos,
46 monotonic_nanos,
47 uptime_ms,
48 }
49 }
50
51 /// Creates deterministic event time.
52 ///
53 /// # Arguments
54 ///
55 /// - `generation`: Child generation for the event.
56 /// - `attempt`: Child attempt for the event.
57 ///
58 /// # Returns
59 ///
60 /// Returns an [`EventTime`] value.
61 pub fn event_time(&self, generation: Generation, attempt: Attempt) -> EventTime {
62 EventTime::deterministic(
63 self.unix_nanos,
64 self.monotonic_nanos,
65 self.uptime_ms,
66 generation,
67 attempt,
68 )
69 }
70}
71
72/// Deterministic jitter helper for backoff tests.
73#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
74pub struct DeterministicJitter {
75 /// Percentage points applied to the base delay.
76 pub percent: i64,
77}
78
79impl DeterministicJitter {
80 /// Creates a deterministic jitter source.
81 ///
82 /// # Arguments
83 ///
84 /// - `percent`: Signed percentage applied to the base delay.
85 ///
86 /// # Returns
87 ///
88 /// Returns a [`DeterministicJitter`] value.
89 pub fn new(percent: i64) -> Self {
90 Self { percent }
91 }
92
93 /// Applies jitter to a millisecond delay.
94 ///
95 /// # Arguments
96 ///
97 /// - `base_ms`: Base delay in milliseconds.
98 ///
99 /// # Returns
100 ///
101 /// Returns the adjusted delay in milliseconds.
102 ///
103 /// # Examples
104 ///
105 /// ```
106 /// let jitter = rust_supervisor::test_support::factory::DeterministicJitter::new(10);
107 /// assert_eq!(jitter.apply_ms(100), 110);
108 /// ```
109 pub fn apply_ms(&self, base_ms: u64) -> u64 {
110 let base = i128::from(base_ms);
111 let delta = base.saturating_mul(i128::from(self.percent)) / 100;
112 base.saturating_add(delta).max(0) as u64
113 }
114}
115
116/// Fake task outcome for tests that do not need a real runtime.
117#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
118pub enum FakeTaskOutcome {
119 /// Task completes successfully.
120 Complete,
121 /// Task is cancelled.
122 Cancel,
123 /// Task returns a typed failure.
124 Fail {
125 /// Failure payload returned by the task.
126 failure: TaskFailure,
127 },
128}
129
130/// Fake task factory that returns deterministic outcomes in order.
131#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
132pub struct FakeTaskFactory {
133 /// Ordered outcomes returned by attempts.
134 pub outcomes: Vec<FakeTaskOutcome>,
135 /// Next outcome index.
136 pub next_index: usize,
137}
138
139impl FakeTaskFactory {
140 /// Creates a fake task factory.
141 ///
142 /// # Arguments
143 ///
144 /// - `outcomes`: Ordered outcomes returned by attempts.
145 ///
146 /// # Returns
147 ///
148 /// Returns a [`FakeTaskFactory`].
149 ///
150 /// # Examples
151 ///
152 /// ```
153 /// let mut factory = rust_supervisor::test_support::factory::FakeTaskFactory::new(vec![
154 /// rust_supervisor::test_support::factory::FakeTaskOutcome::Complete,
155 /// ]);
156 /// assert!(matches!(
157 /// factory.next_outcome(),
158 /// rust_supervisor::test_support::factory::FakeTaskOutcome::Complete
159 /// ));
160 /// ```
161 pub fn new(outcomes: Vec<FakeTaskOutcome>) -> Self {
162 Self {
163 outcomes,
164 next_index: 0,
165 }
166 }
167
168 /// Returns the next deterministic outcome.
169 ///
170 /// # Arguments
171 ///
172 /// This function has no arguments.
173 ///
174 /// # Returns
175 ///
176 /// Returns the next [`FakeTaskOutcome`], or `Complete` after configured
177 /// outcomes are exhausted.
178 pub fn next_outcome(&mut self) -> FakeTaskOutcome {
179 let outcome = self
180 .outcomes
181 .get(self.next_index)
182 .cloned()
183 .unwrap_or(FakeTaskOutcome::Complete);
184 self.next_index = self.next_index.saturating_add(1);
185 outcome
186 }
187}
188
189/// Collector that stores supervisor events in memory.
190#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
191pub struct EventCollector {
192 /// Events collected in receive order.
193 pub events: Vec<SupervisorEvent>,
194}
195
196impl EventCollector {
197 /// Creates an empty collector.
198 ///
199 /// # Arguments
200 ///
201 /// This function has no arguments.
202 ///
203 /// # Returns
204 ///
205 /// Returns a new [`EventCollector`].
206 pub fn new() -> Self {
207 Self::default()
208 }
209
210 /// Pushes one event into the collector.
211 ///
212 /// # Arguments
213 ///
214 /// - `event`: Event to store.
215 ///
216 /// # Returns
217 ///
218 /// This function does not return a value.
219 pub fn push(&mut self, event: SupervisorEvent) {
220 self.events.push(event);
221 }
222
223 /// Returns collected event names.
224 ///
225 /// # Arguments
226 ///
227 /// This function has no arguments.
228 ///
229 /// # Returns
230 ///
231 /// Returns event names in receive order.
232 pub fn event_names(&self) -> Vec<&'static str> {
233 self.events.iter().map(|event| event.what.name()).collect()
234 }
235}
236
237/// Fixture that builds deterministic lifecycle events.
238#[derive(Debug)]
239pub struct EventFixture {
240 /// Paused time used for every event.
241 pub paused_time: PausedTime,
242 /// Sequence source used by the fixture.
243 pub sequences: EventSequenceSource,
244 /// Correlation identifier used by the fixture.
245 pub correlation_id: CorrelationId,
246 /// Configuration version attached to events.
247 pub config_version: u64,
248}
249
250impl EventFixture {
251 /// Creates an event fixture.
252 ///
253 /// # Arguments
254 ///
255 /// - `paused_time`: Time source for deterministic events.
256 /// - `config_version`: Configuration version attached to events.
257 ///
258 /// # Returns
259 ///
260 /// Returns an [`EventFixture`].
261 pub fn new(paused_time: PausedTime, config_version: u64) -> Self {
262 Self {
263 paused_time,
264 sequences: EventSequenceSource::new(),
265 correlation_id: CorrelationId::from_uuid(Uuid::nil()),
266 config_version,
267 }
268 }
269
270 /// Builds a deterministic event for a child.
271 ///
272 /// # Arguments
273 ///
274 /// - `child_id`: Child identifier attached to the event.
275 /// - `child_name`: Child name attached to the event.
276 /// - `what`: Event payload.
277 ///
278 /// # Returns
279 ///
280 /// Returns a [`SupervisorEvent`].
281 pub fn child_event(
282 &self,
283 child_id: ChildId,
284 child_name: impl Into<String>,
285 what: What,
286 ) -> SupervisorEvent {
287 let path = SupervisorPath::root().join(child_id.to_string());
288 let location = Where::new(path.clone()).with_child(child_id, child_name);
289 SupervisorEvent::new(
290 When::new(
291 self.paused_time
292 .event_time(Generation::initial(), Attempt::first()),
293 ),
294 location,
295 what,
296 self.sequences.next(),
297 self.correlation_id,
298 self.config_version,
299 )
300 }
301
302 /// Builds an event sequence value.
303 ///
304 /// # Arguments
305 ///
306 /// - `value`: Sequence value.
307 ///
308 /// # Returns
309 ///
310 /// Returns an [`EventSequence`].
311 pub fn sequence(value: u64) -> EventSequence {
312 EventSequence::new(value)
313 }
314}