pg-api 0.2.0

A high-performance PostgreSQL REST API driver with rate limiting, connection pooling, and observability
//! Testes de integração para execução de queries
//!
//! Estes testes verificam a execução de queries SQL através da API.

use std::time::Duration;

/// Helper para criar cliente HTTP
fn create_client() -> reqwest::Client {
    reqwest::Client::builder()
        .timeout(Duration::from_secs(30))
        .build()
        .unwrap()
}

/// Testa execução de query SELECT simples
#[tokio::test]
async fn test_select_query() {
    let client = create_client();
    
    let result = tokio::time::timeout(
        Duration::from_secs(10),
        client
            .post("http://localhost:8580/v1/query")
            .header("X-API-Key", "sk_test_key")
            .json(&serde_json::json!({
                "database": "postgres",
                "query": "SELECT 1 as num, 'test' as str"
            }))
            .send()
    ).await;
    
    let response = match result {
        Ok(Ok(resp)) => resp,
        _ => {
            println!("⚠️  Servidor não está rodando, pulando teste");
            return;
        }
    };
    
    // Pode retornar 200 (sucesso) ou 401/403 (auth/permission)
    let status = response.status();
    assert!(
        status == 200 || status == 401 || status == 403,
        "Status inesperado: {}",
        status
    );
    
    if status == 200 {
        let body: serde_json::Value = response.json().await.unwrap();
        assert!(body.get("success").is_some());
        assert!(body.get("data").is_some() || body.get("error").is_some());
    }
}

/// Testa query com parâmetros
#[tokio::test]
async fn test_query_with_params() {
    let client = create_client();
    
    let result = tokio::time::timeout(
        Duration::from_secs(10),
        client
            .post("http://localhost:8580/v1/query")
            .header("X-API-Key", "sk_test_key")
            .json(&serde_json::json!({
                "database": "postgres",
                "query": "SELECT $1::int as num, $2::text as str",
                "params": [42, "hello"]
            }))
            .send()
    ).await;
    
    let response = match result {
        Ok(Ok(resp)) => resp,
        _ => {
            println!("⚠️  Servidor não está rodando, pulando teste");
            return;
        }
    };
    
    let status = response.status();
    assert!(
        status == 200 || status == 401 || status == 403,
        "Status inesperado: {}",
        status
    );
}

/// Testa execução de batch
#[tokio::test]
async fn test_batch_queries() {
    let client = create_client();
    
    let result = tokio::time::timeout(
        Duration::from_secs(10),
        client
            .post("http://localhost:8580/v1/batch")
            .header("X-API-Key", "sk_test_key")
            .json(&serde_json::json!([
                {
                    "database": "postgres",
                    "query": "SELECT 1 as n"
                },
                {
                    "database": "postgres",
                    "query": "SELECT 2 as n"
                }
            ]))
            .send()
    ).await;
    
    let response = match result {
        Ok(Ok(resp)) => resp,
        _ => {
            println!("⚠️  Servidor não está rodando, pulando teste");
            return;
        }
    };
    
    let status = response.status();
    assert!(
        status == 200 || status == 401 || status == 403,
        "Status inesperado: {}",
        status
    );
}

/// Testa execução de transação
#[tokio::test]
async fn test_transaction() {
    let client = create_client();
    
    let result = tokio::time::timeout(
        Duration::from_secs(10),
        client
            .post("http://localhost:8580/v1/transaction")
            .header("X-API-Key", "sk_test_key")
            .json(&serde_json::json!({
                "database": "postgres",
                "queries": [
                    {"query": "SELECT 1 as step"},
                    {"query": "SELECT 2 as step"}
                ]
            }))
            .send()
    ).await;
    
    let response = match result {
        Ok(Ok(resp)) => resp,
        _ => {
            println!("⚠️  Servidor não está rodando, pulando teste");
            return;
        }
    };
    
    let status = response.status();
    assert!(
        status == 200 || status == 401 || status == 403,
        "Status inesperado: {}",
        status
    );
}

/// Testa query inválida (deve retornar erro)
#[tokio::test]
async fn test_invalid_query() {
    let client = create_client();
    
    let result = tokio::time::timeout(
        Duration::from_secs(10),
        client
            .post("http://localhost:8580/v1/query")
            .header("X-API-Key", "sk_test_key")
            .json(&serde_json::json!({
                "database": "postgres",
                "query": "INVALID SQL SYNTAX HERE"
            }))
            .send()
    ).await;
    
    let response = match result {
        Ok(Ok(resp)) => resp,
        _ => {
            println!("⚠️  Servidor não está rodando, pulando teste");
            return;
        }
    };
    
    // Query inválida deve retornar erro (200 com success=false ou 400)
    let status = response.status();
    if status == 200 {
        let body: serde_json::Value = response.json().await.unwrap();
        // Deve indicar erro na estrutura da resposta
        if let Some(success) = body.get("success") {
            assert!(!success.as_bool().unwrap(), "Query inválida deve retornar success=false");
        }
    }
}

/// Testa query em database inexistente
#[tokio::test]
async fn test_nonexistent_database() {
    let client = create_client();
    
    let result = tokio::time::timeout(
        Duration::from_secs(10),
        client
            .post("http://localhost:8580/v1/query")
            .header("X-API-Key", "sk_test_key")
            .json(&serde_json::json!({
                "database": "nonexistent_db_xyz",
                "query": "SELECT 1"
            }))
            .send()
    ).await;
    
    let response = match result {
        Ok(Ok(resp)) => resp,
        _ => {
            println!("⚠️  Servidor não está rodando, pulando teste");
            return;
        }
    };
    
    // Deve retornar erro (403 forbidden ou 404 not found)
    let status = response.status();
    assert!(
        status == 200 || status == 401 || status == 403 || status == 404,
        "Status inesperado: {}",
        status
    );
}

/// Testa estrutura da resposta de query
#[tokio::test]
async fn test_query_response_structure() {
    let client = create_client();
    
    let result = tokio::time::timeout(
        Duration::from_secs(10),
        client
            .post("http://localhost:8580/v1/query")
            .header("X-API-Key", "sk_test_key")
            .json(&serde_json::json!({
                "database": "postgres",
                "query": "SELECT version()"
            }))
            .send()
    ).await;
    
    let response = match result {
        Ok(Ok(resp)) => resp,
        _ => {
            println!("⚠️  Servidor não está rodando, pulando teste");
            return;
        }
    };
    
    if response.status() == 200 {
        let body: serde_json::Value = response.json().await.unwrap();
        
        // Verifica estrutura padrão
        assert!(body.get("success").is_some(), "Resposta deve ter campo 'success'");
        assert!(body.get("metadata").is_some(), "Resposta deve ter campo 'metadata'");
        
        if body["success"].as_bool().unwrap_or(false) {
            assert!(body.get("data").is_some(), "Resposta de sucesso deve ter campo 'data'");
            
            let data = &body["data"];
            assert!(data.get("rows").is_some(), "Data deve ter campo 'rows'");
            assert!(data.get("columns").is_some() || data.get("fields").is_some(), 
                "Data deve ter campo 'columns' ou 'fields'");
        } else {
            assert!(body.get("error").is_some(), "Resposta de erro deve ter campo 'error'");
        }
    }
}

/// Testa listagem de databases
#[tokio::test]
async fn test_list_databases() {
    let client = create_client();
    
    let result = tokio::time::timeout(
        Duration::from_secs(10),
        client
            .get("http://localhost:8580/v1/databases")
            .header("X-API-Key", "sk_test_key")
            .send()
    ).await;
    
    let response = match result {
        Ok(Ok(resp)) => resp,
        _ => {
            println!("⚠️  Servidor não está rodando, pulando teste");
            return;
        }
    };
    
    let status = response.status();
    assert!(
        status == 200 || status == 401 || status == 403,
        "Status inesperado: {}",
        status
    );
}

/// Testa obtenção de schema
#[tokio::test]
async fn test_get_schema() {
    let client = create_client();
    
    let result = tokio::time::timeout(
        Duration::from_secs(10),
        client
            .get("http://localhost:8580/v1/databases/postgres/schema")
            .header("X-API-Key", "sk_test_key")
            .send()
    ).await;
    
    let response = match result {
        Ok(Ok(resp)) => resp,
        _ => {
            println!("⚠️  Servidor não está rodando, pulando teste");
            return;
        }
    };
    
    let status = response.status();
    assert!(
        status == 200 || status == 401 || status == 403,
        "Status inesperado: {}",
        status
    );
}