rust_supervisor/test_support/assertions.rs
1//! Test assertions for supervisor diagnostics.
2//!
3//! The functions in this module are intentionally small and panic with direct
4//! messages so failing tests point at missing observability facts.
5
6use crate::event::payload::SupervisorEvent;
7use crate::journal::ring::EventJournal;
8use crate::observe::pipeline::TestRecorder;
9use crate::state::child::ChildLifecycleState;
10use crate::state::supervisor::SupervisorState;
11use crate::summary::builder::RunSummary;
12
13/// Asserts that events were emitted in strict sequence order.
14///
15/// # Arguments
16///
17/// - `events`: Events to inspect.
18///
19/// # Returns
20///
21/// This function does not return a value.
22///
23/// # Examples
24///
25/// ```
26/// let events: Vec<rust_supervisor::event::payload::SupervisorEvent> = Vec::new();
27/// rust_supervisor::test_support::assertions::assert_event_sequences_increase(&events);
28/// ```
29pub fn assert_event_sequences_increase(events: &[SupervisorEvent]) {
30 for pair in events.windows(2) {
31 assert!(
32 pair[0].sequence.value < pair[1].sequence.value,
33 "event sequence must increase"
34 );
35 }
36}
37
38/// Asserts that a journal retained a specific number of events.
39///
40/// # Arguments
41///
42/// - `journal`: Journal to inspect.
43/// - `expected`: Expected retained event count.
44///
45/// # Returns
46///
47/// This function does not return a value.
48pub fn assert_journal_len(journal: &EventJournal, expected: usize) {
49 assert_eq!(journal.len(), expected, "unexpected event journal length");
50}
51
52/// Asserts that a run summary contains at least one recent event.
53///
54/// # Arguments
55///
56/// - `summary`: Summary to inspect.
57///
58/// # Returns
59///
60/// This function does not return a value.
61pub fn assert_summary_has_recent_events(summary: &RunSummary) {
62 assert!(
63 !summary.recent_events.is_empty(),
64 "run summary must include recent events"
65 );
66}
67
68/// Asserts that a child path has the expected lifecycle state.
69///
70/// # Arguments
71///
72/// - `state`: Supervisor state to inspect.
73/// - `path`: Child path text.
74/// - `expected`: Expected lifecycle state.
75///
76/// # Returns
77///
78/// This function does not return a value.
79pub fn assert_child_state(state: &SupervisorState, path: &str, expected: ChildLifecycleState) {
80 let child = state
81 .children
82 .get(path)
83 .unwrap_or_else(|| panic!("missing child state for {path}"));
84 assert_eq!(child.state, expected, "unexpected child state");
85}
86
87/// Asserts that shutdown left no running children.
88///
89/// # Arguments
90///
91/// - `state`: Final supervisor state to inspect.
92///
93/// # Returns
94///
95/// This function does not return a value.
96pub fn assert_shutdown_without_orphaned_tasks(state: &SupervisorState) {
97 let running = state
98 .children
99 .values()
100 .filter(|child| matches!(child.state, ChildLifecycleState::Running))
101 .count();
102 assert_eq!(running, 0, "shutdown left running children");
103}
104
105/// Asserts that a test recorder saw at least one metric sample.
106///
107/// # Arguments
108///
109/// - `recorder`: Observability test recorder.
110///
111/// # Returns
112///
113/// This function does not return a value.
114pub fn assert_recorder_has_metrics(recorder: &TestRecorder) {
115 assert!(
116 !recorder.metrics.is_empty(),
117 "observability recorder must contain metrics"
118 );
119}