pg-api 0.2.0

A high-performance PostgreSQL REST API driver with rate limiting, connection pooling, and observability
//! Benchmarks para PG-API
//!
//! Executar com: `cargo bench`

use criterion::{black_box, criterion_group, criterion_main, Criterion, BenchmarkId};
use std::time::Duration;

/// Configuração dos benchmarks
fn configure_criterion() -> Criterion {
    Criterion::default()
        .measurement_time(Duration::from_secs(10))
        .warm_up_time(Duration::from_secs(3))
        .sample_size(100)
}

/// Benchmark de parsing de JSON (simulação)
fn bench_json_parsing(c: &mut Criterion) {
    let json_data = r#"{
        "database": "postgres",
        "query": "SELECT * FROM users WHERE id = $1",
        "params": [12345]
    }"#;

    c.bench_function("json_parsing_query_request", |b| {
        b.iter(|| {
            let _: serde_json::Value = serde_json::from_str(black_box(json_data)).unwrap();
        });
    });
}

/// Benchmark de serialização de resposta
fn bench_response_serialization(c: &mut Criterion) {
    use serde::Serialize;
    
    #[derive(Serialize)]
    struct MockResponse {
        success: bool,
        data: MockData,
        metadata: MockMetadata,
    }
    
    #[derive(Serialize)]
    struct MockData {
        rows: Vec<MockRow>,
        row_count: u64,
    }
    
    #[derive(Serialize)]
    struct MockRow {
        id: i32,
        name: String,
        email: String,
    }
    
    #[derive(Serialize)]
    struct MockMetadata {
        request_id: String,
        execution_time_ms: u64,
    }
    
    let response = MockResponse {
        success: true,
        data: MockData {
            rows: vec![
                MockRow { id: 1, name: "Alice".to_string(), email: "alice@example.com".to_string() },
                MockRow { id: 2, name: "Bob".to_string(), email: "bob@example.com".to_string() },
                MockRow { id: 3, name: "Charlie".to_string(), email: "charlie@example.com".to_string() },
            ],
            row_count: 3,
        },
        metadata: MockMetadata {
            request_id: "550e8400-e29b-41d4-a716-446655440000".to_string(),
            execution_time_ms: 42,
        },
    };

    c.bench_function("response_serialization", |b| {
        b.iter(|| {
            let _ = serde_json::to_string(black_box(&response)).unwrap();
        });
    });
}

/// Benchmark de validação de permissões
fn bench_permission_validation(c: &mut Criterion) {
    // Importar função do módulo database
    // Como não podemos importar diretamente, simulamos a lógica
    
    fn validate_query_simple(query: &str) -> &'static str {
        let query_upper = query.to_uppercase();
        
        if query_upper.starts_with("SELECT") {
            "SELECT"
        } else if query_upper.starts_with("INSERT") {
            "INSERT"
        } else if query_upper.starts_with("UPDATE") {
            "UPDATE"
        } else if query_upper.starts_with("DELETE") {
            "DELETE"
        } else {
            "OTHER"
        }
    }
    
    let queries = vec![
        "SELECT * FROM users WHERE id = 1",
        "INSERT INTO users (name) VALUES ('test')",
        "UPDATE users SET name = 'test' WHERE id = 1",
        "DELETE FROM users WHERE id = 1",
    ];

    c.bench_function("permission_validation", |b| {
        b.iter(|| {
            for query in &queries {
                let _ = validate_query_simple(black_box(query));
            }
        });
    });
}

/// Benchmark de extração de API key
fn bench_api_key_extraction(c: &mut Criterion) {
    use axum::http::HeaderMap;
    use axum::http::header::HeaderValue;
    
    fn extract_api_key(headers: &HeaderMap) -> Option<String> {
        headers
            .get("x-api-key")
            .or_else(|| headers.get("authorization"))
            .and_then(|h| h.to_str().ok())
            .map(|h| {
                if h.starts_with("Bearer ") {
                    h.strip_prefix("Bearer ").unwrap().to_string()
                } else {
                    h.to_string()
                }
            })
    }
    
    let mut headers = HeaderMap::new();
    headers.insert("x-api-key", HeaderValue::from_static("sk_test_123456789"));

    c.bench_function("api_key_extraction", |b| {
        b.iter(|| {
            let _ = extract_api_key(black_box(&headers));
        });
    });
}

/// Benchmark de substituição de parâmetros
fn bench_param_substitution(c: &mut Criterion) {
    fn substitute_params(query: &str, params: &[serde_json::Value]) -> String {
        let mut result = query.to_string();
        for (i, param) in params.iter().enumerate().rev() {
            let placeholder = format!("${}", i + 1);
            let value = match param {
                serde_json::Value::Null => "NULL".to_string(),
                serde_json::Value::Bool(b) => b.to_string(),
                serde_json::Value::Number(n) => n.to_string(),
                serde_json::Value::String(s) => format!("'{}'", s.replace("'", "''")),
                _ => param.to_string(),
            };
            result = result.replace(&placeholder, &value);
        }
        result
    }
    
    let query = "SELECT * FROM users WHERE id = $1 AND name = $2 AND active = $3";
    let params = vec![
        serde_json::json!(12345),
        serde_json::json!("John Doe"),
        serde_json::json!(true),
    ];

    c.bench_function("param_substitution", |b| {
        b.iter(|| {
            let _ = substitute_params(black_box(query), black_box(&params));
        });
    });
}

/// Benchmark de parsing de configuração
fn bench_config_parsing(c: &mut Criterion) {
    use serde::Deserialize;
    
    #[derive(Deserialize)]
    struct TestConfig {
        host: String,
        port: u16,
        log_level: String,
    }
    
    let config_json = r#"{
        "host": "127.0.0.1",
        "port": 8580,
        "log_level": "info"
    }"#;

    c.bench_function("config_parsing", |b| {
        b.iter(|| {
            let _: TestConfig = serde_json::from_str(black_box(config_json)).unwrap();
        });
    });
}

/// Benchmark de rate limiter (simulação)
fn bench_rate_limiter(c: &mut Criterion) {
    use std::collections::HashMap;
    use std::time::{SystemTime, UNIX_EPOCH};
    
    struct RateLimiter {
        requests: HashMap<String, Vec<u64>>,
        window_size: u64,
        max_requests: usize,
    }
    
    impl RateLimiter {
        fn new(window_size: u64, max_requests: usize) -> Self {
            Self {
                requests: HashMap::new(),
                window_size,
                max_requests,
            }
        }
        
        fn is_allowed(&mut self, key: &str) -> bool {
            let now = SystemTime::now()
                .duration_since(UNIX_EPOCH)
                .unwrap()
                .as_secs();
            
            let window_start = now - self.window_size;
            
            let timestamps = self.requests.entry(key.to_string()).or_default();
            timestamps.retain(|&t| t > window_start);
            
            if timestamps.len() < self.max_requests {
                timestamps.push(now);
                true
            } else {
                false
            }
        }
    }
    
    let mut limiter = RateLimiter::new(60, 100);

    c.bench_function("rate_limiter_check", |b| {
        b.iter(|| {
            let _ = limiter.is_allowed(black_box("test_key"));
        });
    });
}

/// Benchmark de comparação com diferentes tamanhos de payload
fn bench_payload_sizes(c: &mut Criterion) {
    let sizes = vec![10, 100, 1000];
    
    let mut group = c.benchmark_group("payload_serialization");
    
    for size in sizes {
        let data: Vec<i32> = (0..size).collect();
        
        group.bench_with_input(
            BenchmarkId::from_parameter(size),
            &data,
            |b, data| {
                b.iter(|| {
                    let _ = serde_json::to_string(black_box(data)).unwrap();
                });
            }
        );
    }
    
    group.finish();
}

criterion_group! {
    name = benches;
    config = configure_criterion();
    targets = 
        bench_json_parsing,
        bench_response_serialization,
        bench_permission_validation,
        bench_api_key_extraction,
        bench_param_substitution,
        bench_config_parsing,
        bench_rate_limiter,
        bench_payload_sizes
}

criterion_main!(benches);