use crate::context::ExecutionContext;
use crate::error::Result;
use std::collections::HashMap;
pub trait CommandHandler: Send + Sync {
fn execute(
&self,
context: &mut dyn ExecutionContext,
args: &HashMap<String, String>,
) -> Result<()>;
fn validate(&self, _args: &HashMap<String, String>) -> Result<()> {
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::error::ExecutionError;
use std::any::Any;
use std::sync::{Arc, Mutex};
#[derive(Default)]
struct TestContext {
state: String,
}
impl ExecutionContext for TestContext {
fn as_any(&self) -> &dyn Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn Any {
self
}
}
struct HelloCommand;
impl CommandHandler for HelloCommand {
fn execute(
&self,
context: &mut dyn ExecutionContext,
args: &HashMap<String, String>,
) -> Result<()> {
let ctx = crate::context::downcast_mut::<TestContext>(context).ok_or_else(|| {
ExecutionError::CommandFailed(anyhow::anyhow!("Wrong context type"))
})?;
let name = args.get("name").map(|s| s.as_str()).unwrap_or("World");
ctx.state = format!("Hello, {}!", name);
Ok(())
}
}
struct ValidatedCommand;
impl CommandHandler for ValidatedCommand {
fn execute(
&self,
_context: &mut dyn ExecutionContext,
_args: &HashMap<String, String>,
) -> Result<()> {
Ok(())
}
fn validate(&self, args: &HashMap<String, String>) -> Result<()> {
if let Some(count) = args.get("count") {
let count_val: i32 = count.parse().map_err(|_| {
ExecutionError::CommandFailed(anyhow::anyhow!("count must be an integer"))
})?;
if count_val <= 0 {
return Err(ExecutionError::CommandFailed(anyhow::anyhow!(
"count must be positive"
))
.into());
}
} else {
return Err(
ExecutionError::CommandFailed(anyhow::anyhow!("count is required")).into(),
);
}
Ok(())
}
}
struct FailingCommand;
impl CommandHandler for FailingCommand {
fn execute(
&self,
_context: &mut dyn ExecutionContext,
_args: &HashMap<String, String>,
) -> Result<()> {
Err(ExecutionError::CommandFailed(anyhow::anyhow!("Simulated failure")).into())
}
}
struct StatefulCommand;
impl CommandHandler for StatefulCommand {
fn execute(
&self,
context: &mut dyn ExecutionContext,
args: &HashMap<String, String>,
) -> Result<()> {
let ctx = crate::context::downcast_mut::<TestContext>(context).ok_or_else(|| {
ExecutionError::CommandFailed(anyhow::anyhow!("Wrong context type"))
})?;
let value = args.get("value").map(|s| s.as_str()).unwrap_or("default");
ctx.state.push_str(value);
Ok(())
}
}
#[test]
fn test_basic_execution() {
let handler = HelloCommand;
let mut context = TestContext::default();
let mut args = HashMap::new();
args.insert("name".to_string(), "Rust".to_string());
let result = handler.execute(&mut context, &args);
assert!(result.is_ok());
assert_eq!(context.state, "Hello, Rust!");
}
#[test]
fn test_execution_without_args() {
let handler = HelloCommand;
let mut context = TestContext::default();
let args = HashMap::new();
let result = handler.execute(&mut context, &args);
assert!(result.is_ok());
assert_eq!(context.state, "Hello, World!");
}
#[test]
fn test_execution_with_empty_name() {
let handler = HelloCommand;
let mut context = TestContext::default();
let mut args = HashMap::new();
args.insert("name".to_string(), "".to_string());
let result = handler.execute(&mut context, &args);
assert!(result.is_ok());
assert_eq!(context.state, "Hello, !");
}
#[test]
fn test_default_validation_accepts_all() {
let handler = HelloCommand;
let mut args = HashMap::new();
args.insert("random".to_string(), "value".to_string());
let result = handler.validate(&args);
assert!(result.is_ok());
}
#[test]
fn test_custom_validation_success() {
let handler = ValidatedCommand;
let mut args = HashMap::new();
args.insert("count".to_string(), "5".to_string());
let result = handler.validate(&args);
assert!(result.is_ok());
}
#[test]
fn test_custom_validation_missing_arg() {
let handler = ValidatedCommand;
let args = HashMap::new();
let result = handler.validate(&args);
assert!(result.is_err());
let err_msg = format!("{}", result.unwrap_err());
assert!(err_msg.contains("required"));
}
#[test]
fn test_custom_validation_invalid_value() {
let handler = ValidatedCommand;
let mut args = HashMap::new();
args.insert("count".to_string(), "0".to_string());
let result = handler.validate(&args);
assert!(result.is_err());
let err_msg = format!("{}", result.unwrap_err());
assert!(err_msg.contains("positive"));
}
#[test]
fn test_custom_validation_non_integer() {
let handler = ValidatedCommand;
let mut args = HashMap::new();
args.insert("count".to_string(), "abc".to_string());
let result = handler.validate(&args);
assert!(result.is_err());
let err_msg = format!("{}", result.unwrap_err());
assert!(err_msg.contains("integer"));
}
#[test]
fn test_execution_failure() {
let handler = FailingCommand;
let mut context = TestContext::default();
let args = HashMap::new();
let result = handler.execute(&mut context, &args);
assert!(result.is_err());
let err_msg = format!("{}", result.unwrap_err());
assert!(err_msg.contains("Simulated failure"));
}
#[test]
fn test_context_downcast_failure() {
#[derive(Default)]
struct WrongContext;
impl ExecutionContext for WrongContext {
fn as_any(&self) -> &dyn Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn Any {
self
}
}
let handler = HelloCommand;
let mut wrong_context = WrongContext::default();
let args = HashMap::new();
let result = handler.execute(&mut wrong_context, &args);
assert!(result.is_err());
let err_msg = format!("{}", result.unwrap_err());
assert!(err_msg.contains("Wrong context type"));
}
#[test]
fn test_context_state_modification() {
let handler = StatefulCommand;
let mut context = TestContext::default();
context.state = "initial".to_string();
let mut args = HashMap::new();
args.insert("value".to_string(), "_modified".to_string());
let result = handler.execute(&mut context, &args);
assert!(result.is_ok());
assert_eq!(context.state, "initial_modified");
}
#[test]
fn test_multiple_executions_preserve_state() {
let handler = StatefulCommand;
let mut context = TestContext::default();
let mut args1 = HashMap::new();
args1.insert("value".to_string(), "first".to_string());
handler.execute(&mut context, &args1).unwrap();
assert_eq!(context.state, "first");
let mut args2 = HashMap::new();
args2.insert("value".to_string(), "_second".to_string());
handler.execute(&mut context, &args2).unwrap();
assert_eq!(context.state, "first_second");
}
#[test]
fn test_trait_object_usage() {
let handler: Box<dyn CommandHandler> = Box::new(HelloCommand);
let mut context = TestContext::default();
let mut args = HashMap::new();
args.insert("name".to_string(), "TraitObject".to_string());
let result = handler.execute(&mut context, &args);
assert!(result.is_ok());
assert_eq!(context.state, "Hello, TraitObject!");
}
#[test]
fn test_multiple_trait_objects() {
let handlers: Vec<Box<dyn CommandHandler>> =
vec![Box::new(HelloCommand), Box::new(StatefulCommand)];
let mut context = TestContext::default();
let mut args1 = HashMap::new();
args1.insert("name".to_string(), "First".to_string());
handlers[0].execute(&mut context, &args1).unwrap();
assert_eq!(context.state, "Hello, First!");
context.state.clear();
let mut args2 = HashMap::new();
args2.insert("value".to_string(), "Second".to_string());
handlers[1].execute(&mut context, &args2).unwrap();
assert_eq!(context.state, "Second");
}
#[test]
fn test_send_sync_requirement() {
let handler: Arc<dyn CommandHandler> = Arc::new(HelloCommand);
let handler_clone = handler.clone();
let _ = std::thread::spawn(move || {
let _h = handler_clone;
});
}
#[test]
fn test_concurrent_validation() {
let handler = Arc::new(ValidatedCommand);
let handler_clone = handler.clone();
let handle = std::thread::spawn(move || {
let mut args = HashMap::new();
args.insert("count".to_string(), "10".to_string());
handler_clone.validate(&args)
});
let mut args = HashMap::new();
args.insert("count".to_string(), "5".to_string());
let result1 = handler.validate(&args);
let result2 = handle.join().unwrap();
assert!(result1.is_ok());
assert!(result2.is_ok());
}
#[test]
fn test_empty_args() {
let handler = StatefulCommand;
let mut context = TestContext::default();
let args = HashMap::new();
let result = handler.execute(&mut context, &args);
assert!(result.is_ok());
assert_eq!(context.state, "default");
}
#[test]
fn test_args_with_special_characters() {
let handler = HelloCommand;
let mut context = TestContext::default();
let mut args = HashMap::new();
args.insert("name".to_string(), "Hello, δΈη! π".to_string());
let result = handler.execute(&mut context, &args);
assert!(result.is_ok());
assert_eq!(context.state, "Hello, Hello, δΈη! π!");
}
#[test]
fn test_very_long_argument() {
let handler = HelloCommand;
let mut context = TestContext::default();
let mut args = HashMap::new();
let long_name = "x".repeat(10000);
args.insert("name".to_string(), long_name.clone());
let result = handler.execute(&mut context, &args);
assert!(result.is_ok());
assert!(context.state.contains(&long_name));
}
#[test]
fn test_shared_mutable_context() {
let handler1 = StatefulCommand;
let handler2 = StatefulCommand;
let mut context = TestContext::default();
let mut args1 = HashMap::new();
args1.insert("value".to_string(), "A".to_string());
handler1.execute(&mut context, &args1).unwrap();
let mut args2 = HashMap::new();
args2.insert("value".to_string(), "B".to_string());
handler2.execute(&mut context, &args2).unwrap();
assert_eq!(context.state, "AB");
}
#[test]
fn test_object_safety_compile_time() {
fn _accepts_trait_object(_: &dyn CommandHandler) {}
let handler = HelloCommand;
_accepts_trait_object(&handler);
}
#[allow(dead_code)]
fn test_no_generic_methods_documentation() {}
}