use musicfs_cas::CasError; use musicfs_core::Error; use std::time::{Duration, Instant}; pub fn assert_error_contains(result: Result, expected_text: &str) { match result { Ok(_) => panic!("Expected error containing '{}', but got Ok", expected_text), Err(e) => { let error_msg = format!("{:?}", e); assert!( error_msg.contains(expected_text), "Expected error containing '{}', but got: {}", expected_text, error_msg ); } } } pub fn assert_io_error(result: Result) { match result { Err(Error::Io(_)) => (), Err(e) => panic!("Expected Io error, got: {:?}", e), Ok(_) => panic!("Expected Io error, got Ok"), } } pub fn assert_cas_io_error(result: Result) { match result { Err(CasError::Io(_)) => (), Err(e) => panic!("Expected CasError::Io, got: {:?}", e), Ok(_) => panic!("Expected CasError::Io, got Ok"), } } pub fn assert_cas_not_found(result: Result) { match result { Err(CasError::NotFound(_)) => (), Err(e) => panic!("Expected CasError::NotFound, got: {:?}", e), Ok(_) => panic!("Expected CasError::NotFound, got Ok"), } } pub fn assert_cas_integrity_error(result: Result) { match result { Err(CasError::IntegrityError { .. }) => (), Err(e) => panic!("Expected CasError::IntegrityError, got: {:?}", e), Ok(_) => panic!("Expected CasError::IntegrityError, got Ok"), } } pub fn assert_file_not_found(result: Result) { match result { Err(Error::FileNotFound(_)) => (), Err(e) => panic!("Expected FileNotFound error, got: {:?}", e), Ok(_) => panic!("Expected FileNotFound error, got Ok"), } } pub fn assert_origin_error(result: Result) { match result { Err(Error::Origin(_)) => (), Err(e) => panic!("Expected Origin error, got: {:?}", e), Ok(_) => panic!("Expected Origin error, got Ok"), } } pub fn assert_timeout_error(result: Result) { match result { Err(Error::Timeout(_)) => (), Err(e) => panic!("Expected Timeout error, got: {:?}", e), Ok(_) => panic!("Expected Timeout error, got Ok"), } } pub struct TimedAssertion { start: Instant, min_duration: Option, max_duration: Option, } impl TimedAssertion { pub fn new() -> Self { Self { start: Instant::now(), min_duration: None, max_duration: None, } } pub fn expect_at_least(mut self, duration: Duration) -> Self { self.min_duration = Some(duration); self } pub fn expect_at_most(mut self, duration: Duration) -> Self { self.max_duration = Some(duration); self } pub fn assert_elapsed(self) { let elapsed = self.start.elapsed(); if let Some(min) = self.min_duration { assert!( elapsed >= min, "Expected at least {:?}, but only {:?} elapsed", min, elapsed ); } if let Some(max) = self.max_duration { assert!( elapsed <= max, "Expected at most {:?}, but {:?} elapsed", max, elapsed ); } } } impl Default for TimedAssertion { fn default() -> Self { Self::new() } } pub async fn assert_completes_within(future: F, timeout: Duration) -> T where F: std::future::Future, { tokio::time::timeout(timeout, future) .await .expect(&format!("Operation did not complete within {:?}", timeout)) } pub async fn assert_times_out(future: F, timeout: Duration) where F: std::future::Future, { match tokio::time::timeout(timeout, future).await { Ok(_) => panic!("Expected operation to time out, but it completed"), Err(_) => (), } } #[cfg(test)] mod tests { use super::*; #[test] fn test_assert_error_contains() { let result: Result<(), Error> = Err(Error::Origin("connection refused".into())); assert_error_contains(result, "connection"); } #[test] #[should_panic(expected = "Expected error containing")] fn test_assert_error_contains_failure() { let result: Result<(), Error> = Err(Error::Origin("something else".into())); assert_error_contains(result, "connection"); } #[test] fn test_assert_io_error() { let result: Result<(), Error> = Err(Error::Io(std::io::Error::new( std::io::ErrorKind::Other, "test", ))); assert_io_error(result); } #[test] fn test_timed_assertion_at_least() { let timer = TimedAssertion::new().expect_at_least(Duration::from_millis(10)); std::thread::sleep(Duration::from_millis(15)); timer.assert_elapsed(); } #[test] fn test_timed_assertion_at_most() { let timer = TimedAssertion::new().expect_at_most(Duration::from_millis(100)); timer.assert_elapsed(); } #[tokio::test] async fn test_assert_completes_within() { let result = assert_completes_within(async { 42 }, Duration::from_millis(100)).await; assert_eq!(result, 42); } #[tokio::test] async fn test_assert_times_out() { assert_times_out( async { tokio::time::sleep(Duration::from_secs(10)).await; }, Duration::from_millis(10), ) .await; } }