mockforge_test/
scenario.rs1use crate::error::{Error, Result};
4use reqwest::Client;
5use serde_json::Value;
6use std::time::Duration;
7use tracing::{debug, info};
8
9pub struct ScenarioManager {
11 client: Client,
12 base_url: String,
13}
14
15impl ScenarioManager {
16 pub fn new(host: &str, port: u16) -> Self {
23 Self {
24 client: Client::builder()
25 .timeout(Duration::from_secs(10))
26 .build()
27 .expect("Failed to build HTTP client"),
28 base_url: format!("http://{}:{}", host, port),
29 }
30 }
31
32 pub async fn switch_scenario(&self, scenario_name: &str) -> Result<()> {
38 info!("Switching to scenario: {}", scenario_name);
39
40 let url = format!("{}/__mockforge/workspace/switch", self.base_url);
41
42 let response = self
43 .client
44 .post(&url)
45 .json(&serde_json::json!({
46 "workspace": scenario_name
47 }))
48 .send()
49 .await?;
50
51 if !response.status().is_success() {
52 return Err(Error::ScenarioError(format!(
53 "Failed to switch scenario: HTTP {} - {}",
54 response.status(),
55 response.text().await.unwrap_or_default()
56 )));
57 }
58
59 debug!("Successfully switched to scenario: {}", scenario_name);
60 Ok(())
61 }
62
63 pub async fn load_workspace<P: AsRef<std::path::Path>>(&self, workspace_file: P) -> Result<()> {
69 let path = workspace_file.as_ref();
70 info!("Loading workspace from: {}", path.display());
71
72 let content = tokio::fs::read_to_string(path)
73 .await
74 .map_err(|e| Error::WorkspaceError(format!("Failed to read workspace file: {}", e)))?;
75
76 let workspace: Value = if path.extension().and_then(|s| s.to_str()) == Some("yaml")
77 || path.extension().and_then(|s| s.to_str()) == Some("yml")
78 {
79 serde_yaml::from_str(&content)?
80 } else {
81 serde_json::from_str(&content)?
82 };
83
84 let url = format!("{}/__mockforge/workspace/load", self.base_url);
85
86 let response = self.client.post(&url).json(&workspace).send().await?;
87
88 if !response.status().is_success() {
89 return Err(Error::WorkspaceError(format!(
90 "Failed to load workspace: HTTP {} - {}",
91 response.status(),
92 response.text().await.unwrap_or_default()
93 )));
94 }
95
96 debug!("Successfully loaded workspace from: {}", path.display());
97 Ok(())
98 }
99
100 pub async fn update_mock(&self, endpoint: &str, config: Value) -> Result<()> {
107 info!("Updating mock for endpoint: {}", endpoint);
108
109 let url = format!("{}/__mockforge/config{}", self.base_url, endpoint);
110
111 let response = self.client.post(&url).json(&config).send().await?;
112
113 if !response.status().is_success() {
114 return Err(Error::ScenarioError(format!(
115 "Failed to update mock: HTTP {} - {}",
116 response.status(),
117 response.text().await.unwrap_or_default()
118 )));
119 }
120
121 debug!("Successfully updated mock for: {}", endpoint);
122 Ok(())
123 }
124
125 pub async fn list_fixtures(&self) -> Result<Vec<String>> {
127 debug!("Listing available fixtures");
128
129 let url = format!("{}/__mockforge/fixtures", self.base_url);
130
131 let response = self.client.get(&url).send().await?;
132
133 if !response.status().is_success() {
134 return Err(Error::ScenarioError(format!(
135 "Failed to list fixtures: HTTP {}",
136 response.status()
137 )));
138 }
139
140 let fixtures: Vec<String> = response.json().await?;
141 debug!("Found {} fixtures", fixtures.len());
142
143 Ok(fixtures)
144 }
145
146 pub async fn get_stats(&self) -> Result<Value> {
148 debug!("Fetching server statistics");
149
150 let url = format!("{}/__mockforge/stats", self.base_url);
151
152 let response = self.client.get(&url).send().await?;
153
154 if !response.status().is_success() {
155 return Err(Error::InvalidResponse(format!(
156 "Failed to get stats: HTTP {}",
157 response.status()
158 )));
159 }
160
161 let stats: Value = response.json().await?;
162 Ok(stats)
163 }
164
165 pub async fn reset(&self) -> Result<()> {
167 info!("Resetting all mocks");
168
169 let url = format!("{}/__mockforge/reset", self.base_url);
170
171 let response = self.client.post(&url).send().await?;
172
173 if !response.status().is_success() {
174 return Err(Error::ScenarioError(format!(
175 "Failed to reset mocks: HTTP {}",
176 response.status()
177 )));
178 }
179
180 debug!("Successfully reset all mocks");
181 Ok(())
182 }
183}
184
185pub struct ScenarioBuilder {
187 name: String,
188 mocks: Vec<Value>,
189}
190
191impl ScenarioBuilder {
192 pub fn new<S: Into<String>>(name: S) -> Self {
194 Self {
195 name: name.into(),
196 mocks: Vec::new(),
197 }
198 }
199
200 pub fn mock(mut self, endpoint: &str, response: Value) -> Self {
202 self.mocks.push(serde_json::json!({
203 "endpoint": endpoint,
204 "response": response
205 }));
206 self
207 }
208
209 pub fn build(self) -> Value {
211 serde_json::json!({
212 "name": self.name,
213 "mocks": self.mocks
214 })
215 }
216}
217
218#[cfg(test)]
219mod tests {
220 use super::*;
221
222 #[test]
223 fn test_scenario_builder() {
224 let scenario = ScenarioBuilder::new("test-scenario")
225 .mock(
226 "/users",
227 serde_json::json!({
228 "users": [
229 {"id": 1, "name": "Alice"},
230 {"id": 2, "name": "Bob"}
231 ]
232 }),
233 )
234 .mock(
235 "/posts",
236 serde_json::json!({
237 "posts": []
238 }),
239 )
240 .build();
241
242 assert_eq!(scenario["name"], "test-scenario");
243 assert_eq!(scenario["mocks"].as_array().unwrap().len(), 2);
244 }
245
246 #[test]
247 fn test_scenario_manager_creation() {
248 let manager = ScenarioManager::new("localhost", 3000);
249 assert_eq!(manager.base_url, "http://localhost:3000");
250 }
251}