Skip to main content

onlyne_client/backend/
fake.rs

1use super::*;
2use std::collections::{HashMap, HashSet};
3use std::sync::{Arc, Mutex};
4
5#[derive(Clone, Default)]
6pub struct FakeBackend {
7    state: Arc<Mutex<HashMap<String, SessionRef>>>,
8    failed_probes: Arc<Mutex<HashSet<String>>>,
9    pub fail_available: bool,
10}
11
12impl FakeBackend {
13    pub fn new() -> Self {
14        Self::default()
15    }
16
17    fn guard<'a, T>(
18        mutex: &'a Mutex<T>,
19        task_id: &str,
20        what: &str,
21    ) -> Result<std::sync::MutexGuard<'a, T>> {
22        mutex
23            .lock()
24            .map_err(|_| anyhow::anyhow!("fake {what} lock poisoned for task {task_id}"))
25    }
26
27    pub fn sessions(&self) -> HashMap<String, SessionRef> {
28        Self::guard(&self.state, "<sessions>", "state")
29            .unwrap_or_else(|err| panic!("{err}"))
30            .clone()
31    }
32
33    /// Force probes for one task to report a dead backend resource.
34    pub fn fail_probe(&self, task_id: &str) {
35        Self::guard(&self.failed_probes, task_id, "failed_probes")
36            .unwrap_or_else(|err| panic!("{err}"))
37            .insert(task_id.to_owned());
38    }
39
40    /// Clear a forced probe failure for one task.
41    pub fn clear_probe_failure(&self, task_id: &str) {
42        Self::guard(&self.failed_probes, task_id, "failed_probes")
43            .unwrap_or_else(|err| panic!("{err}"))
44            .remove(task_id);
45    }
46}
47
48impl SessionBackend for FakeBackend {
49    fn name(&self) -> &'static str {
50        "fake"
51    }
52    fn capabilities(&self) -> Capabilities {
53        Capabilities {
54            spawn: true,
55            attach: true,
56            probe: true,
57            close: true,
58            focus: true,
59            rename: true,
60        }
61    }
62    fn available(&self) -> Result<bool> {
63        Ok(!self.fail_available)
64    }
65    fn spawn(&self, spec: SpawnSpec) -> Result<SessionRef> {
66        let session = SessionRef {
67            task_id: spec.task_id.clone(),
68            backend: self.name().into(),
69            backend_ref: serde_json::json!({"id": spec.task_id}),
70            generation: 1,
71        };
72        Self::guard(&self.state, &session.task_id, "state")?.insert(spec.task_id, session.clone());
73        Ok(session)
74    }
75    fn attach(&self, session: &SessionRef) -> Result<SessionRef> {
76        if Self::guard(&self.state, &session.task_id, "state")?.contains_key(&session.task_id) {
77            Ok(session.clone())
78        } else {
79            anyhow::bail!("fake session not found: {}", session.task_id)
80        }
81    }
82    fn probe(&self, session: &SessionRef) -> Result<ResourceProbe> {
83        let failed = Self::guard(&self.failed_probes, &session.task_id, "failed_probes")?
84            .contains(&session.task_id);
85        Ok(ResourceProbe {
86            alive: !failed
87                && Self::guard(&self.state, &session.task_id, "state")?
88                    .contains_key(&session.task_id),
89            attached: !failed,
90            detail: failed.then(|| serde_json::json!({"forced": "probe_failure"})),
91        })
92    }
93    fn close(&self, session: &SessionRef, _reason: CloseReason, _force: bool) -> Result<()> {
94        Self::guard(&self.state, &session.task_id, "state")?.remove(&session.task_id);
95        Ok(())
96    }
97}