dinah 0.1.0

Rust-first modular runtime and high-performance compute foundation
Documentation
use super::{
    CoreConfig, CoreConfigError, CoreId, IdError, LifecycleState, Validate, CORE_API_VERSION,
    CORE_INVARIANTS,
};

#[test]
fn config_defaults_are_valid_and_conservative() {
    let config = CoreConfig::default();
    assert_eq!(config.max_workers(), 1);
    assert_eq!(config.max_batch_size(), 64);
    assert_eq!(config, CoreConfig::default_config());
    assert_eq!(config.validate(), Ok(()));
}

#[test]
fn config_rejects_zero_limits() {
    assert_eq!(CoreConfig::new(0, 1), Err(CoreConfigError::ZeroWorkers));
    assert_eq!(CoreConfig::new(1, 0), Err(CoreConfigError::ZeroBatchSize));
}

#[test]
fn config_reports_worker_error_before_batch_error() {
    assert_eq!(
        CoreConfig::new(0, 0),
        Err(CoreConfigError::ZeroWorkers),
        "when both limits are invalid, worker validation has precedence"
    );
}

#[test]
fn config_accepts_maximum_usize_limits() {
    let config = CoreConfig::new(usize::MAX, usize::MAX)
        .expect("non-zero limits should be accepted without arithmetic");
    assert_eq!(config.max_workers(), usize::MAX);
    assert_eq!(config.max_batch_size(), usize::MAX);
    assert_eq!(config.validate(), Ok(()));
}

#[test]
fn ids_reject_zero_and_preserve_nonzero_values() {
    assert_eq!(CoreId::new(0), Err(IdError));
    let id = CoreId::new(42).expect("non-zero ID should be accepted");
    assert_eq!(id.get(), 42);
    assert_eq!(CoreId::new(id.get()), Ok(id));
}

#[test]
fn ids_accept_maximum_u64_and_format_as_decimal() {
    let id = CoreId::new(u64::MAX).expect("maximum non-zero ID should be accepted");
    assert_eq!(id.get(), u64::MAX);
    assert_eq!(id.to_string(), u64::MAX.to_string());
}

#[test]
fn ids_support_ordering_and_hashing_contracts() {
    use std::collections::HashSet;

    let lower = CoreId::new(7).expect("non-zero ID");
    let higher = CoreId::new(8).expect("non-zero ID");
    assert!(lower < higher);

    let mut ids = HashSet::new();
    ids.insert(lower);
    ids.insert(CoreId::new(7).expect("same ID"));
    ids.insert(higher);
    assert_eq!(ids.len(), 2);
}

#[test]
fn lifecycle_transitions_are_explicit() {
    assert!(LifecycleState::Ready.can_transition_to(LifecycleState::Running));
    assert!(LifecycleState::Running.can_transition_to(LifecycleState::Stopping));
    assert!(LifecycleState::Stopping.can_transition_to(LifecycleState::Stopped));
    assert!(!LifecycleState::Stopped.can_transition_to(LifecycleState::Running));
    assert!(!LifecycleState::Running.can_transition_to(LifecycleState::Ready));
}

#[test]
fn every_lifecycle_state_is_copyable_and_hashable() {
    use std::collections::HashSet;
    use LifecycleState::{Ready, Running, Stopped, Stopping};

    let original = Running;
    let copied = original;
    assert_eq!(original, copied);

    let states = [Ready, Running, Stopping, Stopped];
    let unique: HashSet<_> = states.into_iter().collect();
    assert_eq!(unique.len(), 4);
}

#[test]
fn lifecycle_transition_matrix_matches_the_contract() {
    use LifecycleState::{Ready, Running, Stopped, Stopping};

    let cases = [
        (Ready, Ready, false),
        (Ready, Running, true),
        (Ready, Stopping, false),
        (Ready, Stopped, true),
        (Running, Ready, false),
        (Running, Running, false),
        (Running, Stopping, true),
        (Running, Stopped, false),
        (Stopping, Ready, false),
        (Stopping, Running, false),
        (Stopping, Stopping, false),
        (Stopping, Stopped, true),
        (Stopped, Ready, false),
        (Stopped, Running, false),
        (Stopped, Stopping, false),
        (Stopped, Stopped, false),
    ];

    for (current, next, expected) in cases {
        assert_eq!(
            current.can_transition_to(next),
            expected,
            "unexpected transition: {current:?} -> {next:?}"
        );
    }
}

#[test]
fn core_error_conversions_and_messages_are_stable() {
    use super::CoreError;

    assert_eq!(
        CoreConfigError::ZeroWorkers.to_string(),
        "max_workers must be greater than zero"
    );
    assert_eq!(
        CoreConfigError::ZeroBatchSize.to_string(),
        "max_batch_size must be greater than zero"
    );
    assert_eq!(
        CoreError::from(CoreConfigError::ZeroWorkers),
        CoreError::InvalidConfiguration
    );
    assert_eq!(IdError.to_string(), "core identifier must be non-zero");
    assert_eq!(
        CoreError::InvalidState.to_string(),
        "operation is invalid for the current state"
    );
    assert_eq!(CoreError::InvalidId.to_string(), "invalid core identifier");
}

#[test]
fn core_api_version_is_defined() {
    assert_eq!(CORE_API_VERSION, 1);
}

#[test]
fn validate_trait_checks_configuration_contract() {
    let config = CoreConfig::default();
    assert_eq!(Validate::validate(&config), Ok(()));
}

#[test]
fn validate_trait_preserves_specific_configuration_errors() {
    let invalid = CoreConfig::new(0, 1);
    assert_eq!(invalid, Err(CoreConfigError::ZeroWorkers));

    let config = CoreConfig::new(2, 3).expect("valid limits");
    assert_eq!(Validate::validate(&config), Ok(()));
}

#[test]
fn core_invariants_are_documented_in_api() {
    assert_eq!(CORE_INVARIANTS.len(), 4);
    assert!(CORE_INVARIANTS
        .iter()
        .all(|invariant| !invariant.is_empty()));
}