acorn-lib 0.3.2

ACORN library
#![allow(clippy::unwrap_used, clippy::expect_used, clippy::panic)]
use super::super::{
    llama_swap::{Alias, Argument, Config, EnvironmentVariable, Inner, ModelOverride, ModelValidation},
    SyncTarget,
};
use crate::io::home_directory;
use crate::util::constants::app::DEFAULT_LLAMA_SWAP_CONFIG_PATH;
use acorn_schema::agent::ModelDetails;
use acorn_schema::validation::Validate;
use serde_norway::Value;

#[test]
fn test_argument_validation_rejects_reserved_options() {
    ["--port", "--port=9000", "--model", "--model=/tmp/model.gguf", "-m=/tmp/model.gguf"]
        .into_iter()
        .for_each(|argument| assert!(Argument::from(argument).validate().is_err()));
    assert!(Argument::from("--flash-attn").validate().is_ok());
}
#[test]
fn test_build_command_simple() {
    let cmd = Inner::build_command("llama-server", Some("/models/qwen.gguf"), None, None, Some(8192));
    assert_eq!(
        cmd,
        "llama-server\n  --model /models/qwen.gguf\n  --port ${PORT}\n  --ctx-size 8192\n  --offline\n  --jinja\n  --batch-size 2048\n  --host 0.0.0.0\n  --sleep-idle-seconds 600\n  --tools all\n  --ubatch-size 2048"
    );
}
#[test]
fn test_build_command_with_environment() {
    let env = vec![EnvironmentVariable::from("CUDA_VISIBLE_DEVICES=0")];
    let cmd = Inner::build_command("llama-server", Some("/models/qwen.gguf"), None, Some(&env), None);
    assert!(cmd.contains("env:CUDA_VISIBLE_DEVICES=0"));
}
#[test]
fn test_build_command_with_extra_args() {
    let extras = vec![
        Argument::from("--batch-size"),
        Argument::from("1024"),
        Argument::from("--flash-attn"),
        Argument::from("on"),
    ];
    let cmd = Inner::build_command("llama-server", Some("/models/qwen.gguf"), Some(&extras), None, None);
    assert!(cmd.contains("--batch-size 1024"));
    assert!(cmd
        .find("--batch-size 2048")
        .zip(cmd.rfind("--batch-size 1024"))
        .is_some_and(|(default, override_)| default < override_));
    assert!(cmd.contains("--flash-attn"));
    assert!(cmd.contains("on"));
}
#[test]
fn test_build_command_with_optional_values() {
    let cmd = Inner::build_command("llama-server", None, None, None, None);
    assert!(cmd.starts_with("llama-server\n  --port ${PORT}\n  --offline"));
    assert!(cmd.ends_with("--ubatch-size 2048"));
}
#[test]
fn test_build_command_with_spaces_in_path() {
    let cmd = Inner::build_command("llama-server", Some("/path with spaces/model.gguf"), None, None, None);
    assert!(cmd.contains('"'));
}
#[test]
fn test_resolve_path_uses_llama_swap_user_config() {
    assert_eq!(
        Config::resolve_path(None).unwrap(),
        home_directory(DEFAULT_LLAMA_SWAP_CONFIG_PATH).unwrap()
    );
}
#[test]
fn test_upsert_adds_gold_base_defaults() {
    let models = [ModelDetails::init().id("qwen").name("qwen").path("/models/qwen.gguf").build()];
    let output = Config::default()
        .upsert(Value::Mapping(Default::default()), &models, false)
        .and_then(|value| serde_norway::to_string(&value).map_err(Into::into))
        .unwrap();
    assert!(output.contains("healthCheckTimeout: 500"));
    assert!(output.contains("listen: http://0.0.0.0:10732"));
    assert!(output.contains("context_size: ${env.LLAMA_ARG_CTX_SIZE}"));
    assert!(output.contains("parallel: ${env.LLAMA_ARG_N_PARALLEL}"));
    assert!(output.contains("models_dir: ${env.HOME}/.models"));
    assert!(output.contains("ssl_key: ${env.HOME}/certs/my.key"));
    assert!(output.contains("ssl_cert: ${env.HOME}/certs/my.pem"));
    assert!(output.contains("proxy: http://127.0.0.1:${PORT}"));
    assert!(output.contains("--offline\n"));
    assert!(output.contains("--tools all\n"));
}
#[test]
fn test_upsert_preserves_unrelated_values_and_prunes_only_managed_models() {
    let existing: Value = serde_norway::from_str(
        r#"modelsDir: /legacy/models
healthCheckTimeout: 120
models:
  qwen:
    cmd: old
    proxy: http://127.0.0.1:9000
    metadata:
      owner: user
  stale:
    cmd: stale
    metadata:
      acorn: true
  custom:
    cmd: custom
"#,
    )
    .unwrap();
    let models = [ModelDetails::init().id("qwen").name("qwen").path("/models/qwen.gguf").build()];
    let config = Config::default();
    let additive = config.upsert(existing.clone(), &models, false).unwrap();
    let additive_text = serde_norway::to_string(&additive).unwrap();
    assert!(additive_text.contains("healthCheckTimeout: 120"));
    assert!(additive_text.contains("proxy: http://127.0.0.1:9000"));
    assert!(additive_text.contains("owner: user"));
    assert!(additive_text.contains("acorn: true"));
    assert!(additive_text.contains("stale:"));
    assert!(additive_text.contains("custom:"));
    assert!(additive_text.contains("cmd: |-"));
    assert!(additive_text.contains("llama-server\n"));
    assert!(additive_text.contains("--port ${PORT}\n"));
    assert!(additive_text.contains("--model /models/qwen.gguf\n"));
    assert!(!additive_text.contains("modelsDir"));
    let pruned = config.upsert(existing, &models, true).unwrap();
    let pruned_text = serde_norway::to_string(&pruned).unwrap();
    assert!(!pruned_text.contains("stale:"));
    assert!(pruned_text.contains("custom:"));
    let repeated = config.upsert(pruned, &models, true).unwrap();
    assert_eq!(pruned_text, serde_norway::to_string(&repeated).unwrap());
}
#[test]
fn test_validate_rejects_invalid_context_ttl_environment_and_aliases() {
    let model_ids = vec!["qwen".to_string(), "gemma".to_string()];
    let invalid = [
        Config {
            inner: Inner {
                context_size: Some(0),
                ..Default::default()
            },
            ..Default::default()
        },
        Config {
            inner: Inner {
                ttl: Some(-1),
                ..Default::default()
            },
            ..Default::default()
        },
        Config {
            inner: Inner {
                environment: Some(vec![EnvironmentVariable::from("INVALID")]),
                ..Default::default()
            },
            ..Default::default()
        },
        Config {
            inner: Inner {
                models: Some(
                    [
                        (
                            "qwen".to_string(),
                            ModelOverride {
                                aliases: Some(vec![Alias::from("shared")]),
                                ..Default::default()
                            },
                        ),
                        (
                            "gemma".to_string(),
                            ModelOverride {
                                aliases: Some(vec![Alias::from("shared")]),
                                ..Default::default()
                            },
                        ),
                    ]
                    .into_iter()
                    .collect(),
                ),
                ..Default::default()
            },
            ..Default::default()
        },
    ];
    invalid.iter().for_each(|config| {
        assert!(Validate::validate(config).is_err() || ModelValidation::from((config, model_ids.as_slice())).validate().is_err());
    });
}