ralph_core/testing/
mock_backend.rs1use std::sync::{Arc, Mutex};
4
5#[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#[derive(Debug, Clone)]
20pub struct ExecutionRecord {
21 pub prompt: String,
22 pub response: String,
23}
24
25impl MockBackend {
26 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 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 pub fn execution_count(&self) -> usize {
57 self.responses.lock().unwrap().executions.len()
58 }
59
60 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}