link-assistant-router 0.90.0

Link.Assistant.Router — Claude MAX OAuth proxy and token gateway for Anthropic APIs
Documentation
use super::*;
use crate::config::{
    default_gonka_model, default_gonka_source_url, default_openai_compatible_base_url,
};
use clap::CommandFactory as _;

#[test]
fn login_cli_defaults_to_bare_tui() {
    let cli = Cli::try_parse_from(["link-assistant-router"]).unwrap();
    assert!(cli.login_cli_args.is_empty());
    assert_eq!(cli.upstream_provider, "auto");
}

#[test]
fn every_declared_default_is_visible_in_its_long_help() {
    fn check(command: &clap::Command, path: &str) {
        let help = command.clone().render_long_help().to_string();
        for argument in command.get_arguments() {
            if argument.get_default_values().is_empty() || argument.is_hide_default_value_set() {
                continue;
            }
            let rendered = argument
                .get_default_values()
                .iter()
                .map(|value| value.to_string_lossy())
                .collect::<Vec<_>>()
                .join(", ");
            if rendered.is_empty() {
                continue;
            }
            assert!(
                help.contains(&format!("[default: {rendered}]")),
                "{path} --{} must render its declared default {rendered:?}",
                argument.get_long().unwrap_or("<positional>")
            );
        }
        for subcommand in command.get_subcommands() {
            check(subcommand, &format!("{path} {}", subcommand.get_name()));
        }
    }

    check(&Cli::command(), "link-assistant-router");
}

#[test]
fn sensitive_environment_values_are_hidden_from_help() {
    fn check(command: &clap::Command) {
        for argument in command.get_arguments() {
            let Some(environment) = argument.get_env() else {
                continue;
            };
            let environment = environment.to_string_lossy().to_ascii_uppercase();
            if (["_TOKEN", "_SECRET", "_PRIVATE_KEY", "_API_KEY"]
                .iter()
                .any(|marker| environment.ends_with(marker))
                || environment.ends_with("_KEY") && !environment.contains("PUBLIC_KEY"))
                && !environment.ends_with("_ENV")
            {
                assert!(
                    argument.is_hide_env_values_set(),
                    "environment value {environment} must be hidden from help"
                );
            }
        }
        for subcommand in command.get_subcommands() {
            check(subcommand);
        }
    }

    check(&Cli::command());
}

#[test]
fn cli_defaults_round_trip_to_config() {
    let cli = Cli {
        command: None,
        host: "127.0.0.1".into(),
        port: 9090,
        verbose: false,
        token_secret: Some("k".into()),
        claude_code_home: Some("/tmp/c".into()),
        upstream_base_url: "https://api.anthropic.com".into(),
        api_format: None,
        routing_mode: "direct".into(),
        storage_policy: "memory".into(),
        data_dir: Some(std::path::PathBuf::from("/tmp/d")),
        claude_cli_bin: None,
        upstream_provider: "anthropic".into(),
        gonka_private_key: None,
        gonka_source_url: default_gonka_source_url(),
        gonka_model: default_gonka_model(),
        bridge_model: None,
        bridge_model_policy: None,
        audit_log: None,
        request_log: None,
        request_log_max_bytes: crate::request_log::DEFAULT_MAX_BYTES,
        max_proxy_request_bytes: crate::config::DEFAULT_MAX_PROXY_REQUEST_BYTES,
        crater_forgefed_inbox: None,
        crater_forgefed_actor: None,
        crater_forgefed_target: None,
        crater_poll_interval_ms: 1000,
        crater_poll_timeout_secs: 120,
        openai_compatible_provider_name: "litellm".into(),
        openai_compatible_base_url: default_openai_compatible_base_url(),
        openai_compatible_api_key: None,
        openai_compatible_api_key_env: None,
        openai_compatible_model: None,
        openai_compatible_models: vec![],
        activitypub_actor_base_url: Some("https://router.example".into()),
        activitypub_public_key_pem: None,
        disable_openai_api: false,
        disable_anthropic_api: false,
        disable_metrics: false,
        additional_account_dirs: vec![],
        account_routing_strategy: "round-robin".into(),
        account_cooldown_secs: 60,
        session_affinity_ttl_secs: 3600,
        account_request_limits: vec![],
        experimental_compatibility: false,
        admin_port: None,
        admin_host: "127.0.0.1".into(),
        admin_claim_ttl_secs: crate::admin::DEFAULT_CANDIDATE_TTL_SECS,
        admin_key: None,
        allow_anonymous_admin: false,
        telegram_bot_token: None,
        vk_bot_token: None,
        vk_group_id: None,
        chat_admin_secret_ttl_secs: crate::chat_admin::DEFAULT_SECRET_TTL_SECS,
        chat_admin_rate_limit_per_minute: crate::chat_admin::DEFAULT_RATE_LIMIT_PER_MINUTE,
        mpp_enable: false,
        mpp_amount: "0.00".into(),
        mpp_currency: "USD".into(),
        mpp_recipient: None,
        mpp_method: None,
        disable_login_api: false,
        login_cli_command: "claude".into(),
        login_cli_args: vec![],
        login_session_ttl_secs: 900,
        login_max_sessions: 4,
    };
    let cfg = cli.into_config().unwrap();
    assert_eq!(cfg.listen_addr.port(), 9090);
    assert_eq!(cfg.routing_mode, RoutingMode::Direct);
    assert_eq!(cfg.storage_policy, StoragePolicy::Memory);
    assert!(cfg.enable_openai_api);
    assert!(cfg.enable_anthropic_api);
    assert!(cfg.enable_metrics);
    assert_eq!(
        cfg.max_proxy_request_bytes,
        crate::config::DEFAULT_MAX_PROXY_REQUEST_BYTES
    );
}

#[test]
fn cli_invalid_routing_mode_rejected() {
    let cli = Cli {
        command: None,
        host: "0.0.0.0".into(),
        port: 8080,
        verbose: false,
        token_secret: Some("k".into()),
        claude_code_home: Some("/tmp/c".into()),
        upstream_base_url: "https://api.anthropic.com".into(),
        api_format: None,
        routing_mode: "bogus".into(),
        storage_policy: "memory".into(),
        data_dir: None,
        claude_cli_bin: None,
        upstream_provider: "anthropic".into(),
        gonka_private_key: None,
        gonka_source_url: default_gonka_source_url(),
        gonka_model: default_gonka_model(),
        bridge_model: None,
        bridge_model_policy: None,
        audit_log: None,
        request_log: None,
        request_log_max_bytes: crate::request_log::DEFAULT_MAX_BYTES,
        max_proxy_request_bytes: crate::config::DEFAULT_MAX_PROXY_REQUEST_BYTES,
        crater_forgefed_inbox: None,
        crater_forgefed_actor: None,
        crater_forgefed_target: None,
        crater_poll_interval_ms: 1000,
        crater_poll_timeout_secs: 120,
        openai_compatible_provider_name: "litellm".into(),
        openai_compatible_base_url: default_openai_compatible_base_url(),
        openai_compatible_api_key: None,
        openai_compatible_api_key_env: None,
        openai_compatible_model: None,
        openai_compatible_models: vec![],
        activitypub_actor_base_url: None,
        activitypub_public_key_pem: None,
        disable_openai_api: false,
        disable_anthropic_api: false,
        disable_metrics: false,
        additional_account_dirs: vec![],
        account_routing_strategy: "round-robin".into(),
        account_cooldown_secs: 60,
        session_affinity_ttl_secs: 3600,
        account_request_limits: vec![],
        experimental_compatibility: false,
        admin_port: None,
        admin_host: "127.0.0.1".into(),
        admin_claim_ttl_secs: crate::admin::DEFAULT_CANDIDATE_TTL_SECS,
        admin_key: None,
        allow_anonymous_admin: false,
        telegram_bot_token: None,
        vk_bot_token: None,
        vk_group_id: None,
        chat_admin_secret_ttl_secs: crate::chat_admin::DEFAULT_SECRET_TTL_SECS,
        chat_admin_rate_limit_per_minute: crate::chat_admin::DEFAULT_RATE_LIMIT_PER_MINUTE,
        mpp_enable: false,
        mpp_amount: "0.00".into(),
        mpp_currency: "USD".into(),
        mpp_recipient: None,
        mpp_method: None,
        disable_login_api: false,
        login_cli_command: "claude".into(),
        login_cli_args: vec![],
        login_session_ttl_secs: 900,
        login_max_sessions: 4,
    };
    let r = cli.into_config();
    assert!(matches!(r, Err(ConfigError::InvalidRoutingMode)));
}