1use mockforge_plugin_core::*;
7use std::collections::HashMap;
8
9pub struct TestHarness {
29 contexts: HashMap<String, PluginContext>,
31}
32
33impl TestHarness {
34 pub fn new() -> Self {
36 Self {
37 contexts: HashMap::new(),
38 }
39 }
40
41 pub fn create_context(&mut self, plugin_id: &str, request_id: &str) -> PluginContext {
43 let mut context = PluginContext::new(PluginId::new(plugin_id), PluginVersion::new(0, 1, 0));
44
45 context.request_id = request_id.to_string();
47
48 self.contexts.insert(plugin_id.to_string(), context.clone());
49 context
50 }
51
52 pub fn create_context_with_custom(
54 &mut self,
55 plugin_id: &str,
56 request_id: &str,
57 custom_data: HashMap<String, serde_json::Value>,
58 ) -> PluginContext {
59 let mut context = self.create_context(plugin_id, request_id);
60 for (key, value) in custom_data {
61 context = context.with_custom(key, value);
62 }
63 context
64 }
65
66 pub fn get_context(&self, plugin_id: &str) -> Option<&PluginContext> {
68 self.contexts.get(plugin_id)
69 }
70}
71
72impl Default for TestHarness {
73 fn default() -> Self {
74 Self::new()
75 }
76}
77
78pub struct MockAuthRequest;
80
81impl MockAuthRequest {
82 pub fn with_basic_auth(username: &str, password: &str) -> AuthRequest {
84 use axum::http::{HeaderMap, HeaderValue, Method, Uri};
85 use base64::Engine;
86
87 let credentials = format!("{}:{}", username, password);
88 let encoded = base64::engine::general_purpose::STANDARD.encode(credentials.as_bytes());
89 let auth_value = format!("Basic {}", encoded);
90
91 let mut headers = HeaderMap::new();
92 headers.insert("authorization", HeaderValue::from_str(&auth_value).unwrap());
93
94 AuthRequest::from_axum(Method::GET, Uri::from_static("/"), headers, None)
95 }
96
97 pub fn with_bearer_token(token: &str) -> AuthRequest {
99 use axum::http::{HeaderMap, HeaderValue, Method, Uri};
100
101 let auth_value = format!("Bearer {}", token);
102
103 let mut headers = HeaderMap::new();
104 headers.insert("authorization", HeaderValue::from_str(&auth_value).unwrap());
105
106 AuthRequest::from_axum(Method::GET, Uri::from_static("/"), headers, None)
107 }
108
109 pub fn with_headers(headers_map: HashMap<String, String>) -> AuthRequest {
111 use axum::http::{HeaderMap, HeaderName, HeaderValue, Method, Uri};
112
113 let mut headers = HeaderMap::new();
114 for (key, value) in headers_map {
115 if let (Ok(header_name), Ok(header_value)) =
116 (key.parse::<HeaderName>(), HeaderValue::from_str(&value))
117 {
118 headers.insert(header_name, header_value);
119 }
120 }
121
122 AuthRequest::from_axum(Method::GET, Uri::from_static("/"), headers, None)
123 }
124}
125
126#[macro_export]
128macro_rules! assert_plugin_ok {
129 ($result:expr) => {
130 match $result {
131 Ok(_) => (),
132 Err(e) => panic!("Plugin returned error: {:?}", e),
133 }
134 };
135 ($result:expr, $msg:expr) => {
136 match $result {
137 Ok(_) => (),
138 Err(e) => panic!("{}: {:?}", $msg, e),
139 }
140 };
141}
142
143#[macro_export]
145macro_rules! assert_plugin_err {
146 ($result:expr) => {
147 match $result {
148 Ok(_) => panic!("Expected plugin error, got success"),
149 Err(_) => (),
150 }
151 };
152 ($result:expr, $msg:expr) => {
153 match $result {
154 Ok(_) => panic!("{}: Expected error, got success", $msg),
155 Err(_) => (),
156 }
157 };
158}
159
160pub fn test_context() -> PluginContext {
162 let mut context = PluginContext::new(PluginId::new("test-plugin"), PluginVersion::new(0, 1, 0));
163 context.request_id = "test-request".to_string();
164 context
165}
166
167pub fn test_context_with_id(plugin_id: &str) -> PluginContext {
169 let mut context = PluginContext::new(PluginId::new(plugin_id), PluginVersion::new(0, 1, 0));
170 context.request_id = "test-request".to_string();
171 context
172}
173
174#[cfg(test)]
175mod tests {
176 use super::*;
177
178 #[test]
179 fn test_harness_creation() {
180 let harness = TestHarness::new();
181 assert_eq!(harness.contexts.len(), 0);
182 }
183
184 #[test]
185 fn test_context_creation() {
186 let mut harness = TestHarness::new();
187 let context = harness.create_context("test", "req-1");
188 assert_eq!(context.plugin_id.as_str(), "test");
189 assert_eq!(context.request_id, "req-1");
190 }
191
192 #[test]
193 fn test_mock_auth_request() {
194 let request = MockAuthRequest::with_basic_auth("user", "pass");
195 let auth_header = request.authorization_header();
196 assert!(auth_header.is_some());
197 assert!(auth_header.unwrap().starts_with("Basic "));
198
199 let (username, password) = request.basic_credentials().unwrap();
200 assert_eq!(username, "user");
201 assert_eq!(password, "pass");
202 }
203
204 #[test]
205 fn test_harness_default() {
206 let harness = TestHarness::default();
207 assert_eq!(harness.contexts.len(), 0);
208 }
209
210 #[test]
211 fn test_harness_multiple_contexts() {
212 let mut harness = TestHarness::new();
213 harness.create_context("plugin-1", "req-1");
214 harness.create_context("plugin-2", "req-2");
215 harness.create_context("plugin-3", "req-3");
216 assert_eq!(harness.contexts.len(), 3);
217 }
218
219 #[test]
220 fn test_harness_get_context() {
221 let mut harness = TestHarness::new();
222 harness.create_context("my-plugin", "req-123");
223
224 let retrieved = harness.get_context("my-plugin");
225 assert!(retrieved.is_some());
226 let ctx = retrieved.unwrap();
227 assert_eq!(ctx.plugin_id.as_str(), "my-plugin");
228 assert_eq!(ctx.request_id, "req-123");
229 }
230
231 #[test]
232 fn test_harness_get_context_not_found() {
233 let harness = TestHarness::new();
234 assert!(harness.get_context("nonexistent").is_none());
235 }
236
237 #[test]
238 fn test_harness_context_overwrite() {
239 let mut harness = TestHarness::new();
240 harness.create_context("plugin", "req-1");
241 harness.create_context("plugin", "req-2");
242
243 assert_eq!(harness.contexts.len(), 1);
245 let ctx = harness.get_context("plugin").unwrap();
246 assert_eq!(ctx.request_id, "req-2");
247 }
248
249 #[test]
250 fn test_harness_create_context_with_custom() {
251 let mut harness = TestHarness::new();
252 let mut custom_data = HashMap::new();
253 custom_data.insert("key1".to_string(), serde_json::json!("value1"));
254 custom_data.insert("key2".to_string(), serde_json::json!(42));
255
256 let context = harness.create_context_with_custom("plugin", "req-1", custom_data);
257 assert_eq!(context.plugin_id.as_str(), "plugin");
258 assert_eq!(context.request_id, "req-1");
259 }
260
261 #[test]
262 fn test_harness_create_context_with_empty_custom() {
263 let mut harness = TestHarness::new();
264 let context = harness.create_context_with_custom("plugin", "req-1", HashMap::new());
265 assert_eq!(context.plugin_id.as_str(), "plugin");
266 }
267
268 #[test]
269 fn test_mock_auth_request_bearer_token() {
270 let request = MockAuthRequest::with_bearer_token("my-secret-token");
271 let auth_header = request.authorization_header();
272 assert!(auth_header.is_some());
273 let header = auth_header.unwrap();
274 assert!(header.starts_with("Bearer "));
275 assert!(header.contains("my-secret-token"));
276 }
277
278 #[test]
279 fn test_mock_auth_request_with_headers() {
280 let mut headers = HashMap::new();
281 headers.insert("x-custom-header".to_string(), "custom-value".to_string());
282 headers.insert("content-type".to_string(), "application/json".to_string());
283
284 let request = MockAuthRequest::with_headers(headers);
285 assert_eq!(request.method, "GET");
287 }
288
289 #[test]
290 fn test_mock_auth_request_with_empty_headers() {
291 let request = MockAuthRequest::with_headers(HashMap::new());
292 assert_eq!(request.method, "GET");
293 assert!(request.authorization_header().is_none());
294 }
295
296 #[test]
297 fn test_mock_auth_request_basic_auth_empty_credentials() {
298 let request = MockAuthRequest::with_basic_auth("", "");
299 let (username, password) = request.basic_credentials().unwrap();
300 assert_eq!(username, "");
301 assert_eq!(password, "");
302 }
303
304 #[test]
305 fn test_mock_auth_request_bearer_empty_token() {
306 let request = MockAuthRequest::with_bearer_token("");
307 let auth_header = request.authorization_header();
308 assert!(auth_header.is_some());
309 assert_eq!(auth_header.unwrap(), "Bearer ");
310 }
311
312 #[test]
313 fn test_test_context_function() {
314 let context = test_context();
315 assert_eq!(context.plugin_id.as_str(), "test-plugin");
316 assert_eq!(context.request_id, "test-request");
317 assert_eq!(context.version.major, 0);
318 assert_eq!(context.version.minor, 1);
319 assert_eq!(context.version.patch, 0);
320 }
321
322 #[test]
323 fn test_test_context_with_id_function() {
324 let context = test_context_with_id("custom-plugin");
325 assert_eq!(context.plugin_id.as_str(), "custom-plugin");
326 assert_eq!(context.request_id, "test-request");
327 }
328
329 #[test]
330 fn test_test_context_with_empty_id() {
331 let context = test_context_with_id("");
332 assert_eq!(context.plugin_id.as_str(), "");
333 assert_eq!(context.request_id, "test-request");
334 }
335
336 #[test]
337 fn test_assert_plugin_ok_macro() {
338 let result: std::result::Result<i32, &str> = Ok(42);
339 assert_plugin_ok!(result);
340 }
341
342 #[test]
343 #[should_panic(expected = "Plugin returned error")]
344 fn test_assert_plugin_ok_macro_fails_on_err() {
345 let result: std::result::Result<i32, &str> = Err("error");
346 assert_plugin_ok!(result);
347 }
348
349 #[test]
350 #[should_panic(expected = "Custom message")]
351 fn test_assert_plugin_ok_macro_with_message() {
352 let result: std::result::Result<i32, &str> = Err("error");
353 assert_plugin_ok!(result, "Custom message");
354 }
355
356 #[test]
357 fn test_assert_plugin_err_macro() {
358 let result: std::result::Result<i32, &str> = Err("error");
359 assert_plugin_err!(result);
360 }
361
362 #[test]
363 #[should_panic(expected = "Expected plugin error, got success")]
364 fn test_assert_plugin_err_macro_fails_on_ok() {
365 let result: std::result::Result<i32, &str> = Ok(42);
366 assert_plugin_err!(result);
367 }
368
369 #[test]
370 #[should_panic(expected = "Custom message")]
371 fn test_assert_plugin_err_macro_with_message() {
372 let result: std::result::Result<i32, &str> = Ok(42);
373 assert_plugin_err!(result, "Custom message");
374 }
375
376 #[test]
377 fn test_mock_auth_request_basic_auth_special_chars() {
378 let request = MockAuthRequest::with_basic_auth("user@domain.com", "p@ss:w0rd!");
379 let (username, password) = request.basic_credentials().unwrap();
380 assert_eq!(username, "user@domain.com");
381 assert_eq!(password, "p@ss:w0rd!");
382 }
383
384 #[test]
385 fn test_context_version_is_0_1_0() {
386 let mut harness = TestHarness::new();
387 let context = harness.create_context("plugin", "req");
388 assert_eq!(context.version.major, 0);
389 assert_eq!(context.version.minor, 1);
390 assert_eq!(context.version.patch, 0);
391 }
392}