1#[macro_export]
15macro_rules! assert_ok {
16 ($expr:expr) => {
17 match &$expr {
18 Ok(_) => (),
19 Err(e) => panic!("assertion failed: expected Ok, got Err({:?})", e),
20 }
21 };
22 ($expr:expr, $($arg:tt)+) => {
23 match &$expr {
24 Ok(_) => (),
25 Err(e) => panic!("assertion failed: {}: expected Ok, got Err({:?})", format_args!($($arg)+), e),
26 }
27 };
28}
29
30#[macro_export]
39macro_rules! assert_err {
40 ($expr:expr) => {
41 match &$expr {
42 Err(_) => (),
43 Ok(v) => panic!("assertion failed: expected Err, got Ok({:?})", v),
44 }
45 };
46 ($expr:expr, $($arg:tt)+) => {
47 match &$expr {
48 Err(_) => (),
49 Ok(v) => panic!("assertion failed: {}: expected Err, got Ok({:?})", format_args!($($arg)+), v),
50 }
51 };
52}
53
54#[macro_export]
63macro_rules! assert_err_variant {
64 ($expr:expr, $variant:pat) => {
65 match &$expr {
66 Err($variant) => (),
67 Err(e) => panic!(
68 "assertion failed: expected {}, got {:?}",
69 stringify!($variant),
70 e
71 ),
72 Ok(v) => panic!(
73 "assertion failed: expected Err({}), got Ok({:?})",
74 stringify!($variant),
75 v
76 ),
77 }
78 };
79}
80
81#[macro_export]
99macro_rules! assert_err_matches {
100 ($expr:expr, $variant:pat) => {
101 match &$expr {
102 Err($variant) => (),
103 Err(e) => panic!(
104 "assertion failed: expected {}, got {:?}",
105 stringify!($variant),
106 e
107 ),
108 Ok(v) => panic!(
109 "assertion failed: expected Err({}), got Ok({:?})",
110 stringify!($variant),
111 v
112 ),
113 }
114 };
115 ($expr:expr, $variant:pat if $guard:expr) => {
116 match &$expr {
117 Err($variant) if $guard => (),
118 Err($variant) => panic!(
119 "assertion failed: pattern {} matched but guard failed for {:?}",
120 stringify!($variant),
121 $expr
122 ),
123 Err(e) => panic!(
124 "assertion failed: expected {}, got {:?}",
125 stringify!($variant),
126 e
127 ),
128 Ok(v) => panic!(
129 "assertion failed: expected Err({}), got Ok({:?})",
130 stringify!($variant),
131 v
132 ),
133 }
134 };
135}
136
137#[macro_export]
146macro_rules! assert_job_dispatched {
147 ($ctx:expr, $job_type:expr) => {
148 $ctx.job_dispatch().assert_dispatched($job_type);
149 };
150 ($ctx:expr, $job_type:expr, $predicate:expr) => {
151 $ctx.job_dispatch()
152 .assert_dispatched_with($job_type, $predicate);
153 };
154}
155
156#[macro_export]
164macro_rules! assert_job_not_dispatched {
165 ($ctx:expr, $job_type:expr) => {
166 $ctx.job_dispatch().assert_not_dispatched($job_type);
167 };
168}
169
170#[macro_export]
179macro_rules! assert_workflow_started {
180 ($ctx:expr, $workflow_name:expr) => {
181 $ctx.workflow_dispatch().assert_started($workflow_name);
182 };
183 ($ctx:expr, $workflow_name:expr, $predicate:expr) => {
184 $ctx.workflow_dispatch()
185 .assert_started_with($workflow_name, $predicate);
186 };
187}
188
189#[macro_export]
197macro_rules! assert_workflow_not_started {
198 ($ctx:expr, $workflow_name:expr) => {
199 $ctx.workflow_dispatch().assert_not_started($workflow_name);
200 };
201}
202
203#[macro_export]
211macro_rules! assert_http_called {
212 ($ctx:expr, $pattern:expr) => {
213 $ctx.http().assert_called($pattern);
214 };
215}
216
217#[macro_export]
225macro_rules! assert_http_not_called {
226 ($ctx:expr, $pattern:expr) => {
227 $ctx.http().assert_not_called($pattern);
228 };
229}
230
231pub fn assert_json_matches(actual: &serde_json::Value, pattern: &serde_json::Value) -> bool {
244 match (actual, pattern) {
245 (serde_json::Value::Object(a), serde_json::Value::Object(p)) => {
246 for (key, expected_value) in p {
247 match a.get(key) {
248 Some(actual_value) => {
249 if !assert_json_matches(actual_value, expected_value) {
250 return false;
251 }
252 }
253 None => return false,
254 }
255 }
256 true
257 }
258 (serde_json::Value::Array(a), serde_json::Value::Array(p)) => {
259 if a.len() != p.len() {
260 return false;
261 }
262 a.iter()
263 .zip(p.iter())
264 .all(|(a, p)| assert_json_matches(a, p))
265 }
266 (a, p) => a == p,
267 }
268}
269
270pub fn assert_contains<T, F>(items: &[T], predicate: F) -> bool
272where
273 F: Fn(&T) -> bool,
274{
275 items.iter().any(predicate)
276}
277
278#[cfg(test)]
279mod tests {
280 use super::{assert_contains, assert_json_matches};
281 use crate::error::ForgeError;
282
283 #[test]
284 fn test_assert_ok_macro() {
285 let result: Result<i32, String> = Ok(42);
286 assert_ok!(result);
287 }
288
289 #[test]
290 #[should_panic(expected = "expected Ok")]
291 fn test_assert_ok_macro_fails() {
292 let result: Result<i32, String> = Err("error".to_string());
293 assert_ok!(result);
294 }
295
296 #[test]
297 fn test_assert_err_macro() {
298 let result: Result<i32, String> = Err("error".to_string());
299 assert_err!(result);
300 }
301
302 #[test]
303 #[should_panic(expected = "expected Err")]
304 fn test_assert_err_macro_fails() {
305 let result: Result<i32, String> = Ok(42);
306 assert_err!(result);
307 }
308
309 #[test]
310 fn test_assert_err_variant() {
311 let result: Result<(), ForgeError> = Err(ForgeError::NotFound("user".into()));
312 assert_err_variant!(result, ForgeError::NotFound(_));
313 }
314
315 #[test]
316 #[should_panic(expected = "expected ForgeError::Unauthorized(_)")]
317 fn test_assert_err_variant_wrong_variant() {
318 let result: Result<(), ForgeError> = Err(ForgeError::NotFound("user".into()));
319 assert_err_variant!(result, ForgeError::Unauthorized(_));
320 }
321
322 #[test]
323 fn test_assert_err_matches_no_guard() {
324 let result: Result<(), ForgeError> =
325 Err(ForgeError::Validation("email is required".into()));
326 assert_err_matches!(result, ForgeError::Validation(_));
327 }
328
329 #[test]
330 #[allow(unused_variables)]
331 fn test_assert_err_matches_with_guard() {
332 let result: Result<(), ForgeError> =
333 Err(ForgeError::Validation("email is required".into()));
334 assert_err_matches!(result, ForgeError::Validation(msg) if msg.contains("email"));
335 }
336
337 #[test]
338 #[should_panic(expected = "guard failed")]
339 #[allow(unused_variables)]
340 fn test_assert_err_matches_guard_fails() {
341 let result: Result<(), ForgeError> =
342 Err(ForgeError::Validation("email is required".into()));
343 assert_err_matches!(result, ForgeError::Validation(msg) if msg.contains("password"));
344 }
345
346 #[test]
347 #[should_panic(expected = "expected ForgeError::Unauthorized(_)")]
348 fn test_assert_err_matches_wrong_variant() {
349 let result: Result<(), ForgeError> = Err(ForgeError::NotFound("user".into()));
350 assert_err_matches!(result, ForgeError::Unauthorized(_));
351 }
352
353 #[test]
354 fn test_assert_json_matches() {
355 let actual = serde_json::json!({
356 "id": 123,
357 "name": "Test",
358 "nested": {
359 "foo": "bar"
360 }
361 });
362
363 assert!(assert_json_matches(
364 &actual,
365 &serde_json::json!({"id": 123})
366 ));
367 assert!(assert_json_matches(
368 &actual,
369 &serde_json::json!({"name": "Test"})
370 ));
371 assert!(assert_json_matches(
372 &actual,
373 &serde_json::json!({"nested": {"foo": "bar"}})
374 ));
375
376 assert!(!assert_json_matches(
377 &actual,
378 &serde_json::json!({"id": 456})
379 ));
380 assert!(!assert_json_matches(
381 &actual,
382 &serde_json::json!({"missing": true})
383 ));
384 }
385
386 #[test]
387 fn test_assert_json_matches_arrays() {
388 let actual = serde_json::json!([1, 2, 3]);
389 assert!(assert_json_matches(&actual, &serde_json::json!([1, 2, 3])));
390 assert!(!assert_json_matches(&actual, &serde_json::json!([1, 2])));
391 assert!(!assert_json_matches(&actual, &serde_json::json!([1, 2, 4])));
392 }
393
394 #[test]
395 fn test_assert_contains() {
396 let items = vec![1, 2, 3, 4, 5];
397 assert!(assert_contains(&items, |x| *x == 3));
398 assert!(!assert_contains(&items, |x| *x == 6));
399 }
400}