#[test]
fn the_status_pill_does_not_claim_to_be_serving_with_no_model() {
use clap::Parser as _;
let mut args = crate::cli::ServeArgs::parse_from(["met", "m"]);
args.model = None;
let mut app = crate::tui::app::App::new(args);
assert!(app.awaiting_model, "no model was named");
app.progress.ready = true;
let pill = super::status_pill(&app);
assert!(
pill.content.contains("NO MODEL"),
"a bound socket is not a loaded model: {:?}",
pill.content
);
app.awaiting_model = false;
assert!(super::status_pill(&app).content.contains("SERVING"));
}
#[test]
fn the_chip_strip_describes_the_running_config_not_the_boot_argv() {
use clap::Parser as _;
let boot = crate::cli::ServeArgs::parse_from(["met", "m"]);
let boot_chips = crate::tui::logo::badges(&boot, false);
let boot_text: String = boot_chips.iter().map(|b| b.text.clone()).collect();
let mut live = crate::cli::ServeArgs::parse_from(["met", "org/loaded"]);
live.max_batch_size =
metrale_model_engine::factory::SlotRequest::Count(boot.max_batch_size.ceiling() + 7);
let live_chips = crate::tui::logo::badges(&live, false);
let live_text: String = live_chips.iter().map(|b| b.text.clone()).collect();
assert!(live_text.contains("org/loaded"), "names the live model");
assert_ne!(
boot_text, live_text,
"the strip must be able to differ from the boot argv"
);
assert!(
live_text.contains(&(boot.max_batch_size.ceiling() + 7).to_string()),
"and it reports the live batch size: {live_text}"
);
}
#[test]
fn the_chip_strip_asserts_nothing_about_a_model_that_is_not_loaded() {
use clap::Parser as _;
let args = crate::cli::ServeArgs::parse_from(["met"]);
let text: String = crate::tui::logo::badges(&args, true)
.iter()
.map(|b| b.text.clone())
.collect::<Vec<_>>()
.join(" ");
for claim in ["kv ", "lm ", "mtp ", "batch ", "ctx ", "sched ", "<model>"] {
assert!(
!text.contains(claim),
"awaiting strip must not claim {claim:?}: {text}"
);
}
assert!(
text.contains(&format!(":{}", args.port)),
"the bound port is true with no model: {text}"
);
assert!(
text.contains("Library"),
"and it says how to fix it: {text}"
);
}
#[test]
fn the_header_mini_strip_does_not_claim_a_kv_dtype_with_no_model() {
use clap::Parser as _;
let mut args = crate::cli::ServeArgs::parse_from(["met", "m"]);
args.model = None;
let mut app = crate::tui::app::App::new(args);
assert!(app.awaiting_model, "no model was named");
let line = super::header_line(&app);
assert!(!line.contains("kv "), "no dtype with no model: {line}");
assert!(line.contains("Library"), "says the way out: {line}");
assert!(
line.contains(&format!(":{}", app.args.port)),
"keeps the bound port: {line}"
);
app.awaiting_model = false;
let line = super::header_line(&app);
assert!(
line.contains("kv "),
"a loaded model has a KV dtype: {line}"
);
}
mod rendered {
use crate::tui::render::harness::{has, screen};
fn head(rows: &[String], tall: bool) -> Vec<String> {
rows[..if tall { 3 } else { 1 }].to_vec()
}
#[test]
fn a_tall_terminal_gets_the_wordmark_and_the_mini_strip() {
let a = crate::tui::render::tests::app();
let rows = screen(&a, 160, 48);
let head = head(&rows, true);
assert!(has(&head, "M E T R A L E"), "{head:#?}");
assert!(has(&head, "E N G I N E"), "{head:#?}");
assert!(has(&head, "SERVING") || has(&head, "LOADING"), "{head:#?}");
assert!(has(&head, "up 0:00:"), "the uptime clock:\n{head:#?}");
assert!(
has(&head, "kv "),
"and the strip that says what is running:\n{head:#?}"
);
}
#[test]
fn a_short_terminal_gets_one_row_and_drops_the_strip_rather_than_the_status() {
let a = crate::tui::render::tests::app();
let rows = screen(&a, 160, 27);
let head = head(&rows, false);
assert!(has(&head, "Metrale Engine"), "{head:#?}");
assert!(!has(&head, "M E T R A L E"), "{head:#?}");
assert!(has(&head, "SERVING") || has(&head, "LOADING"), "{head:#?}");
assert!(has(&head, "up 0:00:"));
}
#[test]
fn the_pill_and_the_strip_agree_about_whether_a_model_is_loaded() {
let mut a = crate::tui::render::tests::app();
a.awaiting_model = true;
a.progress.ready = true;
let head = head(&screen(&a, 160, 48), true);
assert!(has(&head, "NO MODEL"), "{head:#?}");
assert!(has(&head, "press 4 for Library"), "{head:#?}");
assert!(
!has(&head, "kv "),
"no dtype for a process that loaded nothing:\n{head:#?}"
);
}
#[test]
fn the_header_draws_at_widths_too_narrow_for_anything_it_wants_to_say() {
for (w, h) in [(1u16, 1u16), (8, 30), (20, 48), (40, 28)] {
assert_eq!(
screen(&crate::tui::render::tests::app(), w, h).len(),
h as usize,
"{w}x{h}"
);
}
}
}
#[test]
fn uptime_keeps_counting_past_an_hour_a_day_and_a_hundred_minutes() {
use super::fmt_uptime;
assert_eq!(fmt_uptime(0), "up 0:00:00");
assert_eq!(fmt_uptime(59), "up 0:00:59");
assert_eq!(fmt_uptime(60), "up 0:01:00");
assert_eq!(fmt_uptime(100 * 60), "up 1:40:00", "100 minutes is 1h40m");
assert_eq!(fmt_uptime(3_600), "up 1:00:00");
assert_eq!(fmt_uptime(86_399), "up 23:59:59");
assert_eq!(fmt_uptime(86_400), "up 1d 00:00");
assert_eq!(fmt_uptime(3 * 86_400 + 4 * 3_600 + 12 * 60), "up 3d 04:12");
let mut seen = std::collections::HashSet::new();
for m in 0..(48 * 60) {
assert!(
seen.insert(fmt_uptime(m * 60)),
"minute {m} rendered a string already used earlier"
);
}
}