tideway 0.7.17

A batteries-included Rust web framework built on Axum for building SaaS applications quickly
Documentation
#[cfg(feature = "metrics")]
mod tests {
    use std::sync::Arc;
    use tideway::testing::get as test_get;
    use tideway::{App, ConfigBuilder, MetricsCollector};

    #[tokio::test]
    async fn test_metrics_collector_creation() {
        let collector = MetricsCollector::new();
        assert!(collector.is_ok());
    }

    #[tokio::test]
    async fn test_metrics_collector_recording() {
        let collector = Arc::new(MetricsCollector::new().unwrap());

        // Record some requests
        collector.record_request(
            "GET",
            "/api/test",
            200,
            std::time::Duration::from_millis(50),
        );
        collector.record_request(
            "POST",
            "/api/test",
            201,
            std::time::Duration::from_millis(100),
        );
        collector.record_request(
            "GET",
            "/api/test",
            404,
            std::time::Duration::from_millis(10),
        );

        // Increment in-flight
        collector.increment_in_flight();
        assert_eq!(collector.http_requests_in_flight.get(), 1);

        collector.decrement_in_flight();
        assert_eq!(collector.http_requests_in_flight.get(), 0);
    }

    #[tokio::test]
    async fn test_metrics_endpoint_returns_prometheus_format() {
        use tideway::MetricsConfig;

        let config = ConfigBuilder::new()
            .with_metrics(
                MetricsConfig::builder()
                    .enabled(true)
                    .path("/metrics")
                    .build(),
            )
            .build()
            .unwrap();

        let app = App::with_config(config).into_router();

        let response = test_get(app, "/metrics").execute().await.assert_ok();

        // Verify it returns Prometheus text format
        let body = response.body_string().await;
        // Prometheus metrics should contain HELP and TYPE comments
        assert!(
            body.contains("# HELP") || body.contains("# TYPE") || body.is_empty(),
            "Expected Prometheus format, got: {}",
            body
        );
    }

    #[tokio::test]
    async fn test_metrics_endpoint_custom_path() {
        use tideway::MetricsConfig;

        let config = ConfigBuilder::new()
            .with_metrics(
                MetricsConfig::builder()
                    .enabled(true)
                    .path("/custom/metrics")
                    .build(),
            )
            .build()
            .unwrap();

        let app = App::with_config(config).into_router();

        // Should be available at custom path
        test_get(app.clone(), "/custom/metrics")
            .execute()
            .await
            .assert_ok();

        // Should NOT be at default path
        test_get(app, "/metrics").execute().await.assert_not_found();
    }

    #[tokio::test]
    async fn test_metrics_endpoint_with_app_builder() {
        use tideway::MetricsConfig;

        let config = ConfigBuilder::new()
            .with_metrics(
                MetricsConfig::builder()
                    .enabled(true)
                    .path("/metrics")
                    .build(),
            )
            .build()
            .unwrap();

        let app = App::builder().with_config(config).build().into_router();

        test_get(app, "/metrics").execute().await.assert_ok();
    }

    #[tokio::test]
    async fn test_metrics_endpoint_integration() {
        use tideway::MetricsConfig;

        let config = ConfigBuilder::new()
            .with_port(8080) // Use a valid port
            .with_metrics(
                MetricsConfig::builder()
                    .enabled(true)
                    .path("/metrics")
                    .build(),
            )
            .build()
            .unwrap();

        let _router = App::with_config(config).into_router();
    }

    #[tokio::test]
    async fn test_metrics_path_normalization() {
        let collector = Arc::new(MetricsCollector::new().unwrap());

        // Record requests with IDs
        collector.record_request(
            "GET",
            "/api/users/123",
            200,
            std::time::Duration::from_millis(50),
        );
        collector.record_request(
            "GET",
            "/api/users/456",
            200,
            std::time::Duration::from_millis(60),
        );

        let gathered = collector.registry().gather();
        assert!(!gathered.is_empty());
    }

    #[tokio::test]
    async fn test_metrics_disabled() {
        use tideway::MetricsConfig;

        let config = ConfigBuilder::new()
            .with_metrics(MetricsConfig::builder().enabled(false).build())
            .build()
            .unwrap();

        let _router = App::with_config(config).into_router();
    }
}