Files
owlen/crates/platform/config/tests/precedence.rs

308 lines
9.5 KiB
Rust

use config_agent::{load_settings, Settings};
use permissions::{Mode, PermissionDecision, Tool};
use llm_core::ProviderType;
use std::{env, fs};
#[test]
fn precedence_env_overrides_files() {
let tmp = tempfile::tempdir().unwrap();
let project_file = tmp.path().join(".owlen.toml");
fs::write(&project_file, r#"model="local-model""#).unwrap();
unsafe { env::set_var("OWLEN_MODEL", "env-model"); }
let s = load_settings(Some(tmp.path().to_str().unwrap())).unwrap();
assert_eq!(s.model, "env-model");
}
#[test]
fn default_mode_is_plan() {
let s = Settings::default();
assert_eq!(s.mode, "plan");
}
#[test]
fn settings_create_permission_manager_with_plan_mode() {
let s = Settings::default();
let mgr = s.create_permission_manager();
// Plan mode should allow read operations
assert_eq!(mgr.check(Tool::Read, None), PermissionDecision::Allow);
// Plan mode should ask for write operations
assert_eq!(mgr.check(Tool::Write, None), PermissionDecision::Ask);
}
#[test]
fn settings_parse_mode_from_config() {
let tmp = tempfile::tempdir().unwrap();
let project_file = tmp.path().join(".owlen.toml");
fs::write(&project_file, r#"mode="code""#).unwrap();
let s = load_settings(Some(tmp.path().to_str().unwrap())).unwrap();
assert_eq!(s.mode, "code");
assert_eq!(s.get_mode(), Mode::Code);
let mgr = s.create_permission_manager();
// Code mode should allow everything
assert_eq!(mgr.check(Tool::Write, None), PermissionDecision::Allow);
assert_eq!(mgr.check(Tool::Bash, None), PermissionDecision::Allow);
}
#[test]
fn default_provider_is_ollama() {
let s = Settings::default();
assert_eq!(s.provider, "ollama");
assert_eq!(s.get_provider(), Some(ProviderType::Ollama));
}
#[test]
fn provider_from_config_file() {
let tmp = tempfile::tempdir().unwrap();
let project_file = tmp.path().join(".owlen.toml");
fs::write(&project_file, r#"provider="anthropic""#).unwrap();
let s = load_settings(Some(tmp.path().to_str().unwrap())).unwrap();
assert_eq!(s.provider, "anthropic");
assert_eq!(s.get_provider(), Some(ProviderType::Anthropic));
}
#[test]
#[ignore] // Ignore due to env var interaction in parallel tests
fn provider_from_env_var() {
let tmp = tempfile::tempdir().unwrap();
unsafe {
env::set_var("OWLEN_PROVIDER", "openai");
env::remove_var("PROVIDER");
env::remove_var("ANTHROPIC_API_KEY");
env::remove_var("OPENAI_API_KEY");
}
let s = load_settings(Some(tmp.path().to_str().unwrap())).unwrap();
assert_eq!(s.provider, "openai");
assert_eq!(s.get_provider(), Some(ProviderType::OpenAI));
unsafe { env::remove_var("OWLEN_PROVIDER"); }
}
#[test]
#[ignore] // Ignore due to env var interaction in parallel tests
fn provider_from_provider_env_var() {
let tmp = tempfile::tempdir().unwrap();
unsafe {
env::set_var("PROVIDER", "anthropic");
env::remove_var("OWLEN_PROVIDER");
env::remove_var("ANTHROPIC_API_KEY");
env::remove_var("OPENAI_API_KEY");
}
let s = load_settings(Some(tmp.path().to_str().unwrap())).unwrap();
assert_eq!(s.provider, "anthropic");
assert_eq!(s.get_provider(), Some(ProviderType::Anthropic));
unsafe { env::remove_var("PROVIDER"); }
}
#[test]
fn anthropic_api_key_from_owlen_env() {
let tmp = tempfile::tempdir().unwrap();
let project_file = tmp.path().join(".owlen.toml");
fs::write(&project_file, r#"provider="anthropic""#).unwrap();
unsafe { env::set_var("OWLEN_ANTHROPIC_API_KEY", "sk-ant-test123"); }
let s = load_settings(Some(tmp.path().to_str().unwrap())).unwrap();
assert_eq!(s.anthropic_api_key, Some("sk-ant-test123".to_string()));
assert_eq!(s.get_provider_api_key(), Some("sk-ant-test123".to_string()));
unsafe { env::remove_var("OWLEN_ANTHROPIC_API_KEY"); }
}
#[test]
fn openai_api_key_from_owlen_env() {
let tmp = tempfile::tempdir().unwrap();
let project_file = tmp.path().join(".owlen.toml");
fs::write(&project_file, r#"provider="openai""#).unwrap();
unsafe { env::set_var("OWLEN_OPENAI_API_KEY", "sk-test-456"); }
let s = load_settings(Some(tmp.path().to_str().unwrap())).unwrap();
assert_eq!(s.openai_api_key, Some("sk-test-456".to_string()));
assert_eq!(s.get_provider_api_key(), Some("sk-test-456".to_string()));
unsafe { env::remove_var("OWLEN_OPENAI_API_KEY"); }
}
#[test]
#[ignore] // Ignore due to env var interaction in parallel tests
fn api_keys_from_config_file() {
let tmp = tempfile::tempdir().unwrap();
let project_file = tmp.path().join(".owlen.toml");
fs::write(&project_file, r#"
provider = "anthropic"
anthropic_api_key = "sk-ant-from-file"
openai_api_key = "sk-openai-from-file"
"#).unwrap();
// Clear any env vars that might interfere
unsafe {
env::remove_var("ANTHROPIC_API_KEY");
env::remove_var("OPENAI_API_KEY");
env::remove_var("OWLEN_ANTHROPIC_API_KEY");
env::remove_var("OWLEN_OPENAI_API_KEY");
}
let s = load_settings(Some(tmp.path().to_str().unwrap())).unwrap();
assert_eq!(s.anthropic_api_key, Some("sk-ant-from-file".to_string()));
assert_eq!(s.openai_api_key, Some("sk-openai-from-file".to_string()));
assert_eq!(s.get_provider_api_key(), Some("sk-ant-from-file".to_string()));
}
#[test]
#[ignore] // Ignore due to env var interaction in parallel tests
fn anthropic_api_key_from_standard_env() {
let tmp = tempfile::tempdir().unwrap();
let project_file = tmp.path().join(".owlen.toml");
fs::write(&project_file, r#"provider="anthropic""#).unwrap();
unsafe {
env::set_var("ANTHROPIC_API_KEY", "sk-ant-std");
env::remove_var("OWLEN_ANTHROPIC_API_KEY");
env::remove_var("PROVIDER");
env::remove_var("OWLEN_PROVIDER");
}
let s = load_settings(Some(tmp.path().to_str().unwrap())).unwrap();
assert_eq!(s.anthropic_api_key, Some("sk-ant-std".to_string()));
assert_eq!(s.get_provider_api_key(), Some("sk-ant-std".to_string()));
unsafe { env::remove_var("ANTHROPIC_API_KEY"); }
}
#[test]
#[ignore] // Ignore due to env var interaction in parallel tests
fn openai_api_key_from_standard_env() {
let tmp = tempfile::tempdir().unwrap();
let project_file = tmp.path().join(".owlen.toml");
fs::write(&project_file, r#"provider="openai""#).unwrap();
unsafe {
env::set_var("OPENAI_API_KEY", "sk-openai-std");
env::remove_var("OWLEN_OPENAI_API_KEY");
env::remove_var("PROVIDER");
env::remove_var("OWLEN_PROVIDER");
}
let s = load_settings(Some(tmp.path().to_str().unwrap())).unwrap();
assert_eq!(s.openai_api_key, Some("sk-openai-std".to_string()));
assert_eq!(s.get_provider_api_key(), Some("sk-openai-std".to_string()));
unsafe { env::remove_var("OPENAI_API_KEY"); }
}
#[test]
#[ignore] // Ignore due to env var interaction in parallel tests
fn owlen_prefix_overrides_standard_env() {
let tmp = tempfile::tempdir().unwrap();
unsafe {
env::set_var("ANTHROPIC_API_KEY", "sk-ant-std");
env::set_var("OWLEN_ANTHROPIC_API_KEY", "sk-ant-owlen");
}
let s = load_settings(Some(tmp.path().to_str().unwrap())).unwrap();
// OWLEN_ prefix should take precedence
assert_eq!(s.anthropic_api_key, Some("sk-ant-owlen".to_string()));
unsafe {
env::remove_var("ANTHROPIC_API_KEY");
env::remove_var("OWLEN_ANTHROPIC_API_KEY");
}
}
#[test]
fn effective_model_uses_provider_default() {
// Test Anthropic provider default
let mut s = Settings::default();
s.provider = "anthropic".to_string();
assert_eq!(s.get_effective_model(), "claude-sonnet-4-20250514");
// Test OpenAI provider default
s.provider = "openai".to_string();
assert_eq!(s.get_effective_model(), "gpt-4o");
// Test Ollama provider default
s.provider = "ollama".to_string();
assert_eq!(s.get_effective_model(), "qwen3:8b");
}
#[test]
fn effective_model_respects_explicit_model() {
let mut s = Settings::default();
s.provider = "anthropic".to_string();
s.model = "claude-opus-4-20250514".to_string();
// Should use explicit model, not provider default
assert_eq!(s.get_effective_model(), "claude-opus-4-20250514");
}
#[test]
fn test_get_set_model_for_provider() {
let mut s = Settings::default();
// Default fallback
assert_eq!(s.get_model_for_provider(ProviderType::Ollama), "qwen3:8b");
assert_eq!(s.get_model_for_provider(ProviderType::Anthropic), "claude-sonnet-4-20250514");
// Set specific model
s.set_model_for_provider(ProviderType::Ollama, "llama3:70b");
assert_eq!(s.get_model_for_provider(ProviderType::Ollama), "llama3:70b");
// Other provider still uses default
assert_eq!(s.get_model_for_provider(ProviderType::Anthropic), "claude-sonnet-4-20250514");
}
#[test]
fn test_permission_manager_integration() {
let mut s = Settings::default();
s.mode = "plan".to_string();
s.allowed_tools = vec!["bash:npm test*".to_string()];
s.disallowed_tools = vec!["bash:rm*".to_string()];
let pm = s.create_permission_manager();
// Allowed by list
assert_eq!(pm.check(Tool::Bash, Some("npm test")), PermissionDecision::Allow);
// Denied by list (takes precedence over mode default if matched)
assert_eq!(pm.check(Tool::Bash, Some("rm -rf /")), PermissionDecision::Deny);
// Mode default (Ask for bash in plan mode)
assert_eq!(pm.check(Tool::Bash, Some("ls -la")), PermissionDecision::Ask);
}