acorn-lib 0.3.2

ACORN library
#![allow(clippy::unwrap_used, clippy::expect_used, clippy::panic)]
use super::super::{
    opencode::{Config, Inner},
    McpSync, SyncTarget,
};
use crate::io::{home_directory, parse_jsonc_cst, PathConversion};
use crate::test::utils::temp_dir;
use crate::util::constants::app::DEFAULT_OPENCODE_CONFIG_PATH;
use acorn_schema::agent::opencode::{self, McpConfig, McpLocalConfig};
use serde_json::Value;
use std::env::{current_dir, current_exe};
use std::fs::{create_dir_all, remove_dir_all, write};
use std::path::{Path, PathBuf};

#[test]
fn test_default_values() {
    let config = Config::default();
    assert_eq!(config.base_url, "http://localhost:8080/v1");
    assert_eq!(config.provider_id, "llama-swap");
    assert_eq!(config.provider_name, "Local (llama-swap)");
    assert_eq!(config.inner.to_string(), "llama-swap");
}
#[test]
fn test_discover_path_prefers_calling_directory_jsonc_then_json() {
    let dir = temp_dir("opencode-calling-directory");
    let user_config = dir.join("user").join("opencode.jsonc");
    create_dir_all(user_config.parent().unwrap()).unwrap();
    write(&user_config, "{}").unwrap();
    let project_json = dir.join("opencode.json");
    write(&project_json, "{}").unwrap();
    assert_eq!(Inner::discover_path(&dir, user_config.clone()), project_json);
    let project_jsonc = dir.join("opencode.jsonc");
    write(&project_jsonc, "not valid jsonc").unwrap();
    assert_eq!(Inner::discover_path(&dir, user_config), project_jsonc);
    let _ = remove_dir_all(dir);
}
#[test]
fn test_model_json_structure() {
    let json = Config::default().model_entry("Qwen GGUF");
    assert_eq!(json.get("name").unwrap().as_str().unwrap(), "Qwen GGUF");
}
#[test]
fn test_provider_json_structure() {
    let config = Config::default();
    let json = config.provider_entry();
    assert_eq!(json.get("npm").unwrap().as_str().unwrap(), "@ai-sdk/openai-compatible");
    assert_eq!(json.get("name").unwrap().as_str().unwrap(), "Local (llama-swap)");
    let options = json.get("options").unwrap();
    assert_eq!(options.get("baseURL").unwrap().as_str().unwrap(), "http://localhost:8080/v1");
}
#[test]
fn test_render_preserves_jsonc_comments_outside_managed_provider() {
    let (existing, cst) = parse_jsonc_cst::<opencode::Config>(
        r#"{
  // root comment
  "username": "acorn",
  "provider": {
    // unrelated provider comment
    "other": {"options": {"baseURL": "https://example.test"}},
    "llama-swap": {"models": {"stale": {"name": "Stale"}}}
  }
}"#,
    )
    .unwrap();
    let config = Config::default();
    let updated = config.upsert(&existing, &[("qwen".to_string(), "Qwen".to_string())], false).unwrap();
    let rendered = config.render_with_cst(&updated, Some(&cst)).unwrap();
    assert!(rendered.contains("// root comment"));
    assert!(rendered.contains("// unrelated provider comment"));
    assert!(rendered.contains("\"stale\""));
    assert!(rendered.contains("\"qwen\""));
    assert_eq!(rendered, config.render_with_cst(&updated, Some(&cst)).unwrap());
}
#[test]
fn test_resolve_path_prefers_explicit_path() {
    let explicit = PathBuf::from(Path::new("custom/opencode.json").cross_platform_display());
    assert_eq!(Config::resolve_path(Some("custom/opencode.json")).unwrap(), explicit);
}
#[test]
fn test_resolve_path_uses_discovered_or_default_config() {
    let calling_directory = current_dir().unwrap();
    let user_config = home_directory(DEFAULT_OPENCODE_CONFIG_PATH).unwrap();
    let expected = [
        calling_directory.join("opencode.jsonc"),
        calling_directory.join("opencode.json"),
        user_config.clone(),
        user_config.with_extension("json"),
    ]
    .into_iter()
    .find(|path| path.is_file())
    .unwrap_or(user_config);
    assert_eq!(Config::resolve_path(None).unwrap(), expected);
}
#[test]
fn test_upsert_is_additive_unless_pruned() {
    let existing = opencode::Config {
        username: Some("acorn".to_string()),
        provider: Some(
            [
                ("other".to_string(), serde_json::json!({"options": {"baseURL": "https://example.test"}})),
                (
                    "llama-swap".to_string(),
                    serde_json::json!({
                        "custom": true,
                        "options": {"apiKey": "secret"},
                        "models": {"stale": {"name": "Stale"}}
                    }),
                ),
            ]
            .into_iter()
            .collect(),
        ),
        ..Default::default()
    };
    let config = Config::default();
    let models = [("qwen".to_string(), "Qwen".to_string())];
    let additive = config.upsert(&existing, &models, false).unwrap();
    let provider = additive.provider.as_ref().unwrap().get("llama-swap").unwrap();
    assert!(provider.pointer("/models/stale").is_some());
    assert!(provider.pointer("/models/qwen").is_some());
    assert_eq!(provider.get("custom"), Some(&Value::Bool(true)));
    assert_eq!(provider.pointer("/options/apiKey").and_then(Value::as_str), Some("secret"));
    assert!(additive.provider.as_ref().unwrap().contains_key("other"));
    assert_eq!(additive.username.as_deref(), Some("acorn"));
    let pruned = config.upsert(&existing, &models, true).unwrap();
    let provider = pruned.provider.as_ref().unwrap().get("llama-swap").unwrap();
    assert!(provider.pointer("/models/stale").is_none());
    assert!(provider.pointer("/models/qwen").is_some());
}
#[test]
fn test_upsert_mcp_preserves_other_servers_and_prunes_only_acorn() {
    let executable = current_exe().expect("current test executable");
    let existing = opencode::Config {
        mcp: Some(
            [(
                "other".to_string(),
                McpConfig::Local(McpLocalConfig {
                    command: vec!["other".to_string()],
                    cwd: None,
                    environment: None,
                    enabled: None,
                    timeout: None,
                }),
            )]
            .into_iter()
            .collect(),
        ),
        ..Default::default()
    };
    let updated = Config::default().upsert_mcp(existing, &executable, false).expect("upsert MCP");
    assert!(updated
        .mcp
        .as_ref()
        .is_some_and(|servers| servers.contains_key("acorn") && servers.contains_key("other")));
    let pruned = Config::default().upsert_mcp(updated, &executable, true).expect("prune MCP");
    assert!(pruned
        .mcp
        .as_ref()
        .is_some_and(|servers| !servers.contains_key("acorn") && servers.contains_key("other")));
}
#[test]
fn test_upsert_protects_pruned_root_model() {
    let existing = opencode::Config {
        model: Some("llama-swap/stale".to_string()),
        provider: Some(
            [("llama-swap".to_string(), serde_json::json!({"models": {"stale": {"name": "Stale"}}}))]
                .into_iter()
                .collect(),
        ),
        ..Default::default()
    };
    let models = [("qwen".to_string(), "Qwen".to_string())];
    assert!(Config::default().upsert(&existing, &models, true).is_err());
    let replacement = Config {
        inner: Inner {
            default_model: Some("qwen".to_string()),
            ..Default::default()
        },
        ..Default::default()
    }
    .upsert(&existing, &models, true)
    .unwrap();
    assert_eq!(replacement.model.as_deref(), Some("llama-swap/qwen"));
}