Skip to main content

rskit_testutil/
component.rs

1//! Component test doubles.
2
3use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
4
5use rskit_component::{Component, Health};
6use rskit_errors::AppResult;
7
8/// Deterministic component fake for lifecycle tests.
9pub struct FakeComponent {
10    name: String,
11    started: AtomicBool,
12    stopped: AtomicBool,
13    start_count: AtomicUsize,
14    stop_count: AtomicUsize,
15}
16
17impl FakeComponent {
18    /// Create a fake component with the given name.
19    #[must_use]
20    pub fn new(name: impl Into<String>) -> Self {
21        Self {
22            name: name.into(),
23            started: AtomicBool::new(false),
24            stopped: AtomicBool::new(false),
25            start_count: AtomicUsize::new(0),
26            stop_count: AtomicUsize::new(0),
27        }
28    }
29
30    /// Number of successful start calls.
31    #[must_use]
32    pub fn start_count(&self) -> usize {
33        self.start_count.load(Ordering::SeqCst)
34    }
35
36    /// Number of successful stop calls.
37    #[must_use]
38    pub fn stop_count(&self) -> usize {
39        self.stop_count.load(Ordering::SeqCst)
40    }
41}
42
43#[async_trait::async_trait]
44impl Component for FakeComponent {
45    fn name(&self) -> &str {
46        &self.name
47    }
48
49    async fn start(&self) -> AppResult<()> {
50        self.started.store(true, Ordering::SeqCst);
51        self.stopped.store(false, Ordering::SeqCst);
52        self.start_count.fetch_add(1, Ordering::SeqCst);
53        Ok(())
54    }
55
56    async fn stop(&self) -> AppResult<()> {
57        self.stopped.store(true, Ordering::SeqCst);
58        self.stop_count.fetch_add(1, Ordering::SeqCst);
59        Ok(())
60    }
61
62    fn health(&self) -> Health {
63        if self.started.load(Ordering::SeqCst) && !self.stopped.load(Ordering::SeqCst) {
64            Health::healthy(&self.name)
65        } else {
66            Health::unhealthy(&self.name, "not running")
67        }
68    }
69}