use std::sync::Arc;
use rocketmq_error::AuthError;
use crate::authentication::chain::handler::AuthenticationHandler;
use crate::authentication::context::default_authentication_context::DefaultAuthenticationContext;
pub struct AuthenticationHandlerChain {
handlers: Vec<Arc<dyn AuthenticationHandler>>,
}
impl AuthenticationHandlerChain {
pub fn new() -> Self {
Self { handlers: Vec::new() }
}
pub fn with_handlers(handlers: Vec<Arc<dyn AuthenticationHandler>>) -> Self {
Self { handlers }
}
pub fn add_handler(&mut self, handler: Arc<dyn AuthenticationHandler>) {
self.handlers.push(handler);
}
pub async fn execute(&self, context: &DefaultAuthenticationContext) -> Result<(), AuthError> {
for handler in &self.handlers {
handler.handle(context).await?;
}
Ok(())
}
pub fn len(&self) -> usize {
self.handlers.len()
}
pub fn is_empty(&self) -> bool {
self.handlers.is_empty()
}
}
impl Default for AuthenticationHandlerChain {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use std::future::Future;
use std::pin::Pin;
use super::*;
struct TestHandler {
should_fail: bool,
}
impl AuthenticationHandler for TestHandler {
fn handle<'a>(
&'a self,
_context: &'a DefaultAuthenticationContext,
) -> Pin<Box<dyn Future<Output = Result<(), AuthError>> + Send + 'a>> {
let should_fail = self.should_fail;
Box::pin(async move {
if should_fail {
Err(AuthError::AuthenticationFailed("Test failure".to_string()))
} else {
Ok(())
}
})
}
}
#[tokio::test]
async fn test_empty_chain() {
let chain = AuthenticationHandlerChain::new();
let context = DefaultAuthenticationContext::new();
assert!(chain.is_empty());
assert_eq!(chain.len(), 0);
let result = chain.execute(&context).await;
assert!(result.is_ok());
}
#[tokio::test]
async fn test_single_handler_success() {
let mut chain = AuthenticationHandlerChain::new();
chain.add_handler(Arc::new(TestHandler { should_fail: false }));
let context = DefaultAuthenticationContext::new();
let result = chain.execute(&context).await;
assert!(result.is_ok());
}
#[tokio::test]
async fn test_single_handler_failure() {
let mut chain = AuthenticationHandlerChain::new();
chain.add_handler(Arc::new(TestHandler { should_fail: true }));
let context = DefaultAuthenticationContext::new();
let result = chain.execute(&context).await;
assert!(result.is_err());
}
#[tokio::test]
async fn test_multiple_handlers_all_success() {
let mut chain = AuthenticationHandlerChain::new();
chain.add_handler(Arc::new(TestHandler { should_fail: false }));
chain.add_handler(Arc::new(TestHandler { should_fail: false }));
chain.add_handler(Arc::new(TestHandler { should_fail: false }));
let context = DefaultAuthenticationContext::new();
let result = chain.execute(&context).await;
assert_eq!(chain.len(), 3);
assert!(result.is_ok());
}
#[tokio::test]
async fn test_chain_stops_at_first_error() {
let mut chain = AuthenticationHandlerChain::new();
chain.add_handler(Arc::new(TestHandler { should_fail: false }));
chain.add_handler(Arc::new(TestHandler { should_fail: true }));
chain.add_handler(Arc::new(TestHandler { should_fail: false }));
let context = DefaultAuthenticationContext::new();
let result = chain.execute(&context).await;
assert!(result.is_err());
}
#[tokio::test]
async fn test_with_handlers_constructor() {
let handlers: Vec<Arc<dyn AuthenticationHandler>> = vec![
Arc::new(TestHandler { should_fail: false }),
Arc::new(TestHandler { should_fail: false }),
];
let chain = AuthenticationHandlerChain::with_handlers(handlers);
let context = DefaultAuthenticationContext::new();
assert_eq!(chain.len(), 2);
let result = chain.execute(&context).await;
assert!(result.is_ok());
}
}