1use std::time::Duration;
4use wae_types::{WaeError, WaeErrorKind, WaeResult as TestingResult};
5
6#[macro_export]
8macro_rules! assert_ok {
9 ($expr:expr) => {
10 match $expr {
11 Ok(v) => v,
12 Err(e) => panic!("Expected Ok, got Err: {:?}", e),
13 }
14 };
15 ($expr:expr, $msg:expr) => {
16 match $expr {
17 Ok(v) => v,
18 Err(e) => panic!("{}: Expected Ok, got Err: {:?}", $msg, e),
19 }
20 };
21}
22
23#[macro_export]
25macro_rules! assert_err {
26 ($expr:expr) => {
27 match $expr {
28 Err(e) => e,
29 Ok(v) => panic!("Expected Err, got Ok: {:?}", v),
30 }
31 };
32 ($expr:expr, $msg:expr) => {
33 match $expr {
34 Err(e) => e,
35 Ok(v) => panic!("{}: Expected Err, got Ok: {:?}", $msg, v),
36 }
37 };
38}
39
40#[macro_export]
42macro_rules! assert_some {
43 ($expr:expr) => {
44 match $expr {
45 Some(v) => v,
46 None => panic!("Expected Some, got None"),
47 }
48 };
49 ($expr:expr, $msg:expr) => {
50 match $expr {
51 Some(v) => v,
52 None => panic!("{}: Expected Some, got None", $msg),
53 }
54 };
55}
56
57#[macro_export]
59macro_rules! assert_none {
60 ($expr:expr) => {
61 match $expr {
62 None => (),
63 Some(v) => panic!("Expected None, got Some: {:?}", v),
64 }
65 };
66 ($expr:expr, $msg:expr) => {
67 match $expr {
68 None => (),
69 Some(v) => panic!("{}: Expected None, got Some: {:?}", $msg, v),
70 }
71 };
72}
73
74#[macro_export]
76macro_rules! assert_contains {
77 ($container:expr, $item:expr) => {
78 if !$container.contains(&$item) {
79 panic!("Expected container to contain item: {:?}", $item);
80 }
81 };
82 ($container:expr, $item:expr, $msg:expr) => {
83 if !$container.contains(&$item) {
84 panic!("{}: Expected container to contain item: {:?}", $msg, $item);
85 }
86 };
87}
88
89#[macro_export]
91macro_rules! assert_not_contains {
92 ($container:expr, $item:expr) => {
93 if $container.contains(&$item) {
94 panic!("Expected container to NOT contain item: {:?}", $item);
95 }
96 };
97 ($container:expr, $item:expr, $msg:expr) => {
98 if $container.contains(&$item) {
99 panic!("{}: Expected container to NOT contain item: {:?}", $msg, $item);
100 }
101 };
102}
103
104#[macro_export]
106macro_rules! assert_approx_eq {
107 ($left:expr, $right:expr, $epsilon:expr) => {
108 let left = $left;
109 let right = $right;
110 let diff = if left > right { left - right } else { right - left };
111 if diff > $epsilon {
112 panic!("Values are not approximately equal: left={}, right={}, diff={}", left, right, diff);
113 }
114 };
115}
116
117pub struct AsyncAssert;
119
120impl AsyncAssert {
121 pub async fn eventually<F, Fut>(condition: F, timeout_duration: Duration, check_interval: Duration) -> TestingResult<()>
123 where
124 F: Fn() -> Fut,
125 Fut: std::future::Future<Output = bool>,
126 {
127 let start = std::time::Instant::now();
128
129 while start.elapsed() < timeout_duration {
130 if condition().await {
131 return Ok(());
132 }
133 tokio::time::sleep(check_interval).await;
134 }
135
136 Err(WaeError::new(WaeErrorKind::OperationTimeout {
137 operation: "eventually".to_string(),
138 timeout_ms: timeout_duration.as_millis() as u64,
139 }))
140 }
141
142 pub async fn eventually_with_value<F, Fut, T>(
144 condition: F,
145 timeout_duration: Duration,
146 check_interval: Duration,
147 ) -> TestingResult<T>
148 where
149 F: Fn() -> Fut,
150 Fut: std::future::Future<Output = Option<T>>,
151 {
152 let start = std::time::Instant::now();
153
154 while start.elapsed() < timeout_duration {
155 if let Some(value) = condition().await {
156 return Ok(value);
157 }
158 tokio::time::sleep(check_interval).await;
159 }
160
161 Err(WaeError::new(WaeErrorKind::OperationTimeout {
162 operation: "eventually_with_value".to_string(),
163 timeout_ms: timeout_duration.as_millis() as u64,
164 }))
165 }
166}
167
168pub async fn assert_eventually<F, Fut>(condition: F, timeout_duration: Duration, check_interval: Duration) -> TestingResult<()>
170where
171 F: Fn() -> Fut,
172 Fut: std::future::Future<Output = bool>,
173{
174 AsyncAssert::eventually(condition, timeout_duration, check_interval).await
175}
176
177pub fn assert_matches_regex(text: &str, pattern: &str) -> TestingResult<()> {
179 let re = regex::Regex::new(pattern)
180 .map_err(|e| WaeError::new(WaeErrorKind::AssertionFailed { message: format!("Invalid regex: {}", e) }))?;
181
182 if !re.is_match(text) {
183 return Err(WaeError::new(WaeErrorKind::AssertionFailed {
184 message: format!("Text '{}' does not match pattern '{}'", text, pattern),
185 }));
186 }
187
188 Ok(())
189}
190
191pub fn assert_json_contains(json: &serde_json::Value, path: &str) -> TestingResult<()> {
193 let parts: Vec<&str> = path.split('.').collect();
194 let mut current = json;
195
196 for part in parts {
197 if let serde_json::Value::Object(map) = current {
198 current = map.get(part).ok_or_else(|| {
199 WaeError::new(WaeErrorKind::AssertionFailed { message: format!("JSON path '{}' not found", path) })
200 })?;
201 }
202 else {
203 return Err(WaeError::new(WaeErrorKind::AssertionFailed { message: format!("JSON path '{}' not found", path) }));
204 }
205 }
206
207 Ok(())
208}