Skip to main content

ralph_core/testing/
mock_backend.rs

1//! Mock CLI backend for deterministic testing.
2
3use std::sync::{Arc, Mutex};
4
5/// Mock backend that returns pre-scripted responses.
6#[derive(Debug, Clone)]
7pub struct MockBackend {
8    responses: Arc<Mutex<MockState>>,
9}
10
11#[derive(Debug)]
12struct MockState {
13    responses: Vec<String>,
14    current: usize,
15    executions: Vec<ExecutionRecord>,
16}
17
18/// Record of a mock execution.
19#[derive(Debug, Clone)]
20pub struct ExecutionRecord {
21    pub prompt: String,
22    pub response: String,
23}
24
25impl MockBackend {
26    /// Creates a new mock backend with scripted responses.
27    pub fn new(responses: Vec<String>) -> Self {
28        Self {
29            responses: Arc::new(Mutex::new(MockState {
30                responses,
31                current: 0,
32                executions: Vec::new(),
33            })),
34        }
35    }
36
37    /// Executes a prompt, returning the next scripted response.
38    pub fn execute(&self, prompt: &str) -> String {
39        let mut state = self.responses.lock().unwrap();
40        let response = state
41            .responses
42            .get(state.current)
43            .cloned()
44            .unwrap_or_else(String::new);
45
46        state.executions.push(ExecutionRecord {
47            prompt: prompt.to_string(),
48            response: response.clone(),
49        });
50
51        state.current += 1;
52        response
53    }
54
55    /// Returns the number of times execute was called.
56    pub fn execution_count(&self) -> usize {
57        self.responses.lock().unwrap().executions.len()
58    }
59
60    /// Returns all execution records.
61    pub fn executions(&self) -> Vec<ExecutionRecord> {
62        self.responses.lock().unwrap().executions.clone()
63    }
64}
65
66#[cfg(test)]
67mod tests {
68    use super::*;
69
70    #[test]
71    fn test_mock_backend_returns_scripted_responses() {
72        let backend = MockBackend::new(vec!["response 1".into(), "response 2".into()]);
73
74        assert_eq!(backend.execute("prompt 1"), "response 1");
75        assert_eq!(backend.execute("prompt 2"), "response 2");
76        assert_eq!(backend.execution_count(), 2);
77    }
78
79    #[test]
80    fn test_mock_backend_tracks_executions() {
81        let backend = MockBackend::new(vec!["ok".into()]);
82        backend.execute("test prompt");
83
84        let executions = backend.executions();
85        assert_eq!(executions.len(), 1);
86        assert_eq!(executions[0].prompt, "test prompt");
87        assert_eq!(executions[0].response, "ok");
88    }
89}