Skip to main content

ralph_core/testing/
scenario.rs

1//! Test scenario definitions and execution.
2
3use super::mock_backend::MockBackend;
4use crate::config::RalphConfig;
5use crate::event_reader::Event;
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).with_iterations(3);
88
89        assert_eq!(scenario.name, "test");
90        assert_eq!(scenario.expected_iterations, 3);
91    }
92
93    #[test]
94    fn test_scenario_runner_executes() {
95        let backend = MockBackend::new(vec!["ok".into()]);
96        let runner = ScenarioRunner::new(backend);
97
98        let config = RalphConfig::default();
99        let scenario = Scenario::new("test", config).with_iterations(1);
100
101        let trace = runner.run(&scenario);
102        assert_eq!(trace.iterations, 1);
103    }
104
105    #[test]
106    fn test_mock_backend_simulates_hat_execution() {
107        // Demo: Simulate a hat execution with scripted response
108        let responses = vec![
109            r#"Building feature...
110<event topic="build.done">
111tests: pass
112lint: pass
113typecheck: pass
114audit: pass
115coverage: pass
116</event>"#
117                .to_string(),
118        ];
119
120        let backend = MockBackend::new(responses);
121
122        // Execute once
123        let output = backend.execute("You are the builder hat. Build feature X.");
124
125        // Verify response
126        assert!(output.contains("build.done"));
127        assert!(output.contains("tests: pass"));
128
129        // Verify execution was tracked
130        assert_eq!(backend.execution_count(), 1);
131        let executions = backend.executions();
132        assert_eq!(
133            executions[0].prompt,
134            "You are the builder hat. Build feature X."
135        );
136    }
137}