elif-http 0.8.8

HTTP server core for the elif.rs LLM-friendly web framework
Documentation
//! CORRECT Integration Tests for elif-http
//!
//! This demonstrates the PROPER way to write integration tests for elif
//! using only framework native types and abstractions.
//!
//! ❌ DO NOT import axum types in tests
//! ✅ Use elif-testing utilities exclusively

use elif_http::{routing::router::Router, *};
use elif_testing::prelude::*;
use serde::{Deserialize, Serialize};
use serde_json::json;
use tokio::time::Duration;

#[derive(Serialize, Deserialize, Debug, PartialEq)]
struct User {
    id: u32,
    name: String,
    email: String,
}

#[derive(Deserialize)]
struct CreateUserRequest {
    name: String,
    email: String,
}

#[derive(Deserialize)]
struct UserQuery {
    limit: Option<u32>,
    offset: Option<u32>,
}

// ✅ CORRECT: Handler using pure framework abstractions
async fn get_users(request: ElifRequest) -> HttpResult<ElifResponse> {
    let query: UserQuery = request.query().unwrap_or(UserQuery {
        limit: Some(10),
        offset: Some(0),
    });

    let users = vec![
        User {
            id: 1,
            name: "Alice".to_string(),
            email: "alice@example.com".to_string(),
        },
        User {
            id: 2,
            name: "Bob".to_string(),
            email: "bob@example.com".to_string(),
        },
    ];

    let limited_users: Vec<User> = users
        .into_iter()
        .skip(query.offset.unwrap_or(0) as usize)
        .take(query.limit.unwrap_or(10) as usize)
        .collect();

    ElifResponse::ok().json(&json!({
        "users": limited_users,
        "total": 2,
        "limit": query.limit,
        "offset": query.offset
    }))
}

async fn create_user(request: ElifRequest) -> HttpResult<ElifResponse> {
    let create_req: CreateUserRequest = request.json()?;

    let user = User {
        id: 42,
        name: create_req.name,
        email: create_req.email,
    };

    ElifResponse::created().json(&json!({
        "user": user,
        "message": "User created successfully"
    }))
}

async fn health_check(_request: ElifRequest) -> HttpResult<ElifResponse> {
    ElifResponse::ok().json(&json!({
        "status": "healthy",
        "framework": "Elif.rs",
        "version": env!("CARGO_PKG_VERSION"),
        "timestamp": chrono::Utc::now().timestamp()
    }))
}

// ✅ CORRECT: Test setup using elif native types
fn create_test_router() -> Router {
    Router::new()
        .get("/health", health_check)
        .get("/users", get_users)
        .post("/users", create_user)
}

// ✅ CORRECT: Integration test using TestClient
#[tokio::test]
#[ignore] // Requires running server - enable for full integration testing
async fn test_health_endpoint() {
    let _router = create_test_router();

    // Start server on a test port (this would be implemented properly)
    // For now, we'll test the pattern

    let response = TestClient::with_base_url("http://localhost:3001")
        .get("/health")
        .send()
        .await
        .expect("Health check should succeed")
        .assert_success()
        .assert_header_exists("content-type");

    // ✅ CORRECT: JSON assertion using framework tools
    let health_data = response.json().expect("Should parse JSON");
    assert_eq!(health_data["status"], "healthy");
    assert_eq!(health_data["framework"], "Elif.rs");
}

// ✅ CORRECT: Testing with query parameters
#[tokio::test]
#[ignore] // Requires running server - enable for full integration testing
async fn test_users_with_query_params() {
    let response = TestClient::with_base_url("http://localhost:3001")
        .get("/users")
        .query("limit", "5")
        .query("offset", "10")
        .header("authorization", "Bearer test-token")
        .send()
        .await
        .expect("Users request should succeed")
        .assert_success();

    // ✅ CORRECT: Comprehensive JSON assertions
    response
        .assert_json_contains(json!({
            "limit": 5,
            "offset": 10
        }))
        .expect("Should contain query params");
}

// ✅ CORRECT: Testing POST with JSON body
#[tokio::test]
#[ignore] // Requires running server - enable for full integration testing
async fn test_create_user() {
    let user_data = json!({
        "name": "Charlie",
        "email": "charlie@example.com"
    });

    let response = TestClient::with_base_url("http://localhost:3001")
        .post("/users")
        .json(&user_data)
        .header("authorization", "Bearer admin-token")
        .send()
        .await
        .expect("Create user should succeed")
        .assert_status(201); // Created

    response
        .assert_json_contains(json!({
            "user": {
                "name": "Charlie",
                "email": "charlie@example.com"
            },
            "message": "User created successfully"
        }))
        .expect("Should contain user data");
}

// ✅ CORRECT: Error handling test
#[tokio::test]
#[ignore] // Requires running server - enable for full integration testing
async fn test_validation_error() {
    let invalid_data = json!({
        "name": "", // Invalid: empty name
        "email": "invalid-email" // Invalid: bad format
    });

    TestClient::with_base_url("http://localhost:3001")
        .post("/users")
        .json(&invalid_data)
        .send()
        .await
        .expect("Request should complete")
        .assert_status(422) // Validation error
        .assert_validation_error("name", "required")
        .expect("Should have name validation error");
}

// ✅ CORRECT: Authentication testing
#[tokio::test]
#[ignore] // Requires running server - enable for full integration testing
async fn test_authenticated_request() {
    TestClient::with_base_url("http://localhost:3001")
        .authenticated_with_token("valid-jwt-token")
        .get("/users")
        .send()
        .await
        .expect("Authenticated request should succeed")
        .assert_success()
        .assert_header_exists("authorization");
}

// ✅ CORRECT: Testing middleware behavior
#[tokio::test]
#[ignore] // Requires running server - enable for full integration testing
async fn test_cors_headers() {
    TestClient::with_base_url("http://localhost:3001")
        .get("/health")
        .header("Origin", "https://example.com")
        .send()
        .await
        .expect("CORS request should succeed")
        .assert_success()
        .assert_header("Access-Control-Allow-Origin", "*");
}

// ✅ CORRECT: Performance testing pattern
#[tokio::test]
#[ignore] // Requires running server - enable for full integration testing
async fn test_response_time() {
    use std::time::Instant;

    let start = Instant::now();

    TestClient::with_base_url("http://localhost:3001")
        .get("/health")
        .send()
        .await
        .expect("Performance test should succeed")
        .assert_success();

    let duration = start.elapsed();
    assert!(
        duration < Duration::from_millis(100),
        "Response should be fast"
    );
}

// ✅ CORRECT: Batch testing pattern
#[tokio::test]
#[ignore] // Requires running server - enable for full integration testing
async fn test_multiple_requests() {
    let client =
        TestClient::with_base_url("http://localhost:3001").authenticated_with_token("test-token");

    // Multiple requests using the same client
    for i in 1..=5 {
        let response = client
            .clone()
            .get("/users")
            .query("page", &i.to_string())
            .send()
            .await
            .expect("Batch request should succeed")
            .assert_success();

        let data = response.json().expect("Should parse JSON");
        println!(
            "Request {}: got {} users",
            i,
            data["users"].as_array().unwrap().len()
        );
    }
}

// ✅ CORRECT: Database integration (when available)
#[tokio::test]
#[ignore] // Requires running server - enable for full integration testing
async fn test_with_database() {
    // This shows the pattern - actual implementation would use TestDatabase
    // let db = TestDatabase::new().await.expect("Test DB setup");
    // let tx = db.begin().await.expect("Transaction start");

    let response = TestClient::with_base_url("http://localhost:3001")
        .get("/users")
        .send()
        .await
        .expect("DB test should succeed")
        .assert_success();

    // Verify database state
    assert!(response.json().is_ok());

    // tx.rollback().await.expect("Transaction rollback");
}