use super::*;
use crate::recipe::Recipe;
use crate::tui::data::library::LibraryEntry;
fn fixture(stem: &str) -> Recipe {
let path = std::path::Path::new(env!("CARGO_MANIFEST_DIR"))
.join("tests/fixtures/recipes/qwen3.6")
.join(format!("{stem}.yaml"));
let text = std::fs::read_to_string(&path).expect("fixture");
Recipe::parse(format!("qwen3.6/{stem}"), &text).expect("parses")
}
fn plain() -> Recipe {
fixture("qwen3.6-27b-fp8")
}
fn mtp() -> Recipe {
fixture("qwen3.6-27b-fp8-mtp")
}
fn with_weights(model: &str) -> LibraryEntry {
LibraryEntry {
id: model.into(),
snapshot_dir: Default::default(),
size_bytes: 1024,
has_weights: true,
model_type: "qwen3_6_moe".into(),
quant: "fp8".into(),
layers: 40,
hidden: 4096,
heads: 32,
experts: 128,
context: 65536,
optimized: true,
}
}
fn state(recipes: Vec<Recipe>) -> LibState {
let mut models: Vec<String> = recipes.iter().map(|r| r.model.clone()).collect();
models.sort();
models.dedup();
let local: Vec<LibraryEntry> = models.iter().map(|m| with_weights(m)).collect();
let mut s = LibState {
index: Index {
recipes,
..Index::default()
},
..LibState::default()
};
s.rebuild(&local);
s
}
fn flag(s: &LibState, name: &str) -> Option<String> {
let argv = s.preview_argv()?;
let i = argv.iter().position(|a| a == name)?;
argv.get(i + 1).cloned()
}
#[test]
fn several_recipes_for_one_checkpoint_are_one_row_with_several_cards() {
let s = state(vec![mtp(), plain()]);
assert_eq!(s.rows.len(), 1, "one row: {:?}", s.rows[0].model);
assert_eq!(s.cards().len(), 2, "both recipes are behind it");
let ids: Vec<&str> = s.cards().iter().map(|r| r.id.as_str()).collect();
assert_eq!(
ids,
["qwen3.6/qwen3.6-27b-fp8", "qwen3.6/qwen3.6-27b-fp8-mtp"],
"in id order, regardless of the order the fetch returned them"
);
}
#[test]
fn the_card_the_cursor_is_on_is_the_recipe_that_reaches_the_serve_args() {
let mut s = state(vec![mtp(), plain()]);
s.open_cards().expect("cards open");
s.card = 0;
s.open_config().expect("form opens");
assert_eq!(
s.config_recipe().expect("recipe").id,
"qwen3.6/qwen3.6-27b-fp8"
);
assert_eq!(flag(&s, "--max-seq-len").as_deref(), Some("262144"));
assert_eq!(flag(&s, "--kv-cache-dtype").as_deref(), Some("fp8"));
let plain_argv = s.preview_argv().expect("renders");
assert!(
!plain_argv.iter().any(|a| a == "--speculative"),
"this recipe measures speculation OFF: {plain_argv:?}"
);
s.view = View::Cards;
s.card = 1;
s.open_config().expect("form opens");
assert_eq!(
s.config_recipe().expect("recipe").id,
"qwen3.6/qwen3.6-27b-fp8-mtp"
);
assert_eq!(flag(&s, "--max-seq-len").as_deref(), Some("60000"));
assert_eq!(flag(&s, "--kv-cache-dtype").as_deref(), Some("bf16"));
let mtp_argv = s.preview_argv().expect("renders");
assert!(
mtp_argv.iter().any(|a| a == "--speculative"),
"and this one measures it ON: {mtp_argv:?}"
);
assert_eq!(flag(&s, "--num-drafts").as_deref(), Some("1"));
}
#[test]
fn the_form_rows_come_from_the_selected_card_not_the_first_one() {
let mut s = state(vec![mtp(), plain()]);
s.open_cards().expect("cards open");
s.card = 1;
s.open_config().expect("form opens");
let rows = s.config_rows();
let value = |key: &str| rows.iter().find(|r| r.key == key).map(|r| r.value.clone());
assert_eq!(value("max_model_len").as_deref(), Some("60000"));
assert_eq!(value("speculative").as_deref(), Some("true"));
assert_eq!(
rows.len(),
s.config_recipe().expect("recipe").defaults.len(),
"every key the recipe carries, and no more"
);
}
#[test]
fn the_serve_args_a_card_validates_to_match_the_card_it_came_from() {
let mut s = state(vec![mtp(), plain()]);
s.open_cards().expect("cards open");
for (card, want_len, want_spec) in [(0usize, 262144usize, false), (1, 60000, true)] {
s.view = View::Cards;
s.card = card;
s.open_config().expect("form opens");
let args = s
.config_recipe()
.expect("recipe")
.serve_args(&s.overrides)
.expect("the recipe is valid");
assert_eq!(args.max_seq_len, want_len, "card {card}");
assert_eq!(args.speculative, want_spec, "card {card}");
}
}
#[test]
fn opening_another_card_starts_from_that_recipes_own_values() {
let mut s = state(vec![mtp(), plain()]);
s.open_cards().expect("cards open");
s.card = 0;
s.open_config().expect("form opens");
s.row = s
.config_rows()
.iter()
.position(|r| r.key == "max_model_len")
.expect("max_model_len");
s.editing = true;
s.edit_buffer = "8192".into();
s.commit_edit();
assert_eq!(flag(&s, "--max-seq-len").as_deref(), Some("8192"));
s.view = View::Cards;
s.card = 1;
s.open_config().expect("the other form opens");
assert!(
s.overrides.is_empty(),
"the edits did not follow the cursor"
);
assert_eq!(
flag(&s, "--max-seq-len").as_deref(),
Some("60000"),
"the second recipe's own value"
);
}
#[test]
fn a_model_with_no_recipe_has_no_cards_until_starting_points_are_opened() {
let mut s = LibState::default();
s.rebuild(&[with_weights("org/local-only")]);
assert!(s.cards().is_empty());
assert!(s.selected_card().is_none());
assert!(s.config_recipe().is_none());
assert!(s.config_rows().is_empty(), "an empty form, not a blank one");
assert!(s.preview_argv().is_none());
s.open_cards().expect("starting points");
assert!(!s.cards().is_empty());
s.open_config().expect("a starting point is configurable");
}
#[test]
fn committing_an_edit_with_nothing_selected_is_a_no_op() {
let mut s = LibState {
view: View::Config,
edit_buffer: "9100".into(),
editing: true,
..Default::default()
};
s.commit_edit();
assert!(s.overrides.is_empty());
assert!(s.error.is_none(), "no row means nothing to complain about");
}
#[test]
fn a_non_metrale_recipe_is_listed_and_readable_but_refuses_the_form() {
let mut vllm = plain();
vllm.runtime = Some("vllm".into());
let mut s = state(vec![vllm]);
assert!(s.current().expect("a row").has_recipe());
assert!(
!s.current().expect("a row").runnable_now(),
"weights plus a non-metrale recipe is not runnable here"
);
s.open_cards().expect("the card still opens");
let err = s.open_config().expect_err("the form does not");
assert!(err.contains("vllm"), "names the runtime: {err}");
assert_eq!(s.view, View::Cards, "and it stays on the card");
assert!(s.preview_argv().is_none(), "nothing to launch");
}
#[test]
fn the_row_is_described_by_its_metrale_recipe_when_it_has_a_choice() {
let mut vllm = mtp();
vllm.runtime = Some("vllm".into());
let s = state(vec![vllm, plain()]);
let entry = s.current().expect("a row");
assert_eq!(
entry.primary().expect("a primary").id,
"qwen3.6/qwen3.6-27b-fp8"
);
assert!(entry.runnable_now(), "one launchable recipe is enough");
}
#[test]
fn an_override_for_a_key_the_recipe_does_not_have_is_visible_and_unlaunchable() {
let mut s = state(vec![plain()]);
s.open_cards().expect("cards open");
s.open_config().expect("form opens");
s.overrides
.insert("not_a_setting".into(), "whatever".into());
let preview = s
.preview_argv()
.expect("the stray key is shown, not hidden");
assert!(
preview.iter().any(|a| a == "--not-a-setting"),
"the operator must be able to see WHICH key is wrong: {preview:?}"
);
assert!(
s.config_recipe()
.expect("recipe")
.serve_args(&s.overrides)
.is_err(),
"and it must still be unlaunchable"
);
}
#[test]
fn a_recipe_that_needs_more_nodes_still_emits_its_world_size() {
let mut wide = plain();
wide.min_nodes = 2;
let mut s = state(vec![wide]);
s.open_cards().expect("cards open");
s.open_config().expect("form opens");
assert!(
!s.config_rows().iter().any(|r| r.key == "min_nodes"),
"it is not a form row"
);
assert_eq!(flag(&s, "--world-size").as_deref(), Some("2"));
}