magi-code 0.77.1

Repository-aware CLI coding agent for terminal work
Documentation
use super::super::{
    CachePreference, MAX_CUSTOM_PROVIDER_CATALOG_WORKERS, McPaths, ModelsDevLookup,
    load_aggregated_catalog, load_aggregated_catalog_with_models_dev,
};
use super::{
    spawn_counted_model_catalog_server, spawn_delayed_model_catalog_server,
    spawn_model_catalog_server,
};
use crate::config::CustomProviderConfig;
use std::{fs, sync::Arc};
use tempfile::TempDir;

#[test]
fn aggregated_catalog_reports_settings_and_auth_read_failures() {
    let temp = TempDir::new().unwrap();
    let paths = McPaths::from_root(temp.path().join("mc"));
    fs::create_dir_all(&paths.root).unwrap();
    fs::write(&paths.settings_file, "not json").unwrap();
    fs::write(&paths.auth_file, "not json").unwrap();
    #[cfg(unix)]
    {
        use std::os::unix::fs::PermissionsExt;
        let mut permissions = fs::metadata(&paths.auth_file).unwrap().permissions();
        permissions.set_mode(0o600);
        fs::set_permissions(&paths.auth_file, permissions).unwrap();
    }

    let catalog = load_aggregated_catalog(&paths, CachePreference::AllowStale);

    assert!(
        catalog
            .notices
            .iter()
            .any(|notice| notice.contains("settings.json could not be read"))
    );
    assert!(
        catalog
            .notices
            .iter()
            .any(|notice| notice.contains("auth.json could not be read"))
    );
    let notices = catalog.notices.join("\n");
    assert!(!notices.contains("sk-"));
    assert!(!notices.contains("access_token"));
}

#[test]
fn aggregated_catalog_reuses_seeded_models_dev_lookup_across_custom_providers() {
    let temp = TempDir::new().unwrap();
    let paths = McPaths::from_root(temp.path().join("mc"));
    let (provider_a_url, provider_a_handle) = spawn_model_catalog_server("shared-model");
    let (provider_b_url, provider_b_handle) = spawn_model_catalog_server("shared-model");
    crate::config::write_settings(
        &paths,
        &crate::config::Settings {
            custom_providers: std::collections::BTreeMap::from([
                (
                    "provider-a".to_string(),
                    CustomProviderConfig {
                        label: "Provider A".to_string(),
                        base_url: provider_a_url,
                        fast_mode: None,
                        api_key_env_var: None,
                        models_dev_provider: Some("provider-a-meta".to_string()),
                        use_responses_endpoint: false,
                        supports_text_verbosity: false,
                        reasoning_protocol: crate::config::CustomReasoningProtocol::default(),
                        extra_models: Vec::new(),
                        request_headers: Default::default(),
                    },
                ),
                (
                    "provider-b".to_string(),
                    CustomProviderConfig {
                        label: "Provider B".to_string(),
                        base_url: provider_b_url,
                        fast_mode: None,
                        api_key_env_var: None,
                        models_dev_provider: Some("provider-b-meta".to_string()),
                        use_responses_endpoint: false,
                        supports_text_verbosity: false,
                        reasoning_protocol: crate::config::CustomReasoningProtocol::default(),
                        extra_models: Vec::new(),
                        request_headers: Default::default(),
                    },
                ),
            ]),
            ..Default::default()
        },
    )
    .unwrap();
    let mut models_dev = ModelsDevLookup {
        attempted: true,
        value: Some(serde_json::json!({
            "provider-a-meta": {"models": {"shared-model": {"description": "provider a metadata", "limit": {"context": 111_u64}}}},
            "provider-b-meta": {"models": {"shared-model": {"description": "provider b metadata", "limit": {"context": 222_u64}}}}
        })),
    };

    let catalog = load_aggregated_catalog_with_models_dev(
        &paths,
        CachePreference::AllowStale,
        &mut models_dev,
    );
    provider_a_handle.join().unwrap();
    provider_b_handle.join().unwrap();

    assert_eq!(
        catalog
            .entries
            .iter()
            .filter(|entry| entry.provider == "provider-a" || entry.provider == "provider-b")
            .count(),
        2,
        "notices={:?}",
        catalog.notices
    );
    let provider_a = catalog
        .entries
        .iter()
        .find(|entry| entry.provider == "provider-a")
        .unwrap();
    let provider_b = catalog
        .entries
        .iter()
        .find(|entry| entry.provider == "provider-b")
        .unwrap();
    assert_eq!(
        provider_a.description.as_deref(),
        Some("provider a metadata")
    );
    assert_eq!(provider_a.context_window, Some(111));
    assert_eq!(
        provider_b.description.as_deref(),
        Some("provider b metadata")
    );
    assert_eq!(provider_b.context_window, Some(222));
}

#[test]
fn aggregated_catalog_loads_custom_providers_in_parallel() {
    let temp = TempDir::new().unwrap();
    let paths = McPaths::from_root(temp.path().join("mc"));
    let (seen_tx, seen_rx) = std::sync::mpsc::channel();
    let (provider_a_url, provider_a_handle) = spawn_delayed_model_catalog_server(
        Some("parallel-a"),
        200,
        std::time::Duration::from_millis(300),
        Some(seen_tx.clone()),
    );
    let (provider_b_url, provider_b_handle) = spawn_delayed_model_catalog_server(
        Some("parallel-b"),
        200,
        std::time::Duration::from_millis(300),
        Some(seen_tx),
    );
    crate::config::write_settings(
        &paths,
        &crate::config::Settings {
            custom_providers: std::collections::BTreeMap::from([
                (
                    "provider-a".to_string(),
                    CustomProviderConfig {
                        label: "Provider A".to_string(),
                        base_url: provider_a_url,
                        fast_mode: None,
                        api_key_env_var: None,
                        models_dev_provider: Some("provider-a-meta".to_string()),
                        use_responses_endpoint: false,
                        supports_text_verbosity: false,
                        reasoning_protocol: crate::config::CustomReasoningProtocol::default(),
                        extra_models: Vec::new(),
                        request_headers: Default::default(),
                    },
                ),
                (
                    "provider-b".to_string(),
                    CustomProviderConfig {
                        label: "Provider B".to_string(),
                        base_url: provider_b_url,
                        fast_mode: None,
                        api_key_env_var: None,
                        models_dev_provider: Some("provider-b-meta".to_string()),
                        use_responses_endpoint: false,
                        supports_text_verbosity: false,
                        reasoning_protocol: crate::config::CustomReasoningProtocol::default(),
                        extra_models: Vec::new(),
                        request_headers: Default::default(),
                    },
                ),
            ]),
            ..Default::default()
        },
    )
    .unwrap();
    let mut models_dev = ModelsDevLookup {
        attempted: true,
        value: Some(serde_json::json!({})),
    };

    let started = std::time::Instant::now();
    let catalog = load_aggregated_catalog_with_models_dev(
        &paths,
        CachePreference::AllowStale,
        &mut models_dev,
    );
    let elapsed = started.elapsed();
    provider_a_handle.join().unwrap();
    provider_b_handle.join().unwrap();
    let first_seen = seen_rx.recv().unwrap();
    let second_seen = seen_rx.recv().unwrap();

    assert!(
        elapsed < std::time::Duration::from_millis(550),
        "catalog discovery was not parallel enough: elapsed={elapsed:?}, notices={:?}",
        catalog.notices
    );
    assert!(
        second_seen.duration_since(first_seen) < std::time::Duration::from_millis(200),
        "provider requests did not overlap: first={first_seen:?}, second={second_seen:?}"
    );
    for id in ["provider-a/parallel-a", "provider-b/parallel-b"] {
        assert!(
            catalog.entries.iter().any(|entry| entry.id == id),
            "missing {id}; notices={:?}",
            catalog.notices
        );
    }
}

#[test]
fn aggregated_catalog_bounds_custom_provider_worker_concurrency() {
    let temp = TempDir::new().unwrap();
    let paths = McPaths::from_root(temp.path().join("mc"));
    let active = Arc::new(std::sync::atomic::AtomicUsize::new(0));
    let peak = Arc::new(std::sync::atomic::AtomicUsize::new(0));
    let mut providers = std::collections::BTreeMap::new();
    let mut handles = Vec::new();

    for index in 0..(MAX_CUSTOM_PROVIDER_CATALOG_WORKERS + 3) {
        let provider = format!("provider-{index}");
        let model = format!("bounded-{index}");
        let (url, handle) = spawn_counted_model_catalog_server(
            &model,
            std::time::Duration::from_millis(100),
            Arc::clone(&active),
            Arc::clone(&peak),
        );
        handles.push(handle);
        providers.insert(
            provider,
            CustomProviderConfig {
                label: format!("Provider {index}"),
                base_url: url,
                fast_mode: None,
                api_key_env_var: None,
                models_dev_provider: None,
                use_responses_endpoint: false,
                supports_text_verbosity: false,
                reasoning_protocol: crate::config::CustomReasoningProtocol::default(),
                extra_models: Vec::new(),
                request_headers: Default::default(),
            },
        );
    }
    crate::config::write_settings(
        &paths,
        &crate::config::Settings {
            custom_providers: providers,
            ..Default::default()
        },
    )
    .unwrap();
    let mut models_dev = ModelsDevLookup {
        attempted: true,
        value: Some(serde_json::json!({})),
    };

    let catalog = load_aggregated_catalog_with_models_dev(
        &paths,
        CachePreference::AllowStale,
        &mut models_dev,
    );
    for handle in handles {
        handle.join().unwrap();
    }

    assert!(
        peak.load(std::sync::atomic::Ordering::SeqCst) <= MAX_CUSTOM_PROVIDER_CATALOG_WORKERS,
        "peak custom provider requests exceeded cap; notices={:?}",
        catalog.notices
    );
    for index in 0..(MAX_CUSTOM_PROVIDER_CATALOG_WORKERS + 3) {
        let id = format!("provider-{index}/bounded-{index}");
        assert!(
            catalog.entries.iter().any(|entry| entry.id == id),
            "missing {id}; notices={:?}",
            catalog.notices
        );
    }
}

#[test]
fn aggregated_catalog_preserves_order_with_mixed_custom_success_and_failure() {
    let temp = TempDir::new().unwrap();
    let paths = McPaths::from_root(temp.path().join("mc"));
    let (provider_a_url, provider_a_handle) = spawn_delayed_model_catalog_server(
        Some("success-a"),
        200,
        std::time::Duration::from_millis(50),
        None,
    );
    let (provider_b_url, provider_b_handle) =
        spawn_delayed_model_catalog_server(None, 500, std::time::Duration::from_millis(50), None);
    crate::config::write_settings(
        &paths,
        &crate::config::Settings {
            custom_providers: std::collections::BTreeMap::from([
                (
                    "provider-a".to_string(),
                    CustomProviderConfig {
                        label: "Provider A".to_string(),
                        base_url: provider_a_url,
                        fast_mode: None,
                        api_key_env_var: None,
                        models_dev_provider: None,
                        use_responses_endpoint: false,
                        supports_text_verbosity: false,
                        reasoning_protocol: crate::config::CustomReasoningProtocol::default(),
                        extra_models: Vec::new(),
                        request_headers: Default::default(),
                    },
                ),
                (
                    "provider-b".to_string(),
                    CustomProviderConfig {
                        label: "Provider B".to_string(),
                        base_url: provider_b_url,
                        fast_mode: None,
                        api_key_env_var: None,
                        models_dev_provider: None,
                        use_responses_endpoint: false,
                        supports_text_verbosity: false,
                        reasoning_protocol: crate::config::CustomReasoningProtocol::default(),
                        extra_models: Vec::new(),
                        request_headers: Default::default(),
                    },
                ),
            ]),
            ..Default::default()
        },
    )
    .unwrap();
    let mut models_dev = ModelsDevLookup {
        attempted: true,
        value: Some(serde_json::json!({})),
    };

    let catalog = load_aggregated_catalog_with_models_dev(
        &paths,
        CachePreference::AllowStale,
        &mut models_dev,
    );
    provider_a_handle.join().unwrap();
    provider_b_handle.join().unwrap();
    let ids = catalog
        .entries
        .iter()
        .map(|entry| entry.id.as_str())
        .collect::<Vec<_>>();

    assert_eq!(&ids[..1], ["provider-a/success-a"]);
    assert!(catalog.notices.iter().any(|notice| {
        notice.contains("provider-b model discovery unavailable") && notice.contains("500")
    }));
    assert!(!ids.iter().any(|id| id.starts_with("provider-b/")));
}