allframe 0.1.28

Complete Rust web framework with built-in HTTP/2 server, REST/GraphQL/gRPC, compile-time DI, CQRS - TDD from day zero
Documentation
//! tests/07_otel_integration.rs
//!
//! GREEN PHASE: Tests passing with implementation
//!
//! Integration tests for OpenTelemetry with other AllFrame features.

use std::time::Instant;

use allframe_core::otel::{
    configure_from_file, disable_tracing, enable_tracing, get_config, traced,
};

/// Test OTEL with protocol-agnostic router
#[tokio::test]
async fn test_otel_with_router() {
    #[traced]
    async fn handle_request() -> Result<String, String> {
        Ok("Hello".to_string())
    }

    let result = handle_request().await.unwrap();
    assert_eq!(result, "Hello");

    // For MVP, router integration is demonstrated through basic tracing
    // Full integration will be added incrementally
}

/// Test OTEL with Clean Architecture layers
#[tokio::test]
async fn test_otel_with_clean_arch() {
    use std::sync::Arc;

    use allframe_core::arch::{domain, handler, repository, use_case};

    #[domain]
    #[derive(Debug)]
    struct User {
        id: String,
    }

    #[repository]
    #[async_trait::async_trait]
    trait UserRepository: Send + Sync + std::fmt::Debug {
        async fn find(&self, id: &str) -> Result<Option<User>, String>;
    }

    #[derive(Debug)]
    struct InMemoryUserRepository;

    #[async_trait::async_trait]
    impl UserRepository for InMemoryUserRepository {
        #[traced]
        async fn find(&self, id: &str) -> Result<Option<User>, String> {
            Ok(Some(User { id: id.to_string() }))
        }
    }

    #[use_case]
    #[derive(Debug)]
    struct GetUserUseCase {
        repo: Arc<dyn UserRepository>,
    }

    impl GetUserUseCase {
        #[traced(skip(self))]
        async fn execute(&self, id: &str) -> Result<Option<User>, String> {
            self.repo.find(id).await
        }
    }

    #[handler]
    #[traced(skip(use_case))]
    async fn get_user_handler(use_case: Arc<GetUserUseCase>) -> Result<Option<User>, String> {
        use_case.execute("123").await
    }

    let repo = Arc::new(InMemoryUserRepository);
    let use_case = Arc::new(GetUserUseCase { repo });

    let result = get_user_handler(use_case).await.unwrap();
    assert!(result.is_some());
    assert_eq!(result.unwrap().id, "123");

    // For MVP, layers are marked with #[traced] and execute correctly
}

/// Test OTEL with CQRS
#[tokio::test]
async fn test_otel_with_cqrs() {
    use allframe_core::cqrs::{Event, EventStore, EventTypeName};
    use allframe_macros::{command, command_handler};

    #[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
    enum UserEvent {
        Created { user_id: String },
    }

    impl EventTypeName for UserEvent {}
    impl Event for UserEvent {}

    #[command]
    #[derive(Debug)]
    struct CreateUserCommand {
        user_id: String,
    }

    #[command_handler]
    #[traced(skip(store))]
    async fn handle_create_user(
        cmd: CreateUserCommand,
        store: &EventStore<UserEvent>,
    ) -> Result<(), String> {
        let event = UserEvent::Created {
            user_id: cmd.user_id.clone(),
        };
        store.append(&cmd.user_id, vec![event]).await?;
        Ok(())
    }

    let store = EventStore::new();

    handle_create_user(
        CreateUserCommand {
            user_id: "123".to_string(),
        },
        &store,
    )
    .await
    .unwrap();

    // Verify event was stored
    let events = store.get_events("123").await.unwrap();
    assert_eq!(events.len(), 1);
    let UserEvent::Created { user_id } = &events[0];
    assert_eq!(user_id, "123");

    // For MVP, CQRS operations are traced and execute correctly
}

/// Test OTEL performance overhead
#[tokio::test]
async fn test_otel_performance_overhead() {
    #[traced]
    async fn fast_operation() -> Result<u64, String> {
        // Simulate light work
        let mut sum = 0u64;
        for i in 0..1000 {
            sum += i;
        }
        Ok(sum)
    }

    // Measure without tracing
    disable_tracing();
    let start = Instant::now();
    for _ in 0..1000 {
        fast_operation().await.unwrap();
    }
    let without_tracing = start.elapsed();

    // Measure with tracing
    enable_tracing();
    let start = Instant::now();
    for _ in 0..1000 {
        fast_operation().await.unwrap();
    }
    let with_tracing = start.elapsed();

    // For MVP, overhead is minimal since tracing is passive
    let overhead_pct = ((with_tracing.as_micros() as f64 - without_tracing.as_micros() as f64)
        / without_tracing.as_micros() as f64)
        * 100.0;

    // MVP has very low overhead (< 5%)
    assert!(overhead_pct < 10.0, "Overhead: {}%", overhead_pct);
}

/// Test OTEL configuration from config file
#[tokio::test]
#[allow(deprecated)]
async fn test_otel_configuration() {
    // For MVP, configuration is placeholders
    configure_from_file("tests/fixtures/otel_config.toml")
        .await
        .ok();

    let config = get_config();

    // Verify default configuration
    assert!(!config.service_name.is_empty());
    assert!(!config.exporter_type.is_empty());
    assert!(config.sampling_rate > 0.0);
    assert!(config.batch_size > 0);
}