windjammer_runtime/
mock_interface.rs1use std::any::Any;
6use std::collections::HashMap;
7use std::sync::{Arc, Mutex};
8
9#[derive(Debug, Clone)]
11pub struct MethodCall {
12 pub method_name: String,
13 pub args: Vec<String>,
14}
15
16impl MethodCall {
17 pub fn new(method_name: String, args: Vec<String>) -> Self {
18 Self { method_name, args }
19 }
20}
21
22#[derive(Debug, Clone)]
24pub struct Expectation {
25 pub method_name: String,
26 pub expected_args: Option<Vec<String>>,
27 pub return_value: Option<String>,
28 pub call_count: Option<usize>,
29}
30
31impl Expectation {
32 pub fn new(method_name: String) -> Self {
33 Self {
34 method_name,
35 expected_args: None,
36 return_value: None,
37 call_count: None,
38 }
39 }
40
41 pub fn with_args(mut self, args: Vec<String>) -> Self {
42 self.expected_args = Some(args);
43 self
44 }
45
46 pub fn returns(mut self, value: String) -> Self {
47 self.return_value = Some(value);
48 self
49 }
50
51 pub fn times(mut self, count: usize) -> Self {
52 self.call_count = Some(count);
53 self
54 }
55}
56
57pub struct MockObject {
59 calls: Arc<Mutex<Vec<MethodCall>>>,
60 expectations: Arc<Mutex<Vec<Expectation>>>,
61 #[allow(clippy::type_complexity)]
62 return_values: Arc<Mutex<HashMap<String, Vec<Box<dyn Any + Send>>>>>,
63}
64
65impl MockObject {
66 pub fn new() -> Self {
67 Self {
68 calls: Arc::new(Mutex::new(Vec::new())),
69 expectations: Arc::new(Mutex::new(Vec::new())),
70 return_values: Arc::new(Mutex::new(HashMap::new())),
71 }
72 }
73
74 pub fn record_call(&self, method_name: &str, args: Vec<String>) {
76 self.calls
77 .lock()
78 .unwrap()
79 .push(MethodCall::new(method_name.to_string(), args));
80 }
81
82 pub fn expect(&self, expectation: Expectation) {
84 self.expectations.lock().unwrap().push(expectation);
85 }
86
87 pub fn set_return<T: 'static + Send>(&self, method_name: &str, value: T) {
89 self.return_values
90 .lock()
91 .unwrap()
92 .entry(method_name.to_string())
93 .or_default()
94 .push(Box::new(value));
95 }
96
97 pub fn get_return<T: 'static>(&self, method_name: &str) -> Option<T> {
99 let mut values = self.return_values.lock().unwrap();
100 let method_values = values.get_mut(method_name)?;
101 if method_values.is_empty() {
102 return None;
103 }
104 let boxed = method_values.remove(0);
105 boxed.downcast::<T>().ok().map(|b| *b)
106 }
107
108 pub fn verify(&self) {
110 let calls = self.calls.lock().unwrap();
111 let expectations = self.expectations.lock().unwrap();
112
113 for expectation in expectations.iter() {
114 let matching_calls: Vec<_> = calls
115 .iter()
116 .filter(|call| call.method_name == expectation.method_name)
117 .collect();
118
119 if let Some(expected_count) = expectation.call_count {
120 if matching_calls.len() != expected_count {
121 panic!(
122 "Expected {} calls to '{}', but got {}",
123 expected_count,
124 expectation.method_name,
125 matching_calls.len()
126 );
127 }
128 }
129
130 if let Some(expected_args) = &expectation.expected_args {
131 let found = matching_calls
132 .iter()
133 .any(|call| &call.args == expected_args);
134 if !found {
135 panic!(
136 "Expected call to '{}' with args {:?}, but not found",
137 expectation.method_name, expected_args
138 );
139 }
140 }
141 }
142 }
143
144 pub fn call_count(&self, method_name: &str) -> usize {
146 self.calls
147 .lock()
148 .unwrap()
149 .iter()
150 .filter(|call| call.method_name == method_name)
151 .count()
152 }
153
154 pub fn was_called(&self, method_name: &str) -> bool {
156 self.call_count(method_name) > 0
157 }
158
159 pub fn get_calls(&self, method_name: &str) -> Vec<MethodCall> {
161 self.calls
162 .lock()
163 .unwrap()
164 .iter()
165 .filter(|call| call.method_name == method_name)
166 .cloned()
167 .collect()
168 }
169
170 pub fn reset(&self) {
172 self.calls.lock().unwrap().clear();
173 self.expectations.lock().unwrap().clear();
174 self.return_values.lock().unwrap().clear();
175 }
176}
177
178impl Default for MockObject {
179 fn default() -> Self {
180 Self::new()
181 }
182}
183
184impl Clone for MockObject {
185 fn clone(&self) -> Self {
186 Self {
187 calls: Arc::clone(&self.calls),
188 expectations: Arc::clone(&self.expectations),
189 return_values: Arc::clone(&self.return_values),
190 }
191 }
192}
193
194#[cfg(test)]
224mod tests {
225 use super::*;
226
227 #[test]
228 fn test_mock_object_creation() {
229 let mock = MockObject::new();
230 assert_eq!(mock.call_count("test_method"), 0);
231 }
232
233 #[test]
234 fn test_record_call() {
235 let mock = MockObject::new();
236 mock.record_call("query", vec!["SELECT *".to_string()]);
237 assert_eq!(mock.call_count("query"), 1);
238 assert!(mock.was_called("query"));
239 }
240
241 #[test]
242 fn test_multiple_calls() {
243 let mock = MockObject::new();
244 mock.record_call("query", vec!["SELECT *".to_string()]);
245 mock.record_call("query", vec!["INSERT".to_string()]);
246 mock.record_call("insert", vec!["data".to_string()]);
247
248 assert_eq!(mock.call_count("query"), 2);
249 assert_eq!(mock.call_count("insert"), 1);
250 }
251
252 #[test]
253 fn test_get_calls() {
254 let mock = MockObject::new();
255 mock.record_call("query", vec!["SELECT *".to_string()]);
256 mock.record_call("query", vec!["INSERT".to_string()]);
257
258 let calls = mock.get_calls("query");
259 assert_eq!(calls.len(), 2);
260 assert_eq!(calls[0].args[0], "SELECT *");
261 assert_eq!(calls[1].args[0], "INSERT");
262 }
263
264 #[test]
265 fn test_set_and_get_return() {
266 let mock = MockObject::new();
267 mock.set_return("query", 42);
268
269 let result: Option<i32> = mock.get_return("query");
270 assert_eq!(result, Some(42));
271 }
272
273 #[test]
274 fn test_return_values_fifo() {
275 let mock = MockObject::new();
276 mock.set_return("query", 1);
277 mock.set_return("query", 2);
278 mock.set_return("query", 3);
279
280 assert_eq!(mock.get_return::<i32>("query"), Some(1));
281 assert_eq!(mock.get_return::<i32>("query"), Some(2));
282 assert_eq!(mock.get_return::<i32>("query"), Some(3));
283 assert_eq!(mock.get_return::<i32>("query"), None);
284 }
285
286 #[test]
287 fn test_expectation_builder() {
288 let expectation = Expectation::new("query".to_string())
289 .with_args(vec!["SELECT *".to_string()])
290 .returns("result".to_string())
291 .times(2);
292
293 assert_eq!(expectation.method_name, "query");
294 assert_eq!(
295 expectation.expected_args,
296 Some(vec!["SELECT *".to_string()])
297 );
298 assert_eq!(expectation.return_value, Some("result".to_string()));
299 assert_eq!(expectation.call_count, Some(2));
300 }
301
302 #[test]
303 fn test_verify_success() {
304 let mock = MockObject::new();
305 mock.expect(Expectation::new("query".to_string()).times(2));
306
307 mock.record_call("query", vec!["SELECT *".to_string()]);
308 mock.record_call("query", vec!["INSERT".to_string()]);
309
310 mock.verify(); }
312
313 #[test]
314 #[should_panic(expected = "Expected 2 calls")]
315 fn test_verify_call_count_failure() {
316 let mock = MockObject::new();
317 mock.expect(Expectation::new("query".to_string()).times(2));
318
319 mock.record_call("query", vec!["SELECT *".to_string()]);
320
321 mock.verify(); }
323
324 #[test]
325 #[should_panic(expected = "Expected call to 'query' with args")]
326 fn test_verify_args_failure() {
327 let mock = MockObject::new();
328 mock.expect(Expectation::new("query".to_string()).with_args(vec!["SELECT *".to_string()]));
329
330 mock.record_call("query", vec!["INSERT".to_string()]);
331
332 mock.verify(); }
334
335 #[test]
336 fn test_reset() {
337 let mock = MockObject::new();
338 mock.record_call("query", vec!["SELECT *".to_string()]);
339 mock.set_return("query", 42);
340
341 assert_eq!(mock.call_count("query"), 1);
342
343 mock.reset();
344
345 assert_eq!(mock.call_count("query"), 0);
346 assert_eq!(mock.get_return::<i32>("query"), None);
347 }
348
349 #[test]
350 fn test_mock_clone() {
351 let mock1 = MockObject::new();
352 mock1.record_call("query", vec!["SELECT *".to_string()]);
353
354 let mock2 = mock1.clone();
355
356 assert_eq!(mock2.call_count("query"), 1);
358
359 mock2.record_call("query", vec!["INSERT".to_string()]);
360 assert_eq!(mock1.call_count("query"), 2); }
362}