mockforge_plugin_sdk/
testing.rs

1//! Testing utilities for plugin development
2//!
3//! This module provides test harnesses, mock contexts, and utilities
4//! for testing plugins in isolation.
5
6use mockforge_plugin_core::*;
7use std::collections::HashMap;
8
9/// Test harness for plugin development
10///
11/// Provides a mock environment for testing plugins without
12/// loading them into the actual plugin loader.
13///
14/// # Example
15///
16/// ```rust
17/// use mockforge_plugin_sdk::testing::TestHarness;
18/// use mockforge_plugin_sdk::prelude::*;
19///
20/// #[tokio::test]
21/// async fn test_my_plugin() {
22///     let harness = TestHarness::new();
23///     let context = harness.create_context("test-plugin", "req-123");
24///
25///     // Test your plugin here
26/// }
27/// ```
28pub struct TestHarness {
29    /// Mock plugin contexts
30    contexts: HashMap<String, PluginContext>,
31}
32
33impl TestHarness {
34    /// Create a new test harness
35    pub fn new() -> Self {
36        Self {
37            contexts: HashMap::new(),
38        }
39    }
40
41    /// Create a mock plugin context
42    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        // Override request_id if provided
46        context.request_id = request_id.to_string();
47
48        self.contexts.insert(plugin_id.to_string(), context.clone());
49        context
50    }
51
52    /// Create a context with custom data
53    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    /// Get a context by plugin ID
67    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
78/// Mock authentication request helpers for testing
79pub struct MockAuthRequest;
80
81impl MockAuthRequest {
82    /// Create a mock auth request with basic auth header
83    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    /// Create a mock auth request with bearer token
98    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    /// Create a mock auth request with custom headers
110    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/// Assert that a plugin result is successful
127#[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/// Assert that a plugin result is an error
144#[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
160/// Create a test plugin context
161pub 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
167/// Create a test plugin context with custom ID
168pub 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}