asupersync 0.3.4

Spec-first, cancel-correct, capability-secure async runtime for Rust.
Documentation
//! Metric cardinality limits audit test.
//!
//! **AUDIT SCOPE**: Verifies OTLP best practice compliance for metric cardinality limits
//! to prevent DoS via metric explosion.
//!
//! **OTLP BEST PRACTICE REQUIREMENT**:
//! - When >1000 distinct attribute combinations for a single metric: BOUNDED MEMORY (correct)
//! - NOT unlimited growth which causes memory exhaustion DoS (defect)
//! - Default should be safe: drop new metrics rather than grow unbounded
//!
//! **CRITICAL**: Unbounded cardinality allows attackers to exhaust memory via metric explosion.

#![cfg(all(test, feature = "metrics"))]

use crate::observability::otel::{CardinalityOverflow, MetricsConfig};

/// **AUDIT TEST**: Verify default cardinality limits prevent memory exhaustion.
///
/// **SCENARIO**: Default configuration uses safe bounded memory limits.
/// **REQUIREMENT**: max_cardinality=1000, strategy=Drop (bounded memory).
/// **ASSESSMENT**: SOUND - prevents DoS via metric explosion.
#[test]
fn audit_default_cardinality_limits_prevent_dos() {
    println!("🔍 AUDIT: Default cardinality limits DoS protection");

    let config = MetricsConfig::default();

    println!("📊 Default cardinality configuration:");
    println!("   max_cardinality: {}", config.max_cardinality);
    println!("   max_metrics: {}", config.max_metrics);
    println!("   overflow_strategy: {:?}", config.overflow_strategy);

    // OTLP best practice: limit attribute combinations per metric
    assert_eq!(
        config.max_cardinality, 1000,
        "DEFAULT CARDINALITY VIOLATION: max_cardinality should be 1000 for bounded memory"
    );

    // Prevent metric name explosion
    assert_eq!(
        config.max_metrics, 4096,
        "METRIC NAME CAP MISSING: max_metrics should limit distinct metric names"
    );

    // Safe overflow strategy prevents memory exhaustion
    assert_eq!(
        config.overflow_strategy,
        CardinalityOverflow::Drop,
        "UNSAFE OVERFLOW STRATEGY: default should Drop (bounded) not Warn (unbounded)"
    );

    println!("✅ CARDINALITY LIMITS: Default configuration prevents metric explosion DoS");
    println!(
        "   ✓ Bounded attribute combinations: {} per metric",
        config.max_cardinality
    );
    println!("   ✓ Bounded metric names: {}", config.max_metrics);
    println!("   ✓ Safe overflow strategy: Drop (not unbounded growth)");
}

/// **AUDIT TEST**: Verify unsafe overflow strategies are available but not default.
///
/// **SCENARIO**: Warn strategy allows unbounded growth (potentially dangerous).
/// **REQUIREMENT**: Available for debugging but NOT the default.
/// **ASSESSMENT**: SOUND - unsafe option available but requires explicit opt-in.
#[test]
fn audit_unsafe_overflow_strategies_not_default() {
    println!("🔍 AUDIT: Unsafe overflow strategies require explicit opt-in");

    // Verify unsafe strategies exist for debugging
    let warn_config = MetricsConfig::default().with_overflow_strategy(CardinalityOverflow::Warn);

    assert_eq!(warn_config.overflow_strategy, CardinalityOverflow::Warn);

    let aggregate_config =
        MetricsConfig::default().with_overflow_strategy(CardinalityOverflow::Aggregate);

    assert_eq!(
        aggregate_config.overflow_strategy,
        CardinalityOverflow::Aggregate
    );

    // But verify default is safe
    let default_config = MetricsConfig::default();
    assert_eq!(
        default_config.overflow_strategy,
        CardinalityOverflow::Drop,
        "SECURITY VIOLATION: Default overflow strategy must be Drop (bounded memory)"
    );

    println!("✅ OVERFLOW STRATEGY SAFETY: Unsafe options available but require explicit opt-in");
    println!("   ✓ Warn strategy: Available (unbounded - for debugging only)");
    println!("   ✓ Aggregate strategy: Available (bounded fallback)");
    println!("   ✓ Drop strategy: DEFAULT (bounded - memory safe)");
}

/// **AUDIT TEST**: Verify cardinality configuration is tunable.
///
/// **SCENARIO**: Operators can adjust limits for their environment.
/// **REQUIREMENT**: Configuration provides reasonable defaults and tuning options.
/// **ASSESSMENT**: SOUND - configurable but safe defaults.
#[test]
fn audit_cardinality_configuration_tunability() {
    println!("🔍 AUDIT: Cardinality limits are tunable with safe defaults");

    // Test custom configuration
    let custom_config = MetricsConfig::new()
        .with_max_cardinality(500)
        .with_max_metrics(2048)
        .with_overflow_strategy(CardinalityOverflow::Aggregate);

    assert_eq!(custom_config.max_cardinality, 500);
    assert_eq!(custom_config.max_metrics, 2048);
    assert_eq!(
        custom_config.overflow_strategy,
        CardinalityOverflow::Aggregate
    );

    // Verify edge cases
    let minimal_config = MetricsConfig::default().with_max_cardinality(1);
    assert_eq!(minimal_config.max_cardinality, 1);

    let high_config = MetricsConfig::default().with_max_cardinality(10000);
    assert_eq!(high_config.max_cardinality, 10000);

    println!("✅ CONFIGURATION TUNABILITY: Cardinality limits are adjustable");
    println!(
        "   ✓ Custom cardinality: {} (operator choice)",
        custom_config.max_cardinality
    );
    println!(
        "   ✓ Custom metric cap: {} (operator choice)",
        custom_config.max_metrics
    );
    println!(
        "   ✓ Custom strategy: {:?} (operator choice)",
        custom_config.overflow_strategy
    );
}

/// **AUDIT TEST**: Verify OTLP best practice compliance.
///
/// **SCENARIO**: Implementation follows OTLP exporter guidance for cardinality.
/// **REQUIREMENT**: Bounded memory, configurable limits, safe defaults.
/// **ASSESSMENT**: SOUND - full compliance with OTLP best practices.
#[test]
fn audit_otlp_best_practice_compliance() {
    println!("🔍 AUDIT: OTLP cardinality best practice compliance");

    let config = MetricsConfig::default();

    // OTLP Best Practice 1: Bounded memory
    assert!(
        config.max_cardinality > 0 && config.max_cardinality <= 10000,
        "OTLP VIOLATION: cardinality must be bounded and reasonable (1-10k)"
    );

    // OTLP Best Practice 2: Safe overflow handling
    assert!(
        matches!(
            config.overflow_strategy,
            CardinalityOverflow::Drop | CardinalityOverflow::Aggregate
        ),
        "OTLP VIOLATION: overflow strategy must prevent unbounded growth"
    );

    // OTLP Best Practice 3: Metric name limits
    assert!(
        config.max_metrics > 0,
        "OTLP VIOLATION: metric name count must be bounded"
    );

    // OTLP Best Practice 4: Configurable limits
    let tuned = MetricsConfig::default().with_max_cardinality(2000);
    assert_eq!(tuned.max_cardinality, 2000, "Configuration must be tunable");

    println!("✅ OTLP BEST PRACTICE COMPLIANCE: Full compliance verified");
    println!(
        "   ✓ Bounded memory: max {} attributes per metric",
        config.max_cardinality
    );
    println!(
        "   ✓ Bounded metrics: max {} distinct metric names",
        config.max_metrics
    );
    println!(
        "   ✓ Safe overflow: {:?} strategy (prevents DoS)",
        config.overflow_strategy
    );
    println!("   ✓ Configurable: Operators can tune limits for environment");
}