server-less 0.6.0

Composable derive macros for common Rust patterns
Documentation
//! Integration tests for the #[server] blessed preset.

#![allow(dead_code)]
#![allow(unused_variables)]

use server_less::server;
use server_less::Config;

// Basic server preset (zero-config)
#[derive(Clone)]
struct BasicService;

#[server]
impl BasicService {
    /// List items
    pub fn list_items(&self) -> Vec<String> {
        vec!["a".to_string(), "b".to_string()]
    }

    /// Create an item
    pub fn create_item(&self, name: String) -> String {
        name
    }
}

#[test]
fn test_server_basic_router() {
    let service = BasicService;
    let _router = service.router();
}

#[test]
fn test_server_basic_http_router() {
    let service = BasicService;
    // http_router() is generated by #[http]
    let _router = service.http_router();
}

#[test]
fn test_server_basic_openapi_spec() {
    let spec = BasicService::openapi_spec();
    assert_eq!(spec["openapi"], "3.0.0");
}

// Server with prefix
#[derive(Clone)]
struct PrefixedService;

#[server(prefix = "/api/v1")]
impl PrefixedService {
    /// Get status
    pub fn get_status(&self) -> String {
        "ok".to_string()
    }
}

#[test]
fn test_server_prefix_router() {
    let service = PrefixedService;
    let _router = service.router();
}

#[test]
fn test_server_prefix_openapi_paths() {
    let spec = PrefixedService::openapi_spec();
    let paths = &spec["paths"];
    assert!(paths.is_object());
    // Paths should include the prefix
    let paths_str = serde_json::to_string(paths).unwrap();
    assert!(
        paths_str.contains("/api/v1"),
        "Paths should include prefix: {}",
        paths_str
    );
}

// Server with openapi disabled
#[derive(Clone)]
struct NoOpenApiServer;

#[server(openapi = false)]
impl NoOpenApiServer {
    pub fn list_things(&self) -> Vec<String> {
        vec![]
    }
}

#[test]
fn test_server_no_openapi() {
    let service = NoOpenApiServer;
    let _router = service.router();
    // openapi_spec() should NOT be available — verified by compilation
}

// Server with custom health path
#[derive(Clone)]
struct CustomHealthServer;

#[server(health = "/healthz")]
impl CustomHealthServer {
    pub fn list_items(&self) -> Vec<String> {
        vec![]
    }
}

#[test]
fn test_server_custom_health() {
    let service = CustomHealthServer;
    let _router = service.router();
}

// Server with all options
#[derive(Clone)]
struct FullServer;

#[server(prefix = "/api", openapi = true, health = "/status")]
impl FullServer {
    pub fn get_info(&self) -> String {
        "info".to_string()
    }

    pub fn create_item(&self, name: String) -> String {
        name
    }
}

#[test]
fn test_server_full_options() {
    let service = FullServer;
    let _router = service.router();
    let spec = FullServer::openapi_spec();
    assert_eq!(spec["openapi"], "3.0.0");
}

// --- Config subcommand tests ---

#[derive(Config)]
struct ServerConfig {
    #[param(default = "0.0.0.0", help = "Bind address")]
    host: String,
    #[param(default = 3000, help = "Port to listen on", env = "PORT")]
    port: u16,
    db_url: Option<String>,
}

#[derive(Clone)]
struct ConfiguredServer;

#[server(config = ServerConfig, name = "myserver")]
impl ConfiguredServer {
    pub fn health(&self) -> String {
        "ok".to_string()
    }
}

#[test]
fn test_server_config_subcommand_exists() {
    let cmd = ConfiguredServer::config_subcommand();
    assert_eq!(cmd.get_name(), "config");
    let children: Vec<_> = cmd
        .get_subcommands()
        .map(|s| s.get_name().to_string())
        .collect();
    assert!(children.contains(&"show".to_string()), "Missing 'show'");
    assert!(children.contains(&"schema".to_string()), "Missing 'schema'");
    assert!(children.contains(&"validate".to_string()), "Missing 'validate'");
    assert!(children.contains(&"set".to_string()), "Missing 'set'");
}

#[test]
fn test_server_config_custom_cmd_name() {
    #[derive(Clone)]
    struct AltServer;

    #[server(config = ServerConfig, config_cmd = "settings", name = "altserver")]
    impl AltServer {
        pub fn ping(&self) -> String { "pong".into() }
    }

    let cmd = AltServer::config_subcommand();
    assert_eq!(cmd.get_name(), "settings");
}

#[test]
fn test_server_config_load_defaults() {
    use server_less::{ConfigSource, ConfigLoad};
    let cfg = ServerConfig::load(&[ConfigSource::Defaults]).unwrap();
    assert_eq!(cfg.host, "0.0.0.0");
    assert_eq!(cfg.port, 3000);
    assert_eq!(cfg.db_url, None);
}