pub mod cloud;
pub mod models;
pub mod orchestrator;
pub mod settings;
pub mod theme;
#[cfg(test)]
mod tests {
use crate::models::ModelValidator;
use crate::settings::Settings;
use crate::theme::ThemeVariant;
#[test]
fn test_settings_default() {
let settings = Settings::default();
assert_eq!(settings.theme, ThemeVariant::EverforestDark);
assert_eq!(settings.endpoint, "localhost:11434");
assert_eq!(settings.local_model, "[SELECT]");
assert_eq!(settings.api_key, "sk-or-v1-982...b52");
assert_eq!(settings.cloud_model, "[SELECT]");
}
#[test]
fn test_settings_validation() {
let settings = Settings::default();
let result = settings.is_valid();
assert!(result.is_err());
}
#[tokio::test]
async fn test_openrouter_integration() {
let validator = ModelValidator::new();
let api_key = "sk-or-v1-test-key-redacted";
let result = validator.fetch_openrouter_models(api_key).await;
match result {
Ok(models) => {
println!("โ
OpenRouter test passed! Found {} models", models.len());
assert!(!models.is_empty(), "Should find at least some models");
let free_models: Vec<_> = models
.iter()
.filter(|m| m.pricing.prompt == "0" && m.pricing.completion == "0")
.collect();
let paid_models: Vec<_> = models
.iter()
.filter(|m| m.pricing.prompt != "0" || m.pricing.completion != "0")
.collect();
println!(
" ๐ Found {} free models, {} paid models",
free_models.len(),
paid_models.len()
);
assert!(
!free_models.is_empty(),
"Should have at least some free models"
);
assert!(
!paid_models.is_empty(),
"Should have at least some paid models"
);
println!(" ๐ Free models:");
for (i, model) in free_models.iter().enumerate().take(3) {
println!(
" {}. {} ({}k context)",
i + 1,
model.name,
model.context_length / 1000
);
}
println!(" ๐ฐ Paid models:");
for (i, model) in paid_models.iter().enumerate().take(2) {
println!(
" {}. {} ({}k context)",
i + 1,
model.name,
model.context_length / 1000
);
}
}
Err(e) => {
println!("โ OpenRouter test failed: {}", e);
panic!("OpenRouter integration test failed: {}", e);
}
}
}
#[tokio::test]
async fn test_ollama_integration() {
let validator = ModelValidator::new();
let endpoint = "localhost:11434";
let result = validator.fetch_ollama_models(endpoint).await;
match result {
Ok(models) => {
println!("โ
Ollama test passed! Found {} local models", models.len());
for (i, model) in models.iter().enumerate() {
println!(" {}. {} ({})", i + 1, model.name, model.size);
}
if let Some(first_model) = models.first() {
println!("๐ Testing validation with model: {}", first_model.name);
let validation_result = validator
.validate_local_endpoint(endpoint, &first_model.name)
.await;
match validation_result {
Ok(()) => println!("โ
Local endpoint validation passed!"),
Err(e) => println!("โ Local endpoint validation failed: {}", e),
}
}
}
Err(e) => {
println!("โ ๏ธ Ollama test skipped - not running or accessible: {}", e);
}
}
}
#[tokio::test]
async fn test_dual_endpoint_validation() {
use crate::settings::Settings;
println!("๐ Testing dual endpoint validation...");
let validator = ModelValidator::new();
let ollama_models = validator.fetch_ollama_models("localhost:11434").await;
let api_key = "sk-or-v1-test-key-redacted";
let openrouter_models = validator.fetch_openrouter_models(api_key).await;
match (ollama_models, openrouter_models) {
(Ok(local_models), Ok(cloud_models))
if !local_models.is_empty() && !cloud_models.is_empty() =>
{
println!("๐ฆ Testing with real models:");
println!(" Local: {}", local_models[0].name);
println!(" Cloud: {}", cloud_models[0].name);
let mut settings = Settings::default();
settings.local_model = local_models[0].name.clone();
settings.cloud_model = cloud_models[0].id.clone();
settings.api_key = api_key.to_string();
match settings.validate_local_only().await {
Ok(()) => println!("โ
Local-only validation passed!"),
Err(e) => println!("โ Local-only validation failed: {:?}", e),
}
match settings.validate_cloud_only().await {
Ok(()) => println!("โ
Cloud-only validation passed!"),
Err(e) => println!("โ Cloud-only validation failed: {:?}", e),
}
match settings.validate_endpoints().await {
Ok(()) => println!("โ
Dual endpoint validation passed!"),
Err(e) => println!("โ Dual endpoint validation failed: {:?}", e),
}
}
(Ok(local_models), Ok(cloud_models)) => {
println!("๐ฆ Models found but lists might be empty:");
println!(" Local models: {}", local_models.len());
println!(" Cloud models: {}", cloud_models.len());
}
(Err(local_err), Ok(cloud_models)) => {
println!("โ ๏ธ Local endpoint not available: {}", local_err);
println!(
"โ
Cloud endpoint working with {} models",
cloud_models.len()
);
}
(Ok(local_models), Err(cloud_err)) => {
println!(
"โ
Local endpoint working with {} models",
local_models.len()
);
println!("โ ๏ธ Cloud endpoint not available: {}", cloud_err);
}
(Err(local_err), Err(cloud_err)) => {
println!("โ ๏ธ Both endpoints unavailable:");
println!(" Local: {}", local_err);
println!(" Cloud: {}", cloud_err);
}
}
}
#[tokio::test]
async fn test_modal_model_loading() {
println!("๐งช Testing modal model loading scenarios...");
let validator = ModelValidator::new();
let api_key = "sk-or-v1-test-key-redacted";
match validator.fetch_openrouter_models(api_key).await {
Ok(models) => {
println!("โ
Cloud models loaded for modal: {} models", models.len());
assert!(!models.is_empty(), "Should have models for display");
let mut free_count = 0;
let mut paid_count = 0;
let mut found_paid_after_free = false;
for model in models.iter() {
let is_free = model.pricing.prompt == "0" && model.pricing.completion == "0";
if is_free {
free_count += 1;
assert!(
!found_paid_after_free,
"Free models should come before paid models"
);
} else {
paid_count += 1;
found_paid_after_free = true;
}
}
println!(
" ๐ Model breakdown: {} free, {} paid",
free_count, paid_count
);
assert!(free_count > 0, "Should have at least some free models");
assert!(paid_count > 0, "Should have at least some paid models");
for model in models.iter().take(3) {
let pricing_type =
if model.pricing.prompt == "0" && model.pricing.completion == "0" {
"(free)"
} else {
"(paid)"
};
println!(
" - {} {} ({} tokens)",
model.name, pricing_type, model.context_length
);
assert!(!model.name.is_empty(), "Model name should not be empty");
assert!(!model.id.is_empty(), "Model ID should not be empty");
assert!(
model.context_length > 0,
"Context length should be positive"
);
}
}
Err(e) => {
panic!("Failed to load OpenRouter models for modal: {}", e);
}
}
match validator.fetch_ollama_models("localhost:11434").await {
Ok(models) => {
println!("โ
Local models loaded for modal: {} models", models.len());
for model in models.iter().take(3) {
println!(" - {} ({})", model.name, model.size);
assert!(!model.name.is_empty(), "Model name should not be empty");
assert!(!model.size.is_empty(), "Model size should not be empty");
}
}
Err(e) => {
println!("โ ๏ธ Local models test skipped (Ollama not running): {}", e);
}
}
}
#[tokio::test]
async fn test_provider_detection() {
use crate::models::{LocalProvider, ModelValidator};
println!("๐งช Testing provider detection system...");
let validator = ModelValidator::new();
let ollama_provider = validator.detect_provider_type("localhost:11434").await;
let lmstudio_provider = validator.detect_provider_type("localhost:1234").await;
let custom_provider = validator.detect_provider_type("localhost:8080").await;
println!("Provider detection results:");
println!(" Ollama (11434): {:?}", ollama_provider);
println!(" LM Studio (1234): {:?}", lmstudio_provider);
println!(" Custom (8080): {:?}", custom_provider);
if ollama_provider == LocalProvider::Ollama {
println!("Testing local model fetching...");
match validator.fetch_local_models("localhost:11434").await {
Ok(models) => {
println!("โ
Successfully fetched {} local models", models.len());
for model in models.iter().take(3) {
println!(" - {} ({:?}, {})", model.name, model.provider, model.size);
}
}
Err(e) => {
println!(
"โ ๏ธ Could not fetch local models (Ollama not running): {}",
e
);
}
}
} else {
println!("โ ๏ธ Ollama not detected, skipping local model test");
}
println!("โ
Provider detection test completed!");
}
#[test]
fn test_api_key_truncation() {
fn format_api_key_display(api_key: &str) -> String {
if api_key.is_empty() {
return String::new();
}
if api_key.len() <= 21 {
api_key.to_string()
} else {
format!("{}...{}", &api_key[..15], &api_key[api_key.len() - 3..])
}
}
let test_key = "sk-or-v1-test-key-redacted";
let formatted = format_api_key_display(test_key);
println!("๐งช Testing API key truncation:");
println!(" Original: {} (len: {})", test_key, test_key.len());
println!(" Formatted: {} (len: {})", formatted, formatted.len());
assert_eq!(formatted, "sk-or-v1-7d9200...3ac");
assert_eq!(formatted.len(), 21);
let short_key = "sk-test-123";
let short_formatted = format_api_key_display(short_key);
assert_eq!(short_formatted, short_key);
let empty_formatted = format_api_key_display("");
assert_eq!(empty_formatted, "");
println!("โ
API key truncation test passed!");
}
}