use crate::tui::app::help_catalog::{
HelpRow, HelpSection, match_count, max_scroll, section_matches,
};
fn row(name: &str, description: &str, section: HelpSection) -> HelpRow {
HelpRow {
name: name.to_string(),
description: description.to_string(),
section,
}
}
fn catalog() -> Vec<HelpRow> {
vec![
row("/new", "Start a new session", HelpSection::BuiltIn),
row("/models", "Switch AI model", HelpSection::BuiltIn),
row("/respond_to", "Reply to a message", HelpSection::Channel),
row("/drop-release", "Publish a release", HelpSection::Skill),
row("/check", "Run clippy and tests", HelpSection::Custom),
]
}
#[test]
fn an_empty_needle_matches_everything() {
let rows = catalog();
assert_eq!(match_count(&rows, ""), rows.len());
for r in &rows {
assert!(r.matches(""));
}
}
#[test]
fn a_name_substring_matches() {
assert!(row("/mission-control", "Open MC", HelpSection::BuiltIn).matches("mis"));
}
#[test]
fn a_description_substring_matches_too() {
let r = row(
"/drop-release",
"Research, draft and publish",
HelpSection::Skill,
);
assert!(r.matches("publish"), "description must be searchable");
}
#[test]
fn matching_ignores_case_both_ways() {
let r = row("/Doctor", "Diagnose Setup", HelpSection::BuiltIn);
assert!(r.matches("doctor"));
assert!(r.matches("DIAGNOSE"));
assert!(r.matches("SeTuP"));
}
#[test]
fn a_needle_matching_nothing_matches_nothing() {
assert_eq!(match_count(&catalog(), "zzzznope"), 0);
}
#[test]
fn a_filter_narrows_within_a_section() {
let rows = catalog();
let hits = section_matches(&rows, HelpSection::BuiltIn, "model");
assert_eq!(hits.len(), 1);
assert_eq!(hits[0].name, "/models");
}
#[test]
fn a_filter_does_not_leak_across_sections() {
let rows = catalog();
let skills = section_matches(&rows, HelpSection::Skill, "");
assert_eq!(skills.len(), 1);
assert_eq!(skills[0].name, "/drop-release");
}
#[test]
fn section_order_is_fixed() {
assert_eq!(
HelpSection::all(),
[
HelpSection::BuiltIn,
HelpSection::Channel,
HelpSection::Skill,
HelpSection::Custom,
]
);
}
#[test]
fn every_section_has_a_header() {
for s in HelpSection::all() {
assert!(!s.title().is_empty(), "{s:?} renders a header");
}
}
#[test]
fn scroll_stops_with_content_still_on_screen() {
assert_eq!(max_scroll(100, 30), 70);
}
#[test]
fn content_shorter_than_the_viewport_cannot_scroll() {
assert_eq!(max_scroll(10, 30), 0);
assert_eq!(max_scroll(0, 30), 0);
}
#[test]
fn content_exactly_filling_the_viewport_cannot_scroll() {
assert_eq!(max_scroll(30, 30), 0);
}
#[test]
fn a_zero_height_viewport_does_not_underflow() {
assert_eq!(max_scroll(50, 0), 50);
}