clnrm_core/backend/
mock.rs

1//! Mock backend for high-performance testing
2//!
3//! Provides instant command execution for testing without Docker overhead.
4//! Following core team best practices for fast, reliable test execution.
5
6use crate::backend::{Backend, Cmd, RunResult};
7use crate::error::Result;
8use std::collections::HashMap;
9
10/// High-performance mock backend for fast testing
11/// Replaces slow Docker operations with instant responses
12#[derive(Debug, Clone)]
13pub struct MockBackend {
14    /// Mock responses for different commands
15    responses: HashMap<String, MockResponse>,
16}
17
18#[derive(Debug, Clone)]
19pub struct MockResponse {
20    stdout: String,
21    stderr: String,
22    exit_code: i32,
23}
24
25impl MockBackend {
26    /// Create a new mock backend with default responses
27    pub fn new() -> Self {
28        let mut responses = HashMap::new();
29
30        // Pre-configure common mock responses for fast testing
31        responses.insert(
32            "echo".to_string(),
33            MockResponse {
34                stdout: "mock echo output".to_string(),
35                stderr: "".to_string(),
36                exit_code: 0,
37            },
38        );
39
40        responses.insert(
41            "cat".to_string(),
42            MockResponse {
43                stdout: "mock file content".to_string(),
44                stderr: "".to_string(),
45                exit_code: 0,
46            },
47        );
48
49        responses.insert(
50            "test".to_string(),
51            MockResponse {
52                stdout: "".to_string(),
53                stderr: "".to_string(),
54                exit_code: 1, // File doesn't exist
55            },
56        );
57
58        responses.insert(
59            "uname".to_string(),
60            MockResponse {
61                stdout: "Linux\n".to_string(),
62                stderr: "".to_string(),
63                exit_code: 0,
64            },
65        );
66
67        responses.insert(
68            "whoami".to_string(),
69            MockResponse {
70                stdout: "root\n".to_string(),
71                stderr: "".to_string(),
72                exit_code: 0,
73            },
74        );
75
76        responses.insert("env".to_string(), MockResponse {
77            stdout: "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin\nHOSTNAME=mock-container\nHOME=/root\n".to_string(),
78            stderr: "".to_string(),
79            exit_code: 0,
80        });
81
82        responses.insert("ls".to_string(), MockResponse {
83            stdout: "bin\nboot\ndev\netc\nhome\nlib\nlib64\nmedia\nmnt\nopt\nproc\nroot\nrun\nsbin\nsrv\nsys\ntmp\nusr\nvar\n".to_string(),
84            stderr: "".to_string(),
85            exit_code: 0,
86        });
87
88        Self { responses }
89    }
90
91    /// Add a custom mock response for a command
92    pub fn add_response(mut self, command: &str, response: MockResponse) -> Self {
93        self.responses.insert(command.to_string(), response);
94        self
95    }
96
97    /// Ultra-fast command execution (microseconds instead of seconds)
98    fn execute_mock_cmd(&self, cmd: &Cmd) -> Result<RunResult> {
99        let cmd_key = cmd.bin.clone();
100
101        // Instant response - no Docker overhead
102        if let Some(response) = self.responses.get(&cmd_key) {
103            Ok(RunResult {
104                exit_code: response.exit_code,
105                stdout: response.stdout.clone(),
106                stderr: response.stderr.clone(),
107                duration_ms: 1, // 1ms for realistic timing
108                steps: Vec::new(),
109                redacted_env: Vec::new(),
110                backend: "mock".to_string(),
111                concurrent: false,
112                step_order: Vec::new(),
113            })
114        } else {
115            // Default success for unknown commands - simulates container behavior
116            Ok(RunResult {
117                exit_code: 0,
118                stdout: format!("mock output for: {}", cmd.bin),
119                stderr: "".to_string(),
120                duration_ms: 1,
121                steps: Vec::new(),
122                redacted_env: Vec::new(),
123                backend: "mock".to_string(),
124                concurrent: false,
125                step_order: Vec::new(),
126            })
127        }
128    }
129}
130
131impl Backend for MockBackend {
132    /// Run a command in the mock backend
133    fn run_cmd(&self, cmd: Cmd) -> Result<RunResult> {
134        self.execute_mock_cmd(&cmd)
135    }
136
137    /// Get the name of the backend
138    fn name(&self) -> &str {
139        "mock"
140    }
141
142    /// Mock backend is always available for testing
143    fn is_available(&self) -> bool {
144        true
145    }
146
147    /// Mock backend supports hermetic execution (simulated)
148    fn supports_hermetic(&self) -> bool {
149        true
150    }
151
152    /// Mock backend supports deterministic execution (by design)
153    fn supports_deterministic(&self) -> bool {
154        true
155    }
156}
157
158impl Default for MockBackend {
159    fn default() -> Self {
160        Self::new()
161    }
162}