use paladin::application::cli::config::paladin_config::{
GarrisonConfig, PaladinYamlConfig, ProviderConfig,
};
use paladin_ports::output::llm_port::{LlmError, LlmPort};
use paladin::infrastructure::resilience::circuit_breaker::CircuitBreaker;
use paladin::application::services::paladin::paladin_execution_service::PaladinExecutionService;
use paladin::core::base::entity::node::Node;
use paladin::core::platform::container::paladin::{MaxLoops, PaladinData, PaladinStatus};
use std::sync::Arc;
use std::time::Duration;
use tempfile::NamedTempFile;
use crate::helpers::MockLlmAdapter;
#[tokio::test]
async fn test_llm_rate_limit_error() {
let mock_llm = Arc::new(MockLlmAdapter::new());
mock_llm.add_failure(LlmError::RateLimitExceeded);
let paladin_data = PaladinData {
system_prompt: "Test system".to_string(),
name: "RateLimitTest".to_string(),
user_name: "TestUser".to_string(),
model: "gpt-4".to_string(),
temperature: 0.7,
max_loops: MaxLoops::Fixed(1),
stop_words: vec![],
status: PaladinStatus::Idle,
vision_enabled: false,
..Default::default()
};
let paladin = Node::new(paladin_data, None);
let circuit_breaker = Arc::new(CircuitBreaker::new(3, 2, Duration::from_secs(30)));
let service =
PaladinExecutionService::new(mock_llm as Arc<dyn LlmPort>, circuit_breaker, None, None);
let result = service.execute(&paladin, "test").await;
assert!(result.is_err(), "Should return error for rate limit");
}
#[tokio::test]
async fn test_llm_timeout_error() {
let mock_llm = Arc::new(MockLlmAdapter::new());
mock_llm.add_failure(LlmError::Timeout("Request timed out after 30s".to_string()));
let paladin_data = PaladinData {
system_prompt: "Test system".to_string(),
name: "TimeoutTest".to_string(),
user_name: "TestUser".to_string(),
model: "gpt-4".to_string(),
temperature: 0.7,
max_loops: MaxLoops::Fixed(1),
stop_words: vec![],
status: PaladinStatus::Idle,
vision_enabled: false,
..Default::default()
};
let paladin = Node::new(paladin_data, None);
let circuit_breaker = Arc::new(CircuitBreaker::new(3, 2, Duration::from_secs(30)));
let service =
PaladinExecutionService::new(mock_llm as Arc<dyn LlmPort>, circuit_breaker, None, None);
let result = service.execute(&paladin, "test").await;
assert!(result.is_err(), "Should return error for timeout");
}
#[tokio::test]
async fn test_llm_authentication_error() {
let mock_llm = Arc::new(MockLlmAdapter::new());
mock_llm.add_failure(LlmError::AuthenticationError("Invalid API key".to_string()));
let paladin_data = PaladinData {
system_prompt: "Test system".to_string(),
name: "AuthTest".to_string(),
user_name: "TestUser".to_string(),
model: "gpt-4".to_string(),
temperature: 0.7,
max_loops: MaxLoops::Fixed(1),
stop_words: vec![],
status: PaladinStatus::Idle,
vision_enabled: false,
..Default::default()
};
let paladin = Node::new(paladin_data, None);
let circuit_breaker = Arc::new(CircuitBreaker::new(3, 2, Duration::from_secs(30)));
let service =
PaladinExecutionService::new(mock_llm as Arc<dyn LlmPort>, circuit_breaker, None, None);
let result = service.execute(&paladin, "test").await;
assert!(
result.is_err(),
"Should return error for authentication failure"
);
}
#[tokio::test]
async fn test_llm_network_error() {
let mock_llm = Arc::new(MockLlmAdapter::new());
mock_llm.add_failure(LlmError::NetworkError(
"Failed to connect to API endpoint".to_string(),
));
let paladin_data = PaladinData {
system_prompt: "Test system".to_string(),
name: "NetworkTest".to_string(),
user_name: "TestUser".to_string(),
model: "gpt-4".to_string(),
temperature: 0.7,
max_loops: MaxLoops::Fixed(1),
stop_words: vec![],
status: PaladinStatus::Idle,
vision_enabled: false,
..Default::default()
};
let paladin = Node::new(paladin_data, None);
let circuit_breaker = Arc::new(CircuitBreaker::new(3, 2, Duration::from_secs(30)));
let service =
PaladinExecutionService::new(mock_llm as Arc<dyn LlmPort>, circuit_breaker, None, None);
let result = service.execute(&paladin, "test").await;
assert!(result.is_err(), "Should return error for network failure");
}
#[test]
fn test_invalid_yaml_config_structure() {
let invalid_yaml = r#"
paladin:
name: "test
missing_closing_quote_and_proper_structure
"#;
let result: Result<PaladinYamlConfig, _> = serde_yaml::from_str(invalid_yaml);
assert!(result.is_err(), "Should fail to parse invalid YAML");
}
#[test]
fn test_empty_yaml_config() {
let empty_yaml = "";
let result: Result<PaladinYamlConfig, _> = serde_yaml::from_str(empty_yaml);
let is_err_or_none = result.is_err() || result.map(|c| c.name.is_empty()).unwrap_or(true);
assert!(is_err_or_none, "Should handle empty YAML appropriately");
}
#[test]
fn test_missing_required_fields() {
let incomplete_yaml = r#"
name: "test-paladin"
# Missing model, system_prompt, etc.
"#;
let result: Result<PaladinYamlConfig, _> = serde_yaml::from_str(incomplete_yaml);
assert!(
result.is_err() || result.is_ok(),
"Should handle missing fields"
);
}
#[test]
fn test_invalid_garrison_type() {
let yaml = r#"
garrison:
garrison_type: "invalid_type"
config:
max_entries: 100
"#;
let result: Result<GarrisonConfig, _> = serde_yaml::from_str(yaml);
assert!(
result.is_ok() || result.is_err(),
"YAML parsing may succeed, validation happens at runtime"
);
}
#[tokio::test]
async fn test_empty_input_string() {
let mock_llm = Arc::new(MockLlmAdapter::new());
mock_llm.add_success("I received an empty input");
let paladin_data = PaladinData {
system_prompt: "Handle empty inputs gracefully".to_string(),
name: "EmptyInputTest".to_string(),
user_name: "TestUser".to_string(),
model: "gpt-4".to_string(),
temperature: 0.7,
max_loops: MaxLoops::Fixed(1),
stop_words: vec![],
status: PaladinStatus::Idle,
vision_enabled: false,
..Default::default()
};
let paladin = Node::new(paladin_data, None);
let circuit_breaker = Arc::new(CircuitBreaker::new(3, 2, Duration::from_secs(30)));
let service =
PaladinExecutionService::new(mock_llm as Arc<dyn LlmPort>, circuit_breaker, None, None);
let result = service.execute(&paladin, "").await;
assert!(
result.is_ok() || result.is_err(),
"Should handle empty input gracefully"
);
}
#[tokio::test]
async fn test_very_large_input() {
let mock_llm = Arc::new(MockLlmAdapter::new());
mock_llm.add_success("Processed large input");
let paladin_data = PaladinData {
system_prompt: "Process large inputs".to_string(),
name: "LargeInputTest".to_string(),
user_name: "TestUser".to_string(),
model: "gpt-4".to_string(),
temperature: 0.7,
max_loops: MaxLoops::Fixed(1),
stop_words: vec![],
status: PaladinStatus::Idle,
vision_enabled: false,
..Default::default()
};
let paladin = Node::new(paladin_data, None);
let circuit_breaker = Arc::new(CircuitBreaker::new(3, 2, Duration::from_secs(30)));
let service =
PaladinExecutionService::new(mock_llm as Arc<dyn LlmPort>, circuit_breaker, None, None);
let large_input = "x".repeat(15 * 1024);
let result = service.execute(&paladin, &large_input).await;
assert!(
result.is_ok() || result.is_err(),
"Should handle large input without crashing"
);
}
#[tokio::test]
async fn test_special_characters_in_input() {
let mock_llm = Arc::new(MockLlmAdapter::new());
mock_llm.add_success("Handled special characters");
let paladin_data = PaladinData {
system_prompt: "Handle special characters".to_string(),
name: "SpecialCharsTest".to_string(),
user_name: "TestUser".to_string(),
model: "gpt-4".to_string(),
temperature: 0.7,
max_loops: MaxLoops::Fixed(1),
stop_words: vec![],
status: PaladinStatus::Idle,
vision_enabled: false,
..Default::default()
};
let paladin = Node::new(paladin_data, None);
let circuit_breaker = Arc::new(CircuitBreaker::new(3, 2, Duration::from_secs(30)));
let service =
PaladinExecutionService::new(mock_llm as Arc<dyn LlmPort>, circuit_breaker, None, None);
let special_input = "Test with émojis 🚀, quotes \"', newlines\n, tabs\t, and 中文字符";
let result = service.execute(&paladin, special_input).await;
assert!(result.is_ok(), "Should handle special characters in input");
}
#[test]
fn test_missing_config_file() {
let result = std::fs::read_to_string("/nonexistent/path/config.yaml");
assert!(
result.is_err(),
"Should fail when config file doesn't exist"
);
}
#[test]
fn test_config_file_with_write_errors() {
let temp_file = NamedTempFile::new().expect("Failed to create temp file");
let path = temp_file.path();
let read_result = std::fs::read_to_string(path);
assert!(
read_result.is_ok() || read_result.is_err(),
"File operations should be testable"
);
}
#[test]
fn test_provider_config_validation() {
let valid_config = ProviderConfig {
provider_type: "openai".to_string(),
};
let yaml = serde_yaml::to_string(&valid_config).unwrap();
let parsed: Result<ProviderConfig, _> = serde_yaml::from_str(&yaml);
assert!(parsed.is_ok(), "Valid provider config should parse");
let anthropic_config = ProviderConfig {
provider_type: "anthropic".to_string(),
};
let yaml2 = serde_yaml::to_string(&anthropic_config).unwrap();
let parsed2: Result<ProviderConfig, _> = serde_yaml::from_str(&yaml2);
assert!(parsed2.is_ok(), "Anthropic provider config should parse");
}