use llm_manager::config::Config;
use llm_manager::models::*;
use llm_manager::tui::app::{ActivePanel, App, GlobalMode, ModelsMode};
#[test]
fn app_new_is_running() {
let config = Config::default();
let app = App::new(config);
assert!(app.running);
}
#[test]
fn app_new_initial_state() {
let config = Config::default();
let app = App::new(config);
assert!(app.models.is_empty());
assert!(app.selected_model_idx.is_none());
assert!(matches!(app.models_mode, ModelsMode::List));
assert!(app.search.local_filter.is_empty());
assert!(!app.search.filtering_local);
assert!(!app.log.log_expanded);
assert!(matches!(app.ui.global_mode, GlobalMode::Normal));
assert_eq!(app.ui.active_panel, ActivePanel::Models);
}
#[test]
fn app_new_has_settings() {
let config = Config::default();
let app = App::new(config);
assert!(app.settings.context_length > 0);
assert!(app.settings.threads > 0);
}
#[test]
fn app_new_empty_log() {
let config = Config::default();
let app = App::new(config);
assert!(!app.log.log_entries.is_empty());
}
#[test]
fn app_new_no_server_handle() {
let config = Config::default();
let app = App::new(config);
assert!(app.server.server_handle.is_none());
}
#[test]
fn app_new_no_download_progress() {
let config = Config::default();
let app = App::new(config);
assert!(app.download.download_progress.is_empty());
}
#[test]
fn app_new_metrics_default() {
let config = Config::default();
let app = App::new(config);
assert!(!app.metrics.loaded);
assert_eq!(app.metrics.tps, 0.0);
}
#[test]
fn app_new_default_left_pct() {
let config = Config::default();
let app = App::new(config);
assert_eq!(app.ui.left_pct, 55);
}
#[test]
fn app_get_filtered_model_indices_empty() {
let config = Config::default();
let mut app = App::new(config);
app.models = vec![];
let indices = app.get_filtered_model_indices();
assert!(indices.is_empty());
}
#[test]
fn app_get_filtered_model_indices_no_filter() {
let config = Config::default();
let mut app = App::new(config);
app.models = vec![
DiscoveredModel {
path: "/a.gguf".into(),
name: "model-a".into(),
file_size: 1000,
display_name: "a".into(),
},
DiscoveredModel {
path: "/b.gguf".into(),
name: "model-b".into(),
file_size: 2000,
display_name: "b".into(),
},
];
let indices = app.get_filtered_model_indices();
assert_eq!(indices, vec![0, 1]);
}
#[test]
fn app_get_filtered_model_indices_case_insensitive() {
let config = Config::default();
let mut app = App::new(config);
app.models = vec![
DiscoveredModel {
path: "/a.gguf".into(),
name: "Qwen2.5-7B".into(),
file_size: 1000,
display_name: "Qwen2.5-7B".into(),
},
DiscoveredModel {
path: "/b.gguf".into(),
name: "Llama3-8B".into(),
file_size: 2000,
display_name: "Llama3-8B".into(),
},
];
app.search.local_filter = "qwen".into();
let indices = app.get_filtered_model_indices();
assert_eq!(indices, vec![0]);
}
#[test]
fn app_get_filtered_model_indices_no_match() {
let config = Config::default();
let mut app = App::new(config);
app.models = vec![DiscoveredModel {
path: "/a.gguf".into(),
name: "model-a".into(),
file_size: 1000,
display_name: "a".into(),
}];
app.search.local_filter = "nonexistent".into();
let indices = app.get_filtered_model_indices();
assert!(indices.is_empty());
}
#[test]
fn app_is_panel_visible_default_all_hidden() {
let config = Config::default();
let app = App::new(config);
for i in 0..6 {
assert!(app.is_panel_visible(i));
}
}
#[test]
fn app_toggle_panel_visibility_hides() {
let config = Config::default();
let mut app = App::new(config);
app.toggle_panel_visibility(0);
assert!(!app.is_panel_visible(0));
}
#[test]
fn app_toggle_panel_visibility_shows() {
let config = Config::default();
let mut app = App::new(config);
app.toggle_panel_visibility(0);
assert!(!app.is_panel_visible(0));
app.toggle_panel_visibility(0);
assert!(app.is_panel_visible(0));
}
#[test]
fn app_toggle_panel_visibility_log_collapses() {
let config = Config::default();
let mut app = App::new(config);
app.log.log_expanded = true;
app.toggle_panel_visibility(5);
assert!(!app.is_panel_visible(5));
assert!(!app.log.log_expanded); }
#[test]
fn app_selected_model_none_when_empty() {
let config = Config::default();
let app = App::new(config);
assert!(app.selected_model().is_none());
}
#[test]
fn app_selected_model_returns_some() {
let config = Config::default();
let mut app = App::new(config);
app.models = vec![DiscoveredModel {
path: "/model.gguf".into(),
name: "test".into(),
file_size: 1000,
display_name: "test".into(),
}];
app.selected_model_idx = Some(0);
assert!(app.selected_model().is_some());
}
#[test]
fn app_is_model_loaded_false_when_not_loaded() {
let config = Config::default();
let app = App::new(config);
assert!(!app.is_model_loaded("nonexistent"));
}
#[test]
fn app_is_model_loaded_true_when_loaded() {
let config = Config::default();
let mut app = App::new(config);
app.model_states.insert(
"model.gguf".into(),
ModelState::Loaded {
port: 8080,
pid: 1234,
},
);
assert!(app.is_model_loaded("model.gguf"));
}
#[test]
fn app_is_model_loaded_false_when_available() {
let config = Config::default();
let mut app = App::new(config);
app.model_states
.insert("model.gguf".into(), ModelState::Available);
assert!(!app.is_model_loaded("model.gguf"));
}
#[test]
fn app_search_results_len_zero_not_in_search_mode() {
let config = Config::default();
let app = App::new(config);
assert_eq!(app.search_results_len(), 0);
}
#[test]
fn app_search_results_len_in_search_mode() {
let config = Config::default();
let mut app = App::new(config);
app.models_mode = ModelsMode::Search {
query: "test".into(),
results: vec![
SearchResult {
model_id: "a".into(),
model_name: "A".into(),
tags: vec![],
downloads: 0,
likes: 0,
pipeline_tag: None,
size: None,
parameters: None,
capabilities: vec![],
context_length: None,
readme: None,
quantization: None,
license: None,
trending_score: 0,
created_at: None,
downloaded: false,
},
SearchResult {
model_id: "b".into(),
model_name: "B".into(),
tags: vec![],
downloads: 0,
likes: 0,
pipeline_tag: None,
size: None,
parameters: None,
capabilities: vec![],
context_length: None,
readme: None,
quantization: None,
license: None,
trending_score: 0,
created_at: None,
downloaded: false,
},
],
sort_by: SearchSort::Relevance,
show_readme: false,
page: 0,
loading: false,
has_more: false,
};
assert_eq!(app.search_results_len(), 2);
}
#[test]
fn app_get_api_port_str_returns_string() {
let config = Config::default();
let app = App::new(config);
let port_str = app.get_api_port_str();
assert!(!port_str.is_empty());
}
#[test]
fn app_add_log_adds_entry() {
let config = Config::default();
let mut app = App::new(config);
let initial_len = app.log.log_entries.len();
app.add_log("test message", llm_manager::config::LogLevel::Info);
assert_eq!(app.log.log_entries.len(), initial_len + 1);
}
#[test]
fn app_add_log_sets_redraw() {
let config = Config::default();
let mut app = App::new(config);
app.add_log("test", llm_manager::config::LogLevel::Info);
}
#[test]
fn app_new_settings_match_config() {
let config = Config::default();
let app = App::new(config);
assert_eq!(app.settings.context_length, 131072);
}
#[test]
fn app_new_model_settings_cache_initialized() {
let config = Config::default();
let app = App::new(config);
assert_eq!(
app.settings.context_length,
app.model_settings_cache.context_length
);
}
#[test]
fn app_new_loading_progress_zero() {
let config = Config::default();
let app = App::new(config);
assert_eq!(app.loading.loading_progress, 0.0);
}
#[test]
fn app_new_no_loading_phases() {
let config = Config::default();
let app = App::new(config);
assert!(app.loading.loading_phases.is_empty());
}
#[test]
fn app_new_no_bench_tune_state() {
let config = Config::default();
let app = App::new(config);
assert!(app.bench_tune.bench_tune_progress.is_none());
assert!(app.bench_tune.bench_tune_results.is_empty());
assert!(!app.bench_tune.bench_tune_running);
}
#[test]
fn app_new_empty_metadata_cache() {
let config = Config::default();
let app = App::new(config);
assert!(app.search.gguf_metadata_cache.is_empty());
}
#[test]
fn app_new_vram_estimate_zero() {
let config = Config::default();
let app = App::new(config);
assert_eq!(app.loading.vram_estimate, 0);
}
#[test]
fn app_new_no_readme_cache() {
let config = Config::default();
let app = App::new(config);
assert!(app.search.readme_cache.is_none());
}
#[test]
fn app_new_no_backend_resolving() {
let config = Config::default();
let app = App::new(config);
assert!(!app.pending.backend_resolving);
assert!(app.pending.backend_resolve_handle.is_none());
}
#[test]
fn app_new_model_metadata_zero() {
let config = Config::default();
let app = App::new(config);
assert_eq!(app.loading.model_total_layers, 0);
assert_eq!(app.loading.model_hidden_size, 0);
assert_eq!(app.loading.model_n_ctx_train, 0);
assert_eq!(app.loading.model_n_head, 0);
assert_eq!(app.loading.model_n_kv_head, 0);
}
#[test]
fn app_new_max_threads_positive() {
let config = Config::default();
let app = App::new(config);
assert!(app.max_threads > 0);
}
#[test]
fn app_new_no_pending_operations() {
let config = Config::default();
let app = App::new(config);
assert!(app.pending.pending_download.is_none());
assert!(app.pending.pending_deletion.is_none());
assert!(app.pending.pending_spawn.is_none());
assert!(app.pending.pending_api_load.is_none());
assert!(app.pending.pending_api_unload.is_none());
assert!(app.pending.pending_kill.is_none());
assert!(!app.download.downloading);
}
#[test]
fn app_new_server_mode_normal() {
let config = Config::default();
let app = App::new(config);
assert_eq!(app.server_mode, ServerMode::Normal);
}
#[test]
fn app_new_panel_help_hidden() {
let config = Config::default();
let app = App::new(config);
assert!(!app.ui.panel_help);
}