use lc::config::{Config, ProviderConfig};
use std::collections::HashMap;
use std::path::PathBuf;
use std::sync::Once;
const TEST_PROVIDER_PREFIX: &str = "test-";
static BUILD_ONCE: Once = Once::new();
pub struct TestEnvironment {
pub config_dir: PathBuf,
}
impl TestEnvironment {
pub fn new() -> Self {
let config_dir = lc::config::Config::config_dir().expect("Failed to get config directory");
TestEnvironment { config_dir }
}
#[allow(dead_code)]
pub fn config_path(&self) -> &PathBuf {
&self.config_dir
}
pub fn verify_test_isolation(&self) {
let path_str = self.config_dir.to_string_lossy();
assert!(
path_str.contains("lc_test") || path_str.contains("tmp") || path_str.contains("temp"),
"Config directory should be in temp location for tests: {}",
path_str
);
#[cfg(target_os = "macos")]
assert!(
!path_str.contains("Library/Application Support/lc"),
"Should not use production config directory in tests"
);
#[cfg(target_os = "linux")]
assert!(
!path_str.contains(".local/share/lc"),
"Should not use production config directory in tests"
);
#[cfg(target_os = "windows")]
assert!(
!path_str.contains("AppData") || path_str.contains("Temp"),
"Should not use production config directory in tests"
);
}
}
#[allow(dead_code)]
pub fn get_test_binary_path() -> PathBuf {
BUILD_ONCE.call_once(|| {
let output = std::process::Command::new("cargo")
.args(&["build", "--bin", "lc"])
.output()
.expect("Failed to build test binary");
if !output.status.success() {
panic!(
"Failed to build test binary: {}",
String::from_utf8_lossy(&output.stderr)
);
}
});
let mut path = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
path.push("target");
path.push("debug");
path.push("lc");
#[cfg(windows)]
path.set_extension("exe");
if !path.exists() {
panic!("Test binary not found at: {:?}", path);
}
path
}
#[allow(dead_code)]
pub fn get_test_provider_name(base_name: &str) -> String {
format!("{}{}", TEST_PROVIDER_PREFIX, base_name)
}
#[allow(dead_code)]
pub fn create_test_config() -> Config {
Config {
providers: HashMap::new(),
default_provider: None,
default_model: None,
aliases: HashMap::new(),
system_prompt: None,
templates: HashMap::new(),
max_tokens: None,
temperature: None,
stream: None,
}
}
#[allow(dead_code)]
pub fn verify_test_isolation() {
let test_env = TestEnvironment::new();
test_env.verify_test_isolation();
}
#[allow(dead_code)]
pub fn create_test_provider_config(endpoint: &str) -> ProviderConfig {
ProviderConfig {
endpoint: endpoint.to_string(),
api_key: None, models: vec!["test-model-1".to_string(), "test-model-2".to_string()],
models_path: "/models".to_string(),
chat_path: "/chat/completions".to_string(),
images_path: Some("/images/generations".to_string()),
embeddings_path: Some("/embeddings".to_string()),
audio_path: Some("/audio/transcriptions".to_string()),
speech_path: Some("/audio/speech".to_string()),
headers: HashMap::new(),
token_url: None,
cached_token: None,
auth_type: None,
vars: std::collections::HashMap::new(),
chat_templates: None,
images_templates: None,
embeddings_templates: None,
models_templates: None,
audio_templates: None,
speech_templates: None,
}
}
#[allow(dead_code)]
pub fn create_config_with_providers() -> Config {
let mut config = Config {
providers: HashMap::new(),
default_provider: None,
default_model: None,
aliases: HashMap::new(),
system_prompt: None,
templates: HashMap::new(),
max_tokens: None,
temperature: None,
stream: None,
};
let openai_name = get_test_provider_name("openai");
let anthropic_name = get_test_provider_name("anthropic");
config.providers.insert(
openai_name.clone(),
create_test_provider_config("https://api.openai.com"),
);
config.providers.insert(
anthropic_name.clone(),
create_test_provider_config("https://api.anthropic.com"),
);
config.default_provider = Some(openai_name.clone());
let mut keys = lc::keys::KeysConfig::load().unwrap_or_default();
keys.set_api_key(openai_name.clone(), "test-api-key".to_string())
.unwrap();
keys.set_api_key(anthropic_name.clone(), "test-api-key".to_string())
.unwrap();
config
}
pub mod test_data {
#[allow(dead_code)]
pub const TEST_PROVIDERS: &[(&str, &str)] = &[
("test-openai", "https://api.openai.com"),
("test-anthropic", "https://api.anthropic.com"),
("test-cohere", "https://api.cohere.ai"),
("test-huggingface", "https://api-inference.huggingface.co"),
];
#[allow(dead_code)]
pub const TEST_MODELS: &[&str] =
&["gpt-4", "gpt-3.5-turbo", "claude-3-opus", "claude-3-sonnet"];
#[allow(dead_code)]
pub const TEST_HEADERS: &[(&str, &str)] = &[
("X-API-Version", "2023-12-01"),
("X-Client", "lc-cli"),
("User-Agent", "LC/1.0"),
];
}
pub mod assertions {
use lc::config::Config;
#[allow(dead_code)]
pub fn assert_provider_exists(config: &Config, name: &str) {
assert!(
config.has_provider(name),
"Provider '{}' should exist",
name
);
}
#[allow(dead_code)]
pub fn assert_provider_not_exists(config: &Config, name: &str) {
assert!(
!config.has_provider(name),
"Provider '{}' should not exist",
name
);
}
#[allow(dead_code)]
pub fn assert_provider_endpoint(config: &Config, name: &str, expected_endpoint: &str) {
let provider = config.get_provider(name).expect("Provider should exist");
assert_eq!(
provider.endpoint, expected_endpoint,
"Provider '{}' endpoint should be '{}'",
name, expected_endpoint
);
}
#[allow(dead_code)]
pub fn assert_provider_has_api_key(_config: &Config, name: &str) {
let keys = lc::keys::KeysConfig::load().expect("Should load keys config");
assert!(
keys.has_auth(name),
"Provider '{}' should have an API key",
name
);
}
#[allow(dead_code)]
pub fn assert_provider_api_key(_config: &Config, name: &str, expected_key: &str) {
let keys = lc::keys::KeysConfig::load().expect("Should load keys config");
let actual_key = keys
.get_api_key(name)
.expect("Provider should have an API key");
assert_eq!(
actual_key, expected_key,
"Provider '{}' API key should be '{}'",
name, expected_key
);
}
#[allow(dead_code)]
pub fn assert_default_provider(config: &Config, expected_name: &str) {
assert_eq!(
config.default_provider.as_ref().unwrap(),
expected_name,
"Default provider should be '{}'",
expected_name
);
}
#[allow(dead_code)]
pub fn assert_header_exists(
config: &Config,
provider: &str,
header_name: &str,
expected_value: &str,
) {
let headers = config
.list_headers(provider)
.expect("Provider should exist");
assert_eq!(
headers.get(header_name).unwrap(),
expected_value,
"Header '{}' should have value '{}'",
header_name,
expected_value
);
}
#[allow(dead_code)]
pub fn assert_header_count(config: &Config, provider: &str, expected_count: usize) {
let headers = config
.list_headers(provider)
.expect("Provider should exist");
assert_eq!(
headers.len(),
expected_count,
"Provider '{}' should have {} headers",
provider,
expected_count
);
}
}