skill-http 0.3.0

HTTP streaming server for Skill - REST API and web interface support
//! Integration tests for Services API endpoints

mod common;

use axum::http::StatusCode;
use common::TestApp;
use serde_json::json;

// ============================================================================
// List Services Tests
// ============================================================================

#[tokio::test]
async fn test_list_services_returns_200() {
    let app = TestApp::new().await;
    let req = TestApp::get_request("/api/services");
    let (status, body) = app.request(req).await;

    assert_eq!(status, StatusCode::OK);

    // Parse response as JSON array
    let services: serde_json::Value = TestApp::parse_json(&body);
    assert!(services.is_array() || services.is_object());
}

#[tokio::test]
async fn test_list_services_structure() {
    let app = TestApp::new().await;
    let req = TestApp::get_request("/api/services");
    let (status, body) = app.request(req).await;

    if status == StatusCode::OK {
        let services: serde_json::Value = TestApp::parse_json(&body);

        if let Some(array) = services.as_array() {
            // Each service should have required fields
            for service in array {
                assert!(service.get("name").is_some());
                assert!(service.get("running").is_some());
            }
        }
    }
}

// ============================================================================
// Start Service Tests
// ============================================================================

#[tokio::test]
async fn test_start_service_with_valid_name() {
    let app = TestApp::new().await;
    let body = json!({
        "service": "kubectl-proxy",
        "port": 8001
    }).to_string();

    let req = TestApp::post_request("/api/services/start", &body);
    let (status, _) = app.request(req).await;

    // Service start might not be fully implemented, accept multiple statuses
    assert!(
        status == StatusCode::OK
        || status == StatusCode::INTERNAL_SERVER_ERROR
        || status == StatusCode::NOT_FOUND
        || status == StatusCode::BAD_REQUEST
    );
}

#[tokio::test]
async fn test_start_service_missing_name() {
    let app = TestApp::new().await;
    let body = json!({
        "port": 8001
        // Missing "service" field
    }).to_string();

    let req = TestApp::post_request("/api/services/start", &body);
    let (status, _) = app.request(req).await;

    // Should fail validation
    assert!(
        status == StatusCode::BAD_REQUEST
        || status == StatusCode::UNPROCESSABLE_ENTITY
    );
}

#[tokio::test]
async fn test_start_service_invalid_json() {
    let app = TestApp::new().await;
    let body = r#"{ invalid json }"#;

    let req = TestApp::post_request("/api/services/start", body);
    let (status, _) = app.request(req).await;

    assert_eq!(status, StatusCode::BAD_REQUEST);
}

#[tokio::test]
async fn test_start_service_with_custom_port() {
    let app = TestApp::new().await;
    let body = json!({
        "service": "kubectl-proxy",
        "port": 9000
    }).to_string();

    let req = TestApp::post_request("/api/services/start", &body);
    let (status, _) = app.request(req).await;

    // Should accept custom port
    assert!(
        status == StatusCode::OK
        || status == StatusCode::INTERNAL_SERVER_ERROR
        || status == StatusCode::NOT_FOUND
        || status == StatusCode::BAD_REQUEST
    );
}

// ============================================================================
// Stop Service Tests
// ============================================================================

#[tokio::test]
async fn test_stop_service_endpoint_exists() {
    let app = TestApp::new().await;
    let body = json!({
        "service": "kubectl-proxy"
    }).to_string();

    let req = TestApp::post_request("/api/services/stop", &body);
    let (status, _) = app.request(req).await;

    // Endpoint may or may not be implemented
    assert!(
        status == StatusCode::OK
        || status == StatusCode::NOT_IMPLEMENTED
        || status == StatusCode::NOT_FOUND
        || status == StatusCode::INTERNAL_SERVER_ERROR
        || status == StatusCode::BAD_REQUEST
    );
}

// ============================================================================
// Service Status Tests
// ============================================================================

#[tokio::test]
async fn test_service_status_includes_port() {
    let app = TestApp::new().await;
    let req = TestApp::get_request("/api/services");
    let (status, body) = app.request(req).await;

    if status == StatusCode::OK {
        let services: serde_json::Value = TestApp::parse_json(&body);

        if let Some(array) = services.as_array() {
            for service in array {
                if service["running"].as_bool() == Some(true) {
                    // Running services should have port or URL
                    assert!(
                        service.get("port").is_some()
                        || service.get("url").is_some()
                    );
                }
            }
        }
    }
}

#[tokio::test]
async fn test_service_status_has_name_and_running() {
    let app = TestApp::new().await;
    let req = TestApp::get_request("/api/services");
    let (status, body) = app.request(req).await;

    if status == StatusCode::OK {
        let services: serde_json::Value = TestApp::parse_json(&body);

        if let Some(array) = services.as_array() {
            for service in array {
                // Required fields
                assert!(service["name"].is_string());
                assert!(service["running"].is_boolean());
            }
        }
    }
}