fraiseql-server 2.2.0

HTTP server for FraiseQL v2 GraphQL engine
Documentation
#![allow(clippy::unwrap_used)] // Reason: test code, panics acceptable

use std::time::Duration;

use fraiseql_error::ConfigError;

use super::*;
use crate::config::{
    env::{parse_duration, parse_size, resolve_env_value},
    validation::ConfigValidator,
};

#[test]
fn test_parse_minimal_config() {
    temp_env::with_vars([("DATABASE_URL", Some("postgres://localhost/test"))], || {
        let toml = r#"
            [server]
            port = 4000

            [database]
            url_env = "DATABASE_URL"
        "#;

        let config: RuntimeConfig = toml::from_str(toml).unwrap();

        assert_eq!(config.server.port, 4000);
        assert_eq!(config.server.host, "127.0.0.1");
        assert_eq!(config.database.url_env, "DATABASE_URL");
        assert_eq!(config.database.pool_size, 10);
    });
}

#[test]
fn test_parse_size() {
    assert_eq!(parse_size("10MB").unwrap(), 10 * 1024 * 1024);
    assert_eq!(parse_size("1GB").unwrap(), 1024 * 1024 * 1024);
    assert_eq!(parse_size("500KB").unwrap(), 500 * 1024);
    assert_eq!(parse_size("1000").unwrap(), 1000);
    assert_eq!(parse_size("100B").unwrap(), 100);
}

#[test]
fn test_parse_size_invalid() {
    assert!(
        parse_size("abc").is_err(),
        "expected Err for invalid size string, got: {:?}",
        parse_size("abc")
    );
}

#[test]
fn test_parse_duration() {
    assert_eq!(parse_duration("30s").unwrap(), Duration::from_secs(30));
    assert_eq!(parse_duration("5m").unwrap(), Duration::from_secs(300));
    assert_eq!(parse_duration("1h").unwrap(), Duration::from_secs(3600));
    assert_eq!(parse_duration("100ms").unwrap(), Duration::from_millis(100));
    assert_eq!(parse_duration("1d").unwrap(), Duration::from_secs(86400));
}

#[test]
fn test_parse_duration_invalid() {
    assert!(
        parse_duration("30").is_err(),
        "expected Err for duration without unit, got: {:?}",
        parse_duration("30")
    ); // Missing unit
    assert!(
        parse_duration("abc").is_err(),
        "expected Err for non-numeric duration, got: {:?}",
        parse_duration("abc")
    );
}

#[test]
fn test_env_resolution_with_default() {
    temp_env::with_vars([("NONEXISTENT_VAR", None::<&str>)], || {
        let result = resolve_env_value("${NONEXISTENT_VAR:-default_value}").unwrap();
        assert_eq!(result, "default_value");
    });
}

#[test]
fn test_env_resolution_without_default() {
    temp_env::with_vars([("EXISTING_VAR", Some("actual_value"))], || {
        let result = resolve_env_value("${EXISTING_VAR:-default}").unwrap();
        assert_eq!(result, "actual_value");
    });
}

#[test]
fn test_validation_missing_env_var() {
    temp_env::with_vars([("NONEXISTENT_DB_URL", None::<&str>)], || {
        let toml = r#"
            [server]
            port = 4000

            [database]
            url_env = "NONEXISTENT_DB_URL"
        "#;

        let config: RuntimeConfig = toml::from_str(toml).unwrap();
        let result = ConfigValidator::new(&config).validate();

        assert!(result.is_err(), "expected Err for missing env var but got no errors");
        assert!(result.errors.iter().any(|e| matches!(e, ConfigError::MissingEnvVar { .. })));
    });
}

#[test]
fn test_validation_cross_field() {
    temp_env::with_vars([("DATABASE_URL", Some("postgres://localhost/test"))], || {
        let toml = r#"
            [server]
            port = 4000

            [database]
            url_env = "DATABASE_URL"

            [observers.test]
            entity = "users"
            events = ["insert"]

            [[observers.test.actions]]
            type = "email"
            template = "welcome"
        "#;

        let config: RuntimeConfig = toml::from_str(toml).unwrap();
        let result = ConfigValidator::new(&config).validate();

        // Should fail because email action requires notifications config
        assert!(
            result.is_err(),
            "expected Err because email action requires notifications config but got no errors"
        );
    });
}