Skip to main content

ci_engine/service/
fake.rs

1// SPDX-License-Identifier: Apache-2.0
2//! Recording command boundary for service-provider tests.
3
4use std::sync::Mutex;
5
6use super::{CommandOutcome, CommandRunner, ServiceError};
7
8/// A recording command runner with configurable `docker run` failure.
9#[derive(Debug)]
10pub struct FakeProvider {
11    calls: Mutex<Vec<Vec<String>>>,
12    fail_run_at: Option<usize>,
13    run_count: Mutex<usize>,
14}
15
16impl FakeProvider {
17    /// Construct a recorder whose calls all succeed.
18    #[must_use]
19    pub fn new() -> Self {
20        Self {
21            calls: Mutex::new(Vec::new()),
22            fail_run_at: None,
23            run_count: Mutex::new(0),
24        }
25    }
26
27    /// Construct a recorder whose first `docker run` fails.
28    #[must_use]
29    pub fn failing() -> Self {
30        Self::failing_run_at(1)
31    }
32
33    /// Construct a recorder whose one-based `docker run` number fails.
34    #[must_use]
35    pub fn failing_run_at(number: usize) -> Self {
36        Self {
37            calls: Mutex::new(Vec::new()),
38            fail_run_at: Some(number),
39            run_count: Mutex::new(0),
40        }
41    }
42
43    /// Snapshot every recorded full argv.
44    #[must_use]
45    pub fn calls(&self) -> Vec<Vec<String>> {
46        self.calls.lock().expect("calls mutex poisoned").clone()
47    }
48}
49
50impl Default for FakeProvider {
51    fn default() -> Self {
52        Self::new()
53    }
54}
55
56impl CommandRunner for FakeProvider {
57    fn run(&self, program: &str, args: &[String]) -> Result<CommandOutcome, ServiceError> {
58        let mut call = Vec::with_capacity(args.len() + 1);
59        call.push(program.to_string());
60        call.extend(args.iter().cloned());
61        let success = if args.first().map(String::as_str) == Some("run") {
62            let mut count = self.run_count.lock().expect("run-count mutex poisoned");
63            *count += 1;
64            self.fail_run_at != Some(*count)
65        } else {
66            true
67        };
68        self.calls.lock().expect("calls mutex poisoned").push(call);
69        Ok(CommandOutcome {
70            success,
71            output: if success {
72                String::new()
73            } else {
74                "fake failure".to_string()
75            },
76        })
77    }
78}