use crate::{Request, Result, Scheduler, SilentError};
use async_lock::Mutex;
use http::StatusCode;
use std::sync::Arc;
pub trait SchedulerExt {
fn scheduler(&self) -> Result<&Arc<Mutex<Scheduler>>>;
}
impl SchedulerExt for Request {
fn scheduler(&self) -> Result<&Arc<Mutex<Scheduler>>> {
self.extensions().get().ok_or_else(|| {
SilentError::business_error(StatusCode::INTERNAL_SERVER_ERROR, "No scheduler found")
})
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_scheduler_ext_success() {
let mut req = Request::empty();
let scheduler = Arc::new(Mutex::new(Scheduler::new()));
req.extensions_mut().insert(scheduler.clone());
let result = req.scheduler();
assert!(result.is_ok());
let retrieved_scheduler = result.unwrap();
assert!(Arc::ptr_eq(retrieved_scheduler, &scheduler));
}
#[test]
fn test_scheduler_ext_multiple_calls() {
let mut req = Request::empty();
let scheduler = Arc::new(Mutex::new(Scheduler::new()));
req.extensions_mut().insert(scheduler.clone());
let result1 = req.scheduler();
let result2 = req.scheduler();
assert!(result1.is_ok());
assert!(result2.is_ok());
let scheduler1 = result1.unwrap();
let scheduler2 = result2.unwrap();
assert!(Arc::ptr_eq(scheduler1, scheduler2));
assert!(Arc::ptr_eq(scheduler1, &scheduler));
}
#[test]
fn test_scheduler_ext_with_global_scheduler() {
let mut req = Request::empty();
let global_scheduler = crate::scheduler::SCHEDULER.clone();
req.extensions_mut().insert(global_scheduler);
let result = req.scheduler();
assert!(result.is_ok());
let retrieved_scheduler = result.unwrap();
assert!(Arc::ptr_eq(
retrieved_scheduler,
&crate::scheduler::SCHEDULER
));
}
#[test]
fn test_scheduler_ext_no_scheduler() {
let req = Request::empty();
let result = req.scheduler();
assert!(result.is_err());
}
#[test]
fn test_scheduler_ext_error_status_code() {
let req = Request::empty();
let result = req.scheduler();
assert!(result.is_err());
let err = result.unwrap_err();
assert_eq!(err.status(), StatusCode::INTERNAL_SERVER_ERROR);
}
#[test]
fn test_scheduler_ext_error_message() {
let req = Request::empty();
let result = req.scheduler();
assert!(result.is_err());
let err = result.unwrap_err();
assert!(err.message().contains("No scheduler found"));
}
#[cfg(feature = "server")]
#[tokio::test]
async fn test_scheduler_ext_with_middleware() {
use crate::Handler;
use crate::route::Route;
use crate::scheduler::middleware::SchedulerMiddleware;
let middleware = SchedulerMiddleware::new();
let route = Route::new("/")
.hook(middleware)
.get(|req: Request| async move {
let scheduler = req.scheduler();
assert!(scheduler.is_ok());
let retrieved_scheduler = scheduler.unwrap();
assert!(Arc::ptr_eq(
retrieved_scheduler,
&crate::scheduler::SCHEDULER
));
Ok("scheduler found")
});
let route = Route::new_root().append(route);
let req = Request::empty();
let result: crate::Result<crate::Response> = route.call(req).await;
assert!(result.is_ok());
}
#[cfg(feature = "server")]
#[tokio::test]
async fn test_scheduler_ext_without_middleware() {
use crate::Handler;
use crate::route::Route;
let route = Route::new("/").get(|req: Request| async move {
let scheduler = req.scheduler();
assert!(scheduler.is_err());
assert_eq!(
scheduler.unwrap_err().status(),
StatusCode::INTERNAL_SERVER_ERROR
);
Ok("no scheduler")
});
let route = Route::new_root().append(route);
let req = Request::empty();
let result: crate::Result<crate::Response> = route.call(req).await;
assert!(result.is_ok());
}
#[test]
fn test_scheduler_ext_scheduler_removed() {
let mut req = Request::empty();
let scheduler = Arc::new(Mutex::new(Scheduler::new()));
req.extensions_mut().insert(scheduler.clone());
let result1 = req.scheduler();
assert!(result1.is_ok());
req.extensions_mut().remove::<Arc<Mutex<Scheduler>>>();
let result2 = req.scheduler();
assert!(result2.is_err());
assert_eq!(
result2.unwrap_err().status(),
StatusCode::INTERNAL_SERVER_ERROR
);
}
#[test]
fn test_scheduler_ext_scheduler_replaced() {
let mut req = Request::empty();
let scheduler1 = Arc::new(Mutex::new(Scheduler::new()));
req.extensions_mut().insert(scheduler1.clone());
let result1 = req.scheduler();
assert!(result1.is_ok());
assert!(Arc::ptr_eq(result1.unwrap(), &scheduler1));
let scheduler2 = Arc::new(Mutex::new(Scheduler::new()));
req.extensions_mut().insert(scheduler2.clone());
let result2 = req.scheduler();
assert!(result2.is_ok());
let retrieved2 = result2.unwrap();
assert!(Arc::ptr_eq(retrieved2, &scheduler2));
assert!(!Arc::ptr_eq(retrieved2, &scheduler1));
}
#[cfg(feature = "server")]
#[tokio::test]
async fn test_scheduler_ext_with_different_http_methods() {
use crate::Handler;
use crate::route::Route;
use crate::scheduler::middleware::SchedulerMiddleware;
let middleware = SchedulerMiddleware::new();
let route = Route::new("/")
.hook(middleware)
.get(|req: Request| async move {
let scheduler = req.scheduler();
assert!(scheduler.is_ok());
Ok("GET")
})
.post(|req: Request| async move {
let scheduler = req.scheduler();
assert!(scheduler.is_ok());
Ok("POST")
})
.put(|req: Request| async move {
let scheduler = req.scheduler();
assert!(scheduler.is_ok());
Ok("PUT")
});
let route = Route::new_root().append(route);
let mut req = Request::empty();
*req.method_mut() = http::Method::GET;
let result: crate::Result<crate::Response> = route.call(req).await;
assert!(result.is_ok());
let mut req = Request::empty();
*req.method_mut() = http::Method::POST;
let result: crate::Result<crate::Response> = route.call(req).await;
assert!(result.is_ok());
let mut req = Request::empty();
*req.method_mut() = http::Method::PUT;
let result: crate::Result<crate::Response> = route.call(req).await;
assert!(result.is_ok());
}
#[cfg(feature = "server")]
#[tokio::test]
async fn test_scheduler_ext_concurrent_requests() {
use crate::Handler;
use crate::route::Route;
use crate::scheduler::middleware::SchedulerMiddleware;
use std::sync::Arc;
let middleware = SchedulerMiddleware::new();
let route = Route::new("/")
.hook(middleware)
.get(|req: Request| async move {
let scheduler = req.scheduler();
assert!(scheduler.is_ok());
Ok("concurrent")
});
let route: Arc<Route> = Arc::new(Route::new_root().append(route));
let tasks = (0..5).map(|_| {
let route = Arc::clone(&route);
tokio::spawn(async move {
let req = Request::empty();
let result: crate::Result<crate::Response> = route.call(req).await;
result
})
});
for task in tasks {
let result = task.await.unwrap();
assert!(result.is_ok());
}
}
#[test]
fn test_scheduler_ext_lifetime() {
let mut req = Request::empty();
let scheduler = Arc::new(Mutex::new(Scheduler::new()));
req.extensions_mut().insert(scheduler.clone());
let scheduler_ref = req.scheduler().unwrap();
assert!(Arc::ptr_eq(scheduler_ref, &scheduler));
drop(scheduler);
assert!(Arc::ptr_eq(
scheduler_ref,
req.extensions().get::<Arc<Mutex<Scheduler>>>().unwrap()
));
}
#[test]
fn test_scheduler_ext_scheduler_mutability() {
let mut req = Request::empty();
let scheduler = Arc::new(Mutex::new(Scheduler::new()));
req.extensions_mut().insert(scheduler);
let scheduler_ref = req.scheduler().unwrap();
assert!(Arc::ptr_eq(
scheduler_ref,
req.extensions().get::<Arc<Mutex<Scheduler>>>().unwrap()
));
}
}