use super::*;
#[test]
fn rediscover_is_noop_when_daemon_reachable() {
let mut client = DaemonClient::new("http://127.0.0.1:7880");
assert!(!rediscover_daemon(&mut client, true));
assert_eq!(client.base_url(), "http://127.0.0.1:7880");
}
#[test]
fn rediscover_is_noop_when_resolved_url_unchanged() {
let mut client = DaemonClient::new(crate::core::DEFAULT_DAEMON_URL);
let changed = rediscover_daemon(&mut client, false);
if !changed {
assert_eq!(client.base_url(), crate::core::DEFAULT_DAEMON_URL);
}
}
const EXPLICIT_URL: &str = "http://127.0.0.1:1";
#[test]
fn rediscover_daemon_keeps_explicit_url() {
let dead = EXPLICIT_URL.to_string();
let mut client = DaemonClient::new(dead.clone()).with_pinned_base_url(true);
assert!(!rediscover_daemon(&mut client, false));
assert_eq!(client.base_url(), dead);
}
#[test]
fn client_for_pins_only_an_explicit_url() {
let explicit = client_for(Some(EXPLICIT_URL));
assert!(explicit.base_url_pinned());
assert_eq!(explicit.base_url(), EXPLICIT_URL);
assert!(!client_for(Some("")).base_url_pinned());
assert!(!client_for(None).base_url_pinned());
}
#[test]
fn rediscover_daemon_follows_discovery_when_not_pinned() {
let dead = EXPLICIT_URL.to_string();
let mut client = DaemonClient::new(dead.clone());
assert!(rediscover_daemon(&mut client, false));
assert_ne!(client.base_url(), dead);
}
#[test]
fn coordinator_session_maps_status() {
let session = crate::client::CoordinatorSession {
id: "00000000-0000-0000-0000-000000000000".into(),
name: "tmpm-foo".into(),
prefix: "foo".into(),
workdir: "/tmp/p".into(),
status: "Paused".into(),
active_delegations: 2,
recent_output: Vec::new(),
..Default::default()
};
let row = coordinator_session_to_row(session);
assert_eq!(row.tmux_name, "tmpm-foo");
assert_eq!(row.active_delegations, 2);
assert_eq!(row.status, crate::core::session::SessionStatus::Paused);
}
#[test]
fn screen_default_is_chat() {
assert_eq!(Screen::default(), Screen::Chat);
}
fn switch(from: Screen, key: char) -> Screen {
match (from, key) {
(Screen::Chat, '2') => Screen::Health,
(Screen::Health, 'c') => Screen::Chat,
_ => from,
}
}
#[test]
fn screen_switch_preserves_chat_state() {
let mut state = DashboardState::default();
state.push_chat(ChatMessage::user("remember me"));
let screen = switch(Screen::Chat, '2');
assert_eq!(screen, Screen::Health);
let screen = switch(screen, 'c');
assert_eq!(screen, Screen::Chat);
assert_eq!(state.chat_history.len(), 1);
assert_eq!(state.chat_history[0].content, "remember me");
}
#[test]
fn health_status_bar_lists_keys() {
for token in [
"[1]health",
"[2]logs",
"[3]search",
"[Tab]",
"[↑↓]",
"[r]",
"[S]start",
"[X]stop",
"[c]chat",
"[q]quit",
] {
assert!(
HEALTH_KEY_HINT.contains(token),
"status bar missing {token}"
);
}
}
#[test]
fn render_screen_draws_both_screens_without_panic() {
use ratatui::{Terminal, backend::TestBackend};
let chat = DashboardState::default();
let hp = HealthScreen::new(
"/nonexistent/trusty-search/test.sock",
"http://127.0.0.1:19999",
);
for screen in [Screen::Chat, Screen::Health] {
let backend = TestBackend::new(120, 24);
let mut terminal = Terminal::new(backend).expect("test terminal");
terminal
.draw(|f| render_screen(f, screen, &chat, &hp))
.expect("render must not panic");
}
}
#[tokio::test]
async fn coordinator_send_without_daemon_reports_error() {
let client = DaemonClient::new("http://127.0.0.1:0");
let mut state = DashboardState::default();
coordinator_send(&mut state, &client, "what is happening?").await;
assert_eq!(state.chat_history.len(), 2);
assert_eq!(state.chat_history[0].role, dashboard::ChatRole::User);
assert!(
state.chat_history[1].content.contains("daemon error"),
"expected a daemon error, got {:?}",
state.chat_history[1].content
);
}
#[test]
fn initial_view_defaults_to_multipane() {
assert_eq!(initial_view(false), TuiView::Multipane);
assert_eq!(TuiView::default(), TuiView::Multipane);
}
#[test]
fn initial_view_honours_single_pane_opt_in() {
assert_eq!(initial_view(true), TuiView::SinglePane);
}
#[test]
fn terminal_guard_drop_is_idempotent() {
drop(super::terminal::TerminalGuard);
drop(super::terminal::TerminalGuard);
}
struct FailingScreen;
impl std::io::Write for FailingScreen {
fn write(&mut self, _buf: &[u8]) -> std::io::Result<usize> {
Err(std::io::Error::other("screen rejected the sequence"))
}
fn flush(&mut self) -> std::io::Result<()> {
Err(std::io::Error::other("screen rejected the flush"))
}
}
#[test]
fn enter_with_restores_the_terminal_when_the_screen_fails() {
let restored = std::cell::Cell::new(false);
let result = super::terminal::enter_with(FailingScreen, || Ok(()), || restored.set(true));
assert!(
result.is_err(),
"a screen that rejects EnterAlternateScreen must surface as Err"
);
assert!(
restored.get(),
"raw mode engaged, so the failure path must restore the terminal"
);
}
#[test]
fn enter_with_leaves_the_terminal_alone_when_raw_mode_fails() {
let restored = std::cell::Cell::new(false);
let result = super::terminal::enter_with(
FailingScreen,
|| Err(std::io::Error::other("no tty")),
|| restored.set(true),
);
assert!(result.is_err(), "a raw-mode failure must surface as Err");
assert!(
!restored.get(),
"nothing engaged, so nothing may be restored"
);
}
#[test]
fn term_supports_raw_mode_rejects_dumb_and_missing() {
use super::terminal::term_supports_raw_mode;
assert!(!term_supports_raw_mode(None), "unset TERM -> static");
assert!(!term_supports_raw_mode(Some("")), "empty TERM -> static");
assert!(!term_supports_raw_mode(Some("dumb")), "dumb TERM -> static");
assert!(
!term_supports_raw_mode(Some("DUMB")),
"the dumb check is case-insensitive"
);
}
#[test]
fn term_supports_raw_mode_accepts_a_real_terminal() {
use super::terminal::term_supports_raw_mode;
assert!(term_supports_raw_mode(Some("xterm-256color")));
assert!(term_supports_raw_mode(Some("screen")));
}