Skip to main content

ralph_core/testing/
scenario.rs

1//! Test scenario definitions and execution.
2
3use crate::config::RalphConfig;
4use crate::event_reader::Event;
5use super::mock_backend::MockBackend;
6
7/// A test scenario definition.
8#[derive(Debug)]
9pub struct Scenario {
10    pub name: String,
11    pub config: RalphConfig,
12    pub expected_events: Vec<Event>,
13    pub expected_iterations: usize,
14}
15
16impl Scenario {
17    /// Creates a new scenario.
18    pub fn new(name: impl Into<String>, config: RalphConfig) -> Self {
19        Self {
20            name: name.into(),
21            config,
22            expected_events: Vec::new(),
23            expected_iterations: 0,
24        }
25    }
26
27    /// Sets expected events.
28    pub fn with_events(mut self, events: Vec<Event>) -> Self {
29        self.expected_events = events;
30        self
31    }
32
33    /// Sets expected iteration count.
34    pub fn with_iterations(mut self, count: usize) -> Self {
35        self.expected_iterations = count;
36        self
37    }
38}
39
40/// Executes test scenarios with mock backend.
41pub struct ScenarioRunner {
42    backend: MockBackend,
43}
44
45impl ScenarioRunner {
46    /// Creates a new scenario runner with mock backend.
47    pub fn new(backend: MockBackend) -> Self {
48        Self { backend }
49    }
50
51    /// Executes a scenario and returns the trace.
52    pub fn run(&self, scenario: &Scenario) -> ExecutionTrace {
53        let mut iterations = 0;
54        let events = Vec::new();
55
56        // Simulate iterations by calling the mock backend
57        while iterations < scenario.expected_iterations {
58            let prompt = format!("Iteration {}", iterations + 1);
59            let _response = self.backend.execute(&prompt);
60            iterations += 1;
61        }
62
63        ExecutionTrace {
64            iterations,
65            events,
66            final_state: iterations as u32,
67        }
68    }
69}
70
71/// Trace of a scenario execution.
72#[derive(Debug)]
73pub struct ExecutionTrace {
74    pub iterations: usize,
75    pub events: Vec<Event>,
76    pub final_state: u32,
77}
78
79#[cfg(test)]
80mod tests {
81    use super::*;
82    use crate::config::RalphConfig;
83
84    #[test]
85    fn test_scenario_creation() {
86        let config = RalphConfig::default();
87        let scenario = Scenario::new("test", config)
88            .with_iterations(3);
89
90        assert_eq!(scenario.name, "test");
91        assert_eq!(scenario.expected_iterations, 3);
92    }
93
94    #[test]
95    fn test_scenario_runner_executes() {
96        let backend = MockBackend::new(vec!["ok".into()]);
97        let runner = ScenarioRunner::new(backend);
98
99        let config = RalphConfig::default();
100        let scenario = Scenario::new("test", config).with_iterations(1);
101
102        let trace = runner.run(&scenario);
103        assert_eq!(trace.iterations, 1);
104    }
105
106    #[test]
107    fn test_mock_backend_simulates_hat_execution() {
108        // Demo: Simulate a hat execution with scripted response
109        let responses = vec![
110            r#"Building feature...
111<event topic="build.done">
112tests: pass
113lint: pass
114typecheck: pass
115</event>"#.to_string(),
116        ];
117
118        let backend = MockBackend::new(responses);
119        
120        // Execute once
121        let output = backend.execute("You are the builder hat. Build feature X.");
122        
123        // Verify response
124        assert!(output.contains("build.done"));
125        assert!(output.contains("tests: pass"));
126        
127        // Verify execution was tracked
128        assert_eq!(backend.execution_count(), 1);
129        let executions = backend.executions();
130        assert_eq!(executions[0].prompt, "You are the builder hat. Build feature X.");
131    }
132}