use std::time::Duration;
use wae_types::{WaeError, WaeErrorKind, WaeResult as TestingResult};
#[macro_export]
macro_rules! assert_ok {
($expr:expr) => {
match $expr {
Ok(v) => v,
Err(e) => panic!("Expected Ok, got Err: {:?}", e),
}
};
($expr:expr, $msg:expr) => {
match $expr {
Ok(v) => v,
Err(e) => panic!("{}: Expected Ok, got Err: {:?}", $msg, e),
}
};
}
#[macro_export]
macro_rules! assert_err {
($expr:expr) => {
match $expr {
Err(e) => e,
Ok(v) => panic!("Expected Err, got Ok: {:?}", v),
}
};
($expr:expr, $msg:expr) => {
match $expr {
Err(e) => e,
Ok(v) => panic!("{}: Expected Err, got Ok: {:?}", $msg, v),
}
};
}
#[macro_export]
macro_rules! assert_some {
($expr:expr) => {
match $expr {
Some(v) => v,
None => panic!("Expected Some, got None"),
}
};
($expr:expr, $msg:expr) => {
match $expr {
Some(v) => v,
None => panic!("{}: Expected Some, got None", $msg),
}
};
}
#[macro_export]
macro_rules! assert_none {
($expr:expr) => {
match $expr {
None => (),
Some(v) => panic!("Expected None, got Some: {:?}", v),
}
};
($expr:expr, $msg:expr) => {
match $expr {
None => (),
Some(v) => panic!("{}: Expected None, got Some: {:?}", $msg, v),
}
};
}
#[macro_export]
macro_rules! assert_contains {
($container:expr, $item:expr) => {
if !$container.contains(&$item) {
panic!("Expected container to contain item: {:?}", $item);
}
};
($container:expr, $item:expr, $msg:expr) => {
if !$container.contains(&$item) {
panic!("{}: Expected container to contain item: {:?}", $msg, $item);
}
};
}
#[macro_export]
macro_rules! assert_not_contains {
($container:expr, $item:expr) => {
if $container.contains(&$item) {
panic!("Expected container to NOT contain item: {:?}", $item);
}
};
($container:expr, $item:expr, $msg:expr) => {
if $container.contains(&$item) {
panic!("{}: Expected container to NOT contain item: {:?}", $msg, $item);
}
};
}
#[macro_export]
macro_rules! assert_approx_eq {
($left:expr, $right:expr, $epsilon:expr) => {
let left = $left;
let right = $right;
let diff = if left > right { left - right } else { right - left };
if diff > $epsilon {
panic!("Values are not approximately equal: left={}, right={}, diff={}", left, right, diff);
}
};
}
pub struct AsyncAssert;
impl AsyncAssert {
pub async fn eventually<F, Fut>(condition: F, timeout_duration: Duration, check_interval: Duration) -> TestingResult<()>
where
F: Fn() -> Fut,
Fut: std::future::Future<Output = bool>,
{
let start = std::time::Instant::now();
while start.elapsed() < timeout_duration {
if condition().await {
return Ok(());
}
tokio::time::sleep(check_interval).await;
}
Err(WaeError::new(WaeErrorKind::OperationTimeout {
operation: "eventually".to_string(),
timeout_ms: timeout_duration.as_millis() as u64,
}))
}
pub async fn eventually_with_value<F, Fut, T>(
condition: F,
timeout_duration: Duration,
check_interval: Duration,
) -> TestingResult<T>
where
F: Fn() -> Fut,
Fut: std::future::Future<Output = Option<T>>,
{
let start = std::time::Instant::now();
while start.elapsed() < timeout_duration {
if let Some(value) = condition().await {
return Ok(value);
}
tokio::time::sleep(check_interval).await;
}
Err(WaeError::new(WaeErrorKind::OperationTimeout {
operation: "eventually_with_value".to_string(),
timeout_ms: timeout_duration.as_millis() as u64,
}))
}
}
pub async fn assert_eventually<F, Fut>(condition: F, timeout_duration: Duration, check_interval: Duration) -> TestingResult<()>
where
F: Fn() -> Fut,
Fut: std::future::Future<Output = bool>,
{
AsyncAssert::eventually(condition, timeout_duration, check_interval).await
}
pub fn assert_matches_regex(text: &str, pattern: &str) -> TestingResult<()> {
let re = regex::Regex::new(pattern)
.map_err(|e| WaeError::new(WaeErrorKind::AssertionFailed { message: format!("Invalid regex: {}", e) }))?;
if !re.is_match(text) {
return Err(WaeError::new(WaeErrorKind::AssertionFailed {
message: format!("Text '{}' does not match pattern '{}'", text, pattern),
}));
}
Ok(())
}
pub fn assert_json_contains(json: &serde_json::Value, path: &str) -> TestingResult<()> {
let parts: Vec<&str> = path.split('.').collect();
let mut current = json;
for part in parts {
if let serde_json::Value::Object(map) = current {
current = map.get(part).ok_or_else(|| {
WaeError::new(WaeErrorKind::AssertionFailed { message: format!("JSON path '{}' not found", path) })
})?;
}
else {
return Err(WaeError::new(WaeErrorKind::AssertionFailed { message: format!("JSON path '{}' not found", path) }));
}
}
Ok(())
}