VesselRs 0.1.0

Shared memory primitives for multi-agent orchestration
Documentation
//! Integration tests for thegent-shm
//!
//! These tests verify the integration between components.

use thegent_shm::domain::entities::{CircuitBreaker, CommandLock, HealthScore};
use thegent_shm::domain::value_objects::{CircuitState, LockStatus, XpLevel};
use thegent_shm::ports::driven::{CircuitBreakerPort, CommandCachePort};
use thegent_shm::adapters::inmemory::{InMemoryCircuitBreaker, InMemoryCommandCache};

#[test]
fn test_circuit_breaker_integration() {
    let mut breaker = InMemoryCircuitBreaker::new();

    // Record 4 failures - should not open yet
    for _ in 0..4 {
        breaker.record_failure("api.example.com").unwrap();
    }

    let cb = breaker.get_breaker("api.example.com").unwrap();
    assert_eq!(cb.state, CircuitState::Closed);
    assert_eq!(cb.failures, 4);

    // 5th failure should open the circuit
    breaker.record_failure("api.example.com").unwrap();

    let cb = breaker.get_breaker("api.example.com").unwrap();
    assert_eq!(cb.state, CircuitState::Open);
    assert_eq!(cb.failures, 5);

    // Record success - should close the circuit
    breaker.record_success("api.example.com").unwrap();

    let cb = breaker.get_breaker("api.example.com").unwrap();
    assert_eq!(cb.state, CircuitState::Closed);
    assert_eq!(cb.failures, 0);
}

#[test]
fn test_command_cache_integration() {
    let mut cache = InMemoryCommandCache::new();

    // Acquire lock
    cache.acquire_lock("cmd_hash_1", 1234).unwrap();

    let lock = cache.get_lock("cmd_hash_1").unwrap();
    assert_eq!(lock.pid, 1234);
    assert!(lock.is_locked());

    // Try to acquire again with different PID - should fail
    let result = cache.acquire_lock("cmd_hash_1", 5678);
    assert!(result.is_err());

    // Release lock
    cache.release_lock("cmd_hash_1", 1234).unwrap();

    let lock = cache.get_lock("cmd_hash_1").unwrap();
    assert!(!lock.is_locked());

    // Now different PID can acquire
    cache.acquire_lock("cmd_hash_1", 5678).unwrap();
    let lock = cache.get_lock("cmd_hash_1").unwrap();
    assert_eq!(lock.pid, 5678);
}

#[test]
fn test_multiple_commands() {
    let mut cache = InMemoryCommandCache::new();

    // Acquire multiple different locks
    cache.acquire_lock("cmd_a", 1001).unwrap();
    cache.acquire_lock("cmd_b", 1002).unwrap();
    cache.acquire_lock("cmd_c", 1003).unwrap();

    let locks = cache.list_locks();
    assert_eq!(locks.len(), 3);

    // Release one and verify others remain
    cache.release_lock("cmd_b", 1002).unwrap();

    let locks = cache.list_locks();
    assert_eq!(locks.len(), 3); // Still 3, but one is unlocked

    let lock_a = cache.get_lock("cmd_a").unwrap();
    let lock_b = cache.get_lock("cmd_b").unwrap();
    let lock_c = cache.get_lock("cmd_c").unwrap();

    assert!(lock_a.is_locked());
    assert!(!lock_b.is_locked());
    assert!(lock_c.is_locked());
}

#[test]
fn test_circuit_breaker_list() {
    let mut breaker = InMemoryCircuitBreaker::new();

    breaker.record_failure("service1").unwrap();
    breaker.record_failure("service2").unwrap();
    breaker.record_failure("service3").unwrap();

    let breakers = breaker.list_breakers();
    assert_eq!(breakers.len(), 3);

    // Verify all are tracked
    assert!(breakers.iter().all(|cb| cb.failures == 1));
}

#[test]
fn test_health_score_integration() {
    let mut score = HealthScore::new();

    score.set_component("cpu", 0.95);
    score.set_component("memory", 0.85);
    score.set_component("disk", 0.90);
    score.set_component("network", 0.90);

    assert!((score.overall() - 0.9).abs() < 0.01);
    assert!(score.is_healthy()); // Above 0.8 threshold

    // Add a failing component
    score.set_component("cpu", 0.1);

    assert!(score.is_degraded()); // Between 0.5 and 0.8
    assert!(!score.is_healthy());
}

#[test]
fn test_xp_level_integration() {
    let mut level = XpLevel::new();

    // Initial state
    assert_eq!(level.level, 1);
    assert_eq!(level.total_xp, 0);
    assert_eq!(level.xp_to_next_level(), 1000);

    // Add 500 XP - should be at 500/1000
    level.add_xp(500);
    assert_eq!(level.level, 1);
    assert_eq!(level.xp_to_next_level(), 500);

    // Add 500 more XP - should level up to 2
    level.add_xp(500);
    assert_eq!(level.level, 2);
    assert_eq!(level.xp_to_next_level(), 1000);

    // Add 1000 more XP - level 3, need 1000 to reach level 4
    // At 2000 XP: at threshold for level 3, need 1000 more
    level.add_xp(1000);
    assert_eq!(level.level, 3);
    assert_eq!(level.xp_to_next_level(), 1000);

    // Add 1000 more XP - level 4, need 1000 to reach level 5
    // At 3000 XP: at threshold for level 4, need 1000 more
    level.add_xp(1000);
    assert_eq!(level.level, 4);
    assert_eq!(level.xp_to_next_level(), 1000);

    // Add 500 XP - level 4, need 500 more to reach level 5
    // At 3500 XP: 500 into level 4, need 500 more
    level.add_xp(500);
    assert_eq!(level.level, 4);
    assert_eq!(level.xp_to_next_level(), 500);
}

#[test]
fn test_value_object_equality() {
    use thegent_shm::domain::value_objects::{CircuitState, LockStatus, XpLevel};

    // Test CircuitState equality
    let state1 = CircuitState::Closed;
    let state2 = CircuitState::Closed;
    let state3 = CircuitState::Open;

    assert_eq!(state1, state2);
    assert_ne!(state1, state3);

    // Test LockStatus equality
    let status1 = LockStatus::Locked;
    let status2 = LockStatus::Locked;
    let status3 = LockStatus::Unlocked;

    assert_eq!(status1, status2);
    assert_ne!(status1, status3);

    // Test XpLevel equality (same values)
    let level1 = XpLevel::new();
    let level2 = XpLevel::new();

    assert_eq!(level1.level, level2.level);
    assert_eq!(level1.total_xp, level2.total_xp);
}