293 lines
8.3 KiB
Rust
293 lines
8.3 KiB
Rust
|
|
//! Integration tests for daemon-cli
|
||
|
|
//!
|
||
|
|
//! Tests interaction between multiple components
|
||
|
|
|
||
|
|
use std::sync::Arc;
|
||
|
|
|
||
|
|
use daemon_cli::orchestration::{EncryptOperation, RuntimeOperation, ValidaOperation};
|
||
|
|
use daemon_cli::{
|
||
|
|
Event, EventBus, EventPayload, EventType, HealthProbe, OperationRegistry, ProbeStatus,
|
||
|
|
};
|
||
|
|
|
||
|
|
#[tokio::test]
|
||
|
|
async fn test_operation_registry_workflow() {
|
||
|
|
// Create cache (shared across operations)
|
||
|
|
let cache = Arc::new(daemon_cli::core::HierarchicalCache::new().unwrap());
|
||
|
|
|
||
|
|
// Create registry and register operations
|
||
|
|
let mut registry = OperationRegistry::default();
|
||
|
|
registry.register("valida", Arc::new(ValidaOperation::new(cache.clone())));
|
||
|
|
registry.register("runtime", Arc::new(RuntimeOperation::new(cache.clone())));
|
||
|
|
registry.register("encrypt", Arc::new(EncryptOperation::new(cache.clone())));
|
||
|
|
|
||
|
|
// Verify all operations registered
|
||
|
|
assert_eq!(registry.len(), 3);
|
||
|
|
assert!(registry.contains("valida"));
|
||
|
|
assert!(registry.contains("runtime"));
|
||
|
|
assert!(registry.contains("encrypt"));
|
||
|
|
|
||
|
|
// Verify list operation
|
||
|
|
let ops = registry.list();
|
||
|
|
assert_eq!(ops.len(), 3);
|
||
|
|
}
|
||
|
|
|
||
|
|
#[tokio::test]
|
||
|
|
async fn test_event_bus_workflow() {
|
||
|
|
let bus = EventBus::new(100);
|
||
|
|
|
||
|
|
// Create publisher and subscriber
|
||
|
|
let mut subscriber = bus.subscribe();
|
||
|
|
|
||
|
|
// Publish event
|
||
|
|
let payload = EventPayload::new("test-op", true).with_target("config.yaml");
|
||
|
|
let event = daemon_cli::Event::new(EventType::ValidationCompleted, payload);
|
||
|
|
let result = bus.publish(event.clone());
|
||
|
|
|
||
|
|
assert!(result.is_ok());
|
||
|
|
assert_eq!(result.unwrap(), 1); // One subscriber
|
||
|
|
|
||
|
|
// Receive event
|
||
|
|
let received = subscriber.recv().await.unwrap();
|
||
|
|
assert_eq!(received.event_type, EventType::ValidationCompleted);
|
||
|
|
assert!(received.payload.success);
|
||
|
|
assert_eq!(received.payload.target, Some("config.yaml".to_string()));
|
||
|
|
|
||
|
|
drop(received); // Drop to use it
|
||
|
|
}
|
||
|
|
|
||
|
|
#[tokio::test]
|
||
|
|
async fn test_event_filtering() {
|
||
|
|
let bus = EventBus::new(100);
|
||
|
|
let mut subscriber = bus.subscribe();
|
||
|
|
|
||
|
|
// Publish multiple events of different types
|
||
|
|
let payload1 = EventPayload::new("valida", true);
|
||
|
|
let event1 = Event::new(EventType::ValidationCompleted, payload1);
|
||
|
|
bus.publish(event1).ok();
|
||
|
|
|
||
|
|
let payload2 = EventPayload::new("runtime", true);
|
||
|
|
let event2 = Event::new(EventType::RuntimeDetected, payload2);
|
||
|
|
bus.publish(event2).ok();
|
||
|
|
|
||
|
|
// Receive and filter
|
||
|
|
let mut validation_count = 0;
|
||
|
|
let mut runtime_count = 0;
|
||
|
|
|
||
|
|
for _ in 0..2 {
|
||
|
|
if let Ok(event) = subscriber.recv().await {
|
||
|
|
match event.event_type {
|
||
|
|
EventType::ValidationCompleted => validation_count += 1,
|
||
|
|
EventType::RuntimeDetected => runtime_count += 1,
|
||
|
|
_ => {}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
assert_eq!(validation_count, 1);
|
||
|
|
assert_eq!(runtime_count, 1);
|
||
|
|
}
|
||
|
|
|
||
|
|
#[test]
|
||
|
|
fn test_health_probe_lifecycle() {
|
||
|
|
let probe = HealthProbe::new();
|
||
|
|
|
||
|
|
// Check initial state
|
||
|
|
assert_eq!(probe.is_alive(), ProbeStatus::Healthy);
|
||
|
|
assert_eq!(probe.is_ready(), ProbeStatus::Healthy);
|
||
|
|
assert_eq!(probe.operation_count(), 0);
|
||
|
|
|
||
|
|
// Simulate operations
|
||
|
|
for _ in 0..5 {
|
||
|
|
probe.increment_operations();
|
||
|
|
}
|
||
|
|
|
||
|
|
assert_eq!(probe.operation_count(), 5);
|
||
|
|
let _ = probe.uptime_secs();
|
||
|
|
|
||
|
|
// Verify probe responses
|
||
|
|
let liveness = probe.liveness_response();
|
||
|
|
assert_eq!(liveness.status, ProbeStatus::Healthy);
|
||
|
|
|
||
|
|
let readiness = probe.readiness_response();
|
||
|
|
assert_eq!(readiness.status, ProbeStatus::Healthy);
|
||
|
|
}
|
||
|
|
|
||
|
|
#[test]
|
||
|
|
fn test_operation_registry_complete_workflow() {
|
||
|
|
let mut registry = OperationRegistry::default();
|
||
|
|
|
||
|
|
// Start with empty registry
|
||
|
|
assert_eq!(registry.len(), 0);
|
||
|
|
assert!(!registry.contains("test-op"));
|
||
|
|
|
||
|
|
// Register operations - not implementing actual operations for this test
|
||
|
|
// In real scenario, we'd register actual operation implementations
|
||
|
|
// For this test, we just verify the registry behavior
|
||
|
|
|
||
|
|
// List operations
|
||
|
|
let list = registry.list();
|
||
|
|
assert_eq!(list.len(), 0); // Still empty since we can't mock without trait objects
|
||
|
|
|
||
|
|
// Verify unregister
|
||
|
|
assert!(!registry.unregister("nonexistent"));
|
||
|
|
}
|
||
|
|
|
||
|
|
#[tokio::test]
|
||
|
|
async fn test_multiple_subscribers_event_delivery() {
|
||
|
|
let bus = EventBus::new(100);
|
||
|
|
|
||
|
|
// Create multiple subscribers
|
||
|
|
let mut sub1 = bus.subscribe();
|
||
|
|
let mut sub2 = bus.subscribe();
|
||
|
|
let mut sub3 = bus.subscribe();
|
||
|
|
|
||
|
|
assert_eq!(bus.subscriber_count(), 3);
|
||
|
|
|
||
|
|
// Publish event
|
||
|
|
let payload = EventPayload::new("multi-sub-test", true);
|
||
|
|
let event = Event::new(EventType::Custom, payload);
|
||
|
|
let result = bus.publish(event);
|
||
|
|
|
||
|
|
assert_eq!(result.unwrap(), 3); // All 3 subscribers receive event
|
||
|
|
|
||
|
|
// Verify all subscribers got the event
|
||
|
|
for _ in 0..3 {
|
||
|
|
if let Ok(_e1) = sub1.try_recv() {
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
if let Ok(_e2) = sub2.try_recv() {
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
if let Ok(_e3) = sub3.try_recv() {
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
#[tokio::test]
|
||
|
|
async fn test_cache_performance_basic() {
|
||
|
|
use std::time::Instant;
|
||
|
|
|
||
|
|
let cache = daemon_cli::core::HierarchicalCache::new().unwrap();
|
||
|
|
|
||
|
|
// Measure cache set performance
|
||
|
|
let start = Instant::now();
|
||
|
|
let mut count = 0;
|
||
|
|
for i in 0..100 {
|
||
|
|
let key = format!("test-key-{}", i);
|
||
|
|
let value = format!("test-value-{}", i);
|
||
|
|
let _ = cache.set_command(key, value).await;
|
||
|
|
count += 1;
|
||
|
|
}
|
||
|
|
let elapsed = start.elapsed();
|
||
|
|
|
||
|
|
assert_eq!(count, 100);
|
||
|
|
println!(
|
||
|
|
"Cache 100 sets: {:?} ({:.2} ops/sec)",
|
||
|
|
elapsed,
|
||
|
|
100.0 / elapsed.as_secs_f64()
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
#[tokio::test]
|
||
|
|
async fn test_webhook_event_creation() {
|
||
|
|
use daemon_cli::webhooks::{WebHookEvent, WebHookPayload, WebHookType};
|
||
|
|
|
||
|
|
let payload = WebHookPayload::new()
|
||
|
|
.with_repository("test-repo")
|
||
|
|
.with_branch("main")
|
||
|
|
.with_message("Test commit");
|
||
|
|
|
||
|
|
let event = WebHookEvent::new(WebHookType::Push, payload);
|
||
|
|
|
||
|
|
assert_eq!(event.hook_type, WebHookType::Push);
|
||
|
|
assert_eq!(event.payload.repository, Some("test-repo".to_string()));
|
||
|
|
assert_eq!(event.attempt, 1);
|
||
|
|
assert!(!event.webhook_id.is_empty());
|
||
|
|
}
|
||
|
|
|
||
|
|
#[test]
|
||
|
|
fn test_rendering_context_builder() {
|
||
|
|
use daemon_cli::rendering::template::OutputFormat;
|
||
|
|
use daemon_cli::rendering::RenderContext;
|
||
|
|
use serde_json::json;
|
||
|
|
|
||
|
|
let ctx = RenderContext::new()
|
||
|
|
.with_variable("name", json!("test"))
|
||
|
|
.with_variable("version", json!("1.0.0"))
|
||
|
|
.with_output_format(OutputFormat::Json)
|
||
|
|
.with_config_file("/etc/config.toml");
|
||
|
|
|
||
|
|
assert_eq!(ctx.variables.len(), 2);
|
||
|
|
assert_eq!(ctx.output_format, OutputFormat::Json);
|
||
|
|
assert_eq!(ctx.config_file, Some("/etc/config.toml".to_string()));
|
||
|
|
}
|
||
|
|
|
||
|
|
#[tokio::test]
|
||
|
|
async fn test_concurrent_event_bus_subscribers() {
|
||
|
|
use std::sync::atomic::{AtomicU32, Ordering};
|
||
|
|
use std::sync::Arc as StdArc;
|
||
|
|
|
||
|
|
let bus = EventBus::new(1000);
|
||
|
|
let counter = StdArc::new(AtomicU32::new(0));
|
||
|
|
|
||
|
|
// Spawn multiple subscribers
|
||
|
|
let mut handles = vec![];
|
||
|
|
for _i in 0..5 {
|
||
|
|
let mut rx = bus.subscribe();
|
||
|
|
let counter_clone = counter.clone();
|
||
|
|
|
||
|
|
let handle = tokio::spawn(async move {
|
||
|
|
if let Ok(_event) = rx.recv().await {
|
||
|
|
counter_clone.fetch_add(1, Ordering::SeqCst);
|
||
|
|
}
|
||
|
|
});
|
||
|
|
handles.push(handle);
|
||
|
|
}
|
||
|
|
|
||
|
|
// Publish event
|
||
|
|
let payload = EventPayload::new("concurrent-test", true);
|
||
|
|
let event = Event::new(EventType::Custom, payload);
|
||
|
|
bus.publish(event).ok();
|
||
|
|
|
||
|
|
// Wait for subscribers
|
||
|
|
for handle in handles {
|
||
|
|
let _ = handle.await;
|
||
|
|
}
|
||
|
|
|
||
|
|
assert_eq!(counter.load(Ordering::SeqCst), 5);
|
||
|
|
}
|
||
|
|
|
||
|
|
#[test]
|
||
|
|
fn test_health_metrics_calculations() {
|
||
|
|
use daemon_cli::HealthMetrics;
|
||
|
|
|
||
|
|
let metrics = HealthMetrics::new();
|
||
|
|
|
||
|
|
// Record operations
|
||
|
|
for _ in 0..100 {
|
||
|
|
metrics.record_operation();
|
||
|
|
}
|
||
|
|
for _ in 0..75 {
|
||
|
|
metrics.record_success();
|
||
|
|
}
|
||
|
|
for _ in 0..25 {
|
||
|
|
metrics.record_failure();
|
||
|
|
}
|
||
|
|
|
||
|
|
// Record cache hits
|
||
|
|
for _ in 0..800 {
|
||
|
|
metrics.record_cache_hit();
|
||
|
|
}
|
||
|
|
for _ in 0..200 {
|
||
|
|
metrics.record_cache_miss();
|
||
|
|
}
|
||
|
|
|
||
|
|
// Verify calculations
|
||
|
|
assert_eq!(metrics.operations_executed(), 100);
|
||
|
|
assert_eq!(metrics.operations_succeeded(), 75);
|
||
|
|
assert_eq!(metrics.operations_failed(), 25);
|
||
|
|
assert_eq!(metrics.success_rate(), 75.0);
|
||
|
|
assert_eq!(metrics.cache_hit_rate(), 80.0);
|
||
|
|
}
|