Vapora/crates/vapora-backend/tests/swarm_api_test.rs
Jesús Pérez ac3f93fe1d fix: Pre-commit configuration and TOML syntax corrections
**Problems Fixed:**
- TOML syntax errors in workspace.toml (inline tables spanning multiple lines)
- TOML syntax errors in vapora.toml (invalid variable substitution syntax)
- YAML multi-document handling (kubernetes and provisioning files)
- Markdown linting issues (disabled temporarily pending review)
- Rust formatting with nightly toolchain

**Changes Made:**
1. Fixed provisioning/vapora-wrksp/workspace.toml:
   - Converted inline tables to proper nested sections
   - Lines 21-39: [storage.surrealdb], [storage.redis], [storage.nats]

2. Fixed config/vapora.toml:
   - Replaced shell-style ${VAR:-default} syntax with literal values
   - All environment-based config marked with comments for runtime override

3. Updated .pre-commit-config.yaml:
   - Added kubernetes/ and provisioning/ to check-yaml exclusions
   - Disabled markdownlint hook pending markdown file cleanup
   - Keep: rust-fmt, clippy, toml check, yaml check, end-of-file, trailing-whitespace

**All Passing Hooks:**
 Rust formatting (cargo +nightly fmt)
 Rust linting (cargo clippy)
 TOML validation
 YAML validation (with multi-document support)
 End-of-file formatting
 Trailing whitespace removal
2026-01-11 21:46:08 +00:00

290 lines
8.6 KiB
Rust

// Integration tests for Swarm API endpoints
// Tests verify swarm statistics and health monitoring endpoints
use std::sync::Arc;
use vapora_swarm::{AgentProfile, SwarmCoordinator};
/// Helper to create a test agent profile
fn create_test_profile(id: &str, success_rate: f64, load: f64) -> AgentProfile {
AgentProfile {
id: id.to_string(),
roles: vec!["developer".to_string()],
capabilities: vec!["coding".to_string(), "testing".to_string()],
current_load: load,
success_rate,
availability: true,
}
}
#[tokio::test]
async fn test_swarm_coordinator_initialization() {
// Create a SwarmCoordinator
let swarm = Arc::new(SwarmCoordinator::new());
// Register test profiles
let profile1 = create_test_profile("agent-1", 0.95, 0.3);
let profile2 = create_test_profile("agent-2", 0.85, 0.5);
swarm.register_agent(profile1).ok();
swarm.register_agent(profile2).ok();
// Get statistics
let stats = swarm.get_swarm_stats();
// Verify statistics
assert_eq!(stats.total_agents, 2);
assert_eq!(stats.available_agents, 2);
assert!(stats.avg_load > 0.0);
assert!(stats.active_tasks == 0); // No tasks assigned yet
}
#[tokio::test]
async fn test_swarm_health_status_healthy() {
// Create swarm with available agents
let swarm = Arc::new(SwarmCoordinator::new());
let profile1 = create_test_profile("agent-1", 0.95, 0.3);
let profile2 = create_test_profile("agent-2", 0.90, 0.2);
swarm.register_agent(profile1).ok();
swarm.register_agent(profile2).ok();
let stats = swarm.get_swarm_stats();
// Verify health calculation
assert_eq!(stats.total_agents, 2);
assert_eq!(stats.available_agents, 2);
// All agents available = healthy
let is_healthy = stats.total_agents > 0 && stats.available_agents > 0;
assert!(is_healthy);
}
#[tokio::test]
async fn test_swarm_health_status_degraded() {
// Create swarm with some unavailable agents
let swarm = Arc::new(SwarmCoordinator::new());
let available_profile = create_test_profile("agent-1", 0.95, 0.3);
let mut unavailable_profile = create_test_profile("agent-2", 0.85, 0.5);
unavailable_profile.availability = false;
swarm.register_agent(available_profile).ok();
swarm.register_agent(unavailable_profile).ok();
let stats = swarm.get_swarm_stats();
// Verify health calculation
assert_eq!(stats.total_agents, 2);
assert_eq!(stats.available_agents, 1);
// Some unavailable = degraded
let is_degraded = stats.total_agents > 0 && stats.available_agents < stats.total_agents;
assert!(is_degraded);
}
#[tokio::test]
async fn test_swarm_health_status_no_agents() {
// Create empty swarm
let swarm = Arc::new(SwarmCoordinator::new());
let stats = swarm.get_swarm_stats();
// Verify no agents
assert_eq!(stats.total_agents, 0);
assert_eq!(stats.available_agents, 0);
}
#[tokio::test]
async fn test_swarm_statistics_load_calculation() {
// Create swarm with varied load profiles
let swarm = Arc::new(SwarmCoordinator::new());
let light_load = create_test_profile("agent-1", 0.95, 0.1);
let medium_load = create_test_profile("agent-2", 0.85, 0.5);
let high_load = create_test_profile("agent-3", 0.80, 0.9);
swarm.register_agent(light_load).ok();
swarm.register_agent(medium_load).ok();
swarm.register_agent(high_load).ok();
let stats = swarm.get_swarm_stats();
// Verify load calculation (average of 0.1, 0.5, 0.9 = 0.5)
assert_eq!(stats.total_agents, 3);
assert!(stats.avg_load > 0.4 && stats.avg_load < 0.6);
}
#[tokio::test]
async fn test_swarm_statistics_success_rate_variance() {
// Create swarm with different success rates
let swarm = Arc::new(SwarmCoordinator::new());
let high_success = create_test_profile("agent-1", 0.99, 0.2);
let medium_success = create_test_profile("agent-2", 0.50, 0.3);
let low_success = create_test_profile("agent-3", 0.10, 0.1);
swarm.register_agent(high_success).ok();
swarm.register_agent(medium_success).ok();
swarm.register_agent(low_success).ok();
let stats = swarm.get_swarm_stats();
// Verify all agents registered despite variance
assert_eq!(stats.total_agents, 3);
assert_eq!(stats.available_agents, 3);
}
#[tokio::test]
async fn test_swarm_agent_availability_transitions() {
// Create swarm with available agent
let swarm = Arc::new(SwarmCoordinator::new());
let mut profile = create_test_profile("agent-1", 0.95, 0.3);
swarm.register_agent(profile.clone()).ok();
// Verify initial state
let mut stats = swarm.get_swarm_stats();
assert_eq!(stats.available_agents, 1);
// Mark agent unavailable
profile.availability = false;
swarm.register_agent(profile).ok();
// Verify transition
stats = swarm.get_swarm_stats();
assert_eq!(stats.available_agents, 0);
}
#[tokio::test]
async fn test_swarm_unregister_agent() {
// Create swarm with agent
let swarm = Arc::new(SwarmCoordinator::new());
let profile = create_test_profile("agent-1", 0.95, 0.3);
swarm.register_agent(profile).ok();
let mut stats = swarm.get_swarm_stats();
assert_eq!(stats.total_agents, 1);
// Unregister agent
swarm.unregister_agent("agent-1").ok();
// Verify removal
stats = swarm.get_swarm_stats();
assert_eq!(stats.total_agents, 0);
}
#[tokio::test]
async fn test_swarm_task_assignment_selects_best_agent() {
// Create swarm with agents of different quality
let swarm = Arc::new(SwarmCoordinator::new());
let poor_agent = create_test_profile("agent-poor", 0.50, 0.9); // Low success, high load
let good_agent = create_test_profile("agent-good", 0.95, 0.2); // High success, low load
swarm.register_agent(poor_agent).ok();
swarm.register_agent(good_agent).ok();
// Score: success_rate / (1.0 + load)
// agent-poor: 0.50 / (1.0 + 0.9) = 0.50 / 1.9 ≈ 0.26
// agent-good: 0.95 / (1.0 + 0.2) = 0.95 / 1.2 ≈ 0.79
// agent-good should be selected
// Verify agent-good has better score
let poor_score = 0.50 / (1.0 + 0.9);
let good_score = 0.95 / (1.0 + 0.2);
assert!(good_score > poor_score);
}
#[tokio::test]
async fn test_swarm_statistics_consistency() {
// Test that statistics remain consistent with multiple operations
let swarm = Arc::new(SwarmCoordinator::new());
// Initial state
let mut stats = swarm.get_swarm_stats();
assert_eq!(stats.total_agents, 0);
// Add agents
for i in 0..5 {
let profile = create_test_profile(&format!("agent-{}", i), 0.85, 0.3);
swarm.register_agent(profile).ok();
}
stats = swarm.get_swarm_stats();
assert_eq!(stats.total_agents, 5);
assert_eq!(stats.available_agents, 5);
// Update one agent to unavailable
let mut profile = create_test_profile("agent-0", 0.85, 0.3);
profile.availability = false;
swarm.register_agent(profile).ok();
stats = swarm.get_swarm_stats();
assert_eq!(stats.total_agents, 5);
assert_eq!(stats.available_agents, 4);
// Remove one agent
swarm.unregister_agent("agent-1").ok();
stats = swarm.get_swarm_stats();
assert_eq!(stats.total_agents, 4);
assert_eq!(stats.available_agents, 3);
}
#[tokio::test]
async fn test_swarm_large_agent_pool() {
// Test swarm behavior with larger agent pool
let swarm = Arc::new(SwarmCoordinator::new());
// Register 50 agents with varied metrics
for i in 0..50 {
let success_rate = if i % 3 == 0 {
0.95
} else if i % 3 == 1 {
0.75
} else {
0.55
};
let load = (i as f64 % 10.0) / 10.0;
let profile = create_test_profile(&format!("agent-{}", i), success_rate, load);
swarm.register_agent(profile).ok();
}
let stats = swarm.get_swarm_stats();
// Verify all registered
assert_eq!(stats.total_agents, 50);
assert_eq!(stats.available_agents, 50);
// Verify average load is reasonable
assert!(stats.avg_load > 0.0 && stats.avg_load < 1.0);
}
#[tokio::test]
async fn test_swarm_empty_after_unregister_all() {
// Create swarm with agents
let swarm = Arc::new(SwarmCoordinator::new());
for i in 0..3 {
let profile = create_test_profile(&format!("agent-{}", i), 0.85, 0.3);
swarm.register_agent(profile).ok();
}
let mut stats = swarm.get_swarm_stats();
assert_eq!(stats.total_agents, 3);
// Unregister all
for i in 0..3 {
swarm.unregister_agent(&format!("agent-{}", i)).ok();
}
stats = swarm.get_swarm_stats();
assert_eq!(stats.total_agents, 0);
assert_eq!(stats.available_agents, 0);
}