use apcore::context::Context;
use apcore::errors::ModuleError;
use apcore::middleware::base::Middleware;
use apcore::middleware::manager::{MiddlewareManager, MiddlewareRegistration};
use async_trait::async_trait;
use serde_json::Value;
#[derive(Debug)]
struct AlphaMiddleware;
#[async_trait]
impl Middleware for AlphaMiddleware {
#[allow(clippy::unnecessary_literal_bound)]
fn name(&self) -> &str {
"alpha"
}
fn priority(&self) -> u16 {
0
}
async fn before(
&self,
_: &str,
_: Value,
_: &Context<Value>,
) -> Result<Option<Value>, ModuleError> {
Ok(None)
}
async fn after(
&self,
_: &str,
_: Value,
_: Value,
_: &Context<Value>,
) -> Result<Option<Value>, ModuleError> {
Ok(None)
}
async fn on_error(
&self,
_: &str,
_: Value,
_: &ModuleError,
_: &Context<Value>,
) -> Result<Option<Value>, ModuleError> {
Ok(None)
}
}
#[derive(Debug)]
struct BetaMiddleware;
#[async_trait]
impl Middleware for BetaMiddleware {
#[allow(clippy::unnecessary_literal_bound)]
fn name(&self) -> &str {
"beta"
}
fn priority(&self) -> u16 {
0
}
async fn before(
&self,
_: &str,
_: Value,
_: &Context<Value>,
) -> Result<Option<Value>, ModuleError> {
Ok(None)
}
async fn after(
&self,
_: &str,
_: Value,
_: Value,
_: &Context<Value>,
) -> Result<Option<Value>, ModuleError> {
Ok(None)
}
async fn on_error(
&self,
_: &str,
_: Value,
_: &ModuleError,
_: &Context<Value>,
) -> Result<Option<Value>, ModuleError> {
Ok(None)
}
}
#[test]
fn single_registration_succeeds_no_warning() {
let mgr = MiddlewareManager::new();
let result = mgr.add_with_opts(MiddlewareRegistration::new(AlphaMiddleware));
assert!(result.is_ok());
assert_eq!(mgr.snapshot(), vec!["alpha"]);
}
#[test]
fn two_same_type_registrations_chain_still_contains_both() {
let mgr = MiddlewareManager::new();
mgr.add_with_opts(MiddlewareRegistration::new(AlphaMiddleware))
.unwrap();
mgr.add_with_opts(MiddlewareRegistration::new(AlphaMiddleware))
.unwrap();
assert_eq!(
mgr.snapshot().len(),
2,
"both registrations must be present in the chain"
);
assert_eq!(
mgr.snapshot(),
vec!["alpha", "alpha"],
"chain order must be preserved"
);
}
#[test]
fn allow_duplicate_true_suppresses_warning_path() {
let mgr = MiddlewareManager::new();
mgr.add_with_opts(MiddlewareRegistration::new(AlphaMiddleware).allow_duplicate(true))
.unwrap();
mgr.add_with_opts(MiddlewareRegistration::new(AlphaMiddleware).allow_duplicate(true))
.unwrap();
assert_eq!(mgr.snapshot().len(), 2, "both entries must be in the chain");
}
#[test]
fn distinct_identity_keys_treat_instances_as_different() {
let mgr = MiddlewareManager::new();
mgr.add_with_opts(MiddlewareRegistration::new(AlphaMiddleware).identity_key("alpha-primary"))
.unwrap();
mgr.add_with_opts(MiddlewareRegistration::new(AlphaMiddleware).identity_key("alpha-secondary"))
.unwrap();
assert_eq!(mgr.snapshot().len(), 2);
}
#[test]
fn different_types_no_duplicate() {
let mgr = MiddlewareManager::new();
mgr.add_with_opts(MiddlewareRegistration::new(AlphaMiddleware))
.unwrap();
mgr.add_with_opts(MiddlewareRegistration::new(BetaMiddleware))
.unwrap();
assert_eq!(mgr.snapshot(), vec!["alpha", "beta"]);
}
#[test]
fn add_with_opts_interoperates_with_plain_add() {
let mgr = MiddlewareManager::new();
mgr.add(Box::new(AlphaMiddleware)).unwrap();
mgr.add_with_opts(MiddlewareRegistration::new(BetaMiddleware))
.unwrap();
assert_eq!(mgr.snapshot(), vec!["alpha", "beta"]);
}
#[test]
fn same_explicit_identity_key_triggers_duplicate_path() {
let mgr = MiddlewareManager::new();
mgr.add_with_opts(MiddlewareRegistration::new(AlphaMiddleware).identity_key("shared-key"))
.unwrap();
mgr.add_with_opts(MiddlewareRegistration::new(BetaMiddleware).identity_key("shared-key"))
.unwrap();
assert_eq!(mgr.snapshot().len(), 2);
}
#[derive(Debug)]
struct HighPriorityMiddleware;
#[async_trait]
impl Middleware for HighPriorityMiddleware {
#[allow(clippy::unnecessary_literal_bound)]
fn name(&self) -> &str {
"high"
}
fn priority(&self) -> u16 {
100
}
async fn before(
&self,
_: &str,
_: Value,
_: &Context<Value>,
) -> Result<Option<Value>, ModuleError> {
Ok(None)
}
async fn after(
&self,
_: &str,
_: Value,
_: Value,
_: &Context<Value>,
) -> Result<Option<Value>, ModuleError> {
Ok(None)
}
async fn on_error(
&self,
_: &str,
_: Value,
_: &ModuleError,
_: &Context<Value>,
) -> Result<Option<Value>, ModuleError> {
Ok(None)
}
}
#[test]
fn priority_ordering_preserved_with_add_with_opts() {
let mgr = MiddlewareManager::new();
mgr.add_with_opts(MiddlewareRegistration::new(AlphaMiddleware))
.unwrap();
mgr.add_with_opts(MiddlewareRegistration::new(HighPriorityMiddleware))
.unwrap();
assert_eq!(mgr.snapshot(), vec!["high", "alpha"]);
}