use tsafe_tui::app::{App, EditFocus, LoginFocus, Screen, Theme};
use tsafe_tui::state::{dispatch_input, AppInput};
use zeroize::Zeroizing;
type PwStore = Option<Zeroizing<String>>;
fn pw(s: &str) -> PwStore {
Some(Zeroizing::new(s.to_owned()))
}
fn no_pw() -> PwStore {
None
}
fn login_app() -> App {
App::new_for_test(vec!["default".into()], Screen::Login)
}
fn dashboard_app() -> App {
let mut app = App::new_for_test(vec!["demo".into()], Screen::Dashboard);
app.active_profile = Some("demo".into());
app.secret_keys = vec!["API_KEY".into(), "DB_URL".into()];
app
}
fn feed_until_quit(app: &mut App, inputs: &[AppInput], pw_store: &mut PwStore) -> bool {
for input in inputs {
if dispatch_input(app, input.clone(), pw_store) {
return true;
}
}
false
}
#[test]
fn smoke_ctrl_c_quits_from_login() {
let mut app = login_app();
let mut store = no_pw();
assert!(dispatch_input(&mut app, AppInput::CtrlC, &mut store));
}
#[test]
fn smoke_ctrl_c_quits_from_dashboard() {
let mut app = dashboard_app();
let mut store = pw("secret");
assert!(dispatch_input(&mut app, AppInput::CtrlC, &mut store));
}
#[test]
fn smoke_q_quits_from_dashboard() {
let mut app = dashboard_app();
let mut store = pw("secret");
assert!(dispatch_input(&mut app, AppInput::Char('q'), &mut store));
}
#[test]
fn smoke_help_overlay_open_close_never_quits() {
let mut app = dashboard_app();
let mut store = no_pw();
let quit = feed_until_quit(
&mut app,
&[AppInput::Char('?'), AppInput::Char('?')],
&mut store,
);
assert!(!quit);
assert!(!app.help_visible);
assert_eq!(app.screen, Screen::Dashboard);
}
#[test]
fn smoke_help_overlay_esc_closes() {
let mut app = dashboard_app();
app.help_visible = true;
let mut store = no_pw();
dispatch_input(&mut app, AppInput::Esc, &mut store);
assert!(!app.help_visible);
}
#[test]
fn smoke_help_blocks_q_while_visible() {
let mut app = dashboard_app();
app.help_visible = true;
let mut store = no_pw();
let quit = dispatch_input(&mut app, AppInput::Char('q'), &mut store);
assert!(!quit);
assert_eq!(app.screen, Screen::Dashboard);
}
#[test]
fn smoke_search_mode_open_type_exit_esc() {
let mut app = dashboard_app();
let mut store = no_pw();
dispatch_input(&mut app, AppInput::Char('/'), &mut store);
assert!(app.search_mode);
for c in "api".chars() {
dispatch_input(&mut app, AppInput::Char(c), &mut store);
}
assert_eq!(app.search_query, "api");
dispatch_input(&mut app, AppInput::Esc, &mut store);
assert!(!app.search_mode);
assert_eq!(app.screen, Screen::Dashboard);
}
#[test]
fn smoke_search_enter_exits_search_mode() {
let mut app = dashboard_app();
let mut store = no_pw();
dispatch_input(&mut app, AppInput::Char('/'), &mut store);
dispatch_input(&mut app, AppInput::Char('d'), &mut store);
dispatch_input(&mut app, AppInput::Enter, &mut store);
assert!(!app.search_mode);
}
#[test]
fn smoke_profile_switch_returns_to_login() {
let mut app = dashboard_app();
let mut store = pw("pw");
dispatch_input(&mut app, AppInput::Char('p'), &mut store);
assert_eq!(app.screen, Screen::Login);
assert!(app.active_profile.is_none());
assert!(store.is_none());
}
#[test]
fn smoke_new_secret_modal_open_cancel() {
let mut app = dashboard_app();
let mut store = pw("pw");
dispatch_input(&mut app, AppInput::Char('n'), &mut store);
assert!(matches!(app.screen, Screen::EditSecret { is_new: true }));
dispatch_input(&mut app, AppInput::Esc, &mut store);
assert_eq!(app.screen, Screen::Dashboard);
}
#[test]
fn smoke_new_secret_tab_switches_focus() {
let mut app = dashboard_app();
let mut store = pw("pw");
dispatch_input(&mut app, AppInput::Char('n'), &mut store);
assert_eq!(app.edit_focus, EditFocus::Key);
dispatch_input(&mut app, AppInput::Tab, &mut store);
assert_eq!(app.edit_focus, EditFocus::Value);
dispatch_input(&mut app, AppInput::Tab, &mut store);
assert_eq!(app.edit_focus, EditFocus::Key);
}
#[test]
fn smoke_new_secret_type_key_and_value() {
let mut app = dashboard_app();
let mut store = pw("pw");
dispatch_input(&mut app, AppInput::Char('n'), &mut store);
for c in "MY_KEY".chars() {
dispatch_input(&mut app, AppInput::Char(c), &mut store);
}
assert_eq!(app.edit_key, "MY_KEY");
dispatch_input(&mut app, AppInput::Tab, &mut store);
for c in "hunter2".chars() {
dispatch_input(&mut app, AppInput::Char(c), &mut store);
}
assert_eq!(app.edit_value, "hunter2");
}
#[test]
fn smoke_audit_log_navigate_and_return() {
let mut app = dashboard_app();
app.screen = Screen::AuditLog;
app.audit_lines = vec!["line1".into(), "line2".into(), "line3".into()];
app.audit_scroll = 0;
let mut store = no_pw();
dispatch_input(&mut app, AppInput::Char('j'), &mut store);
dispatch_input(&mut app, AppInput::Char('j'), &mut store);
assert_eq!(app.audit_scroll, 2);
dispatch_input(&mut app, AppInput::Down, &mut store);
assert_eq!(app.audit_scroll, 2);
dispatch_input(&mut app, AppInput::Char('k'), &mut store);
assert_eq!(app.audit_scroll, 1);
dispatch_input(&mut app, AppInput::Char('q'), &mut store);
assert_eq!(app.screen, Screen::Dashboard);
}
#[test]
fn smoke_new_profile_wizard_open_and_cancel() {
let mut app = App::new_for_test(vec![], Screen::Login);
let mut store = no_pw();
dispatch_input(&mut app, AppInput::Char('n'), &mut store);
assert!(matches!(app.screen, Screen::NewProfile { step: 0 }));
for c in "testapp".chars() {
dispatch_input(&mut app, AppInput::Char(c), &mut store);
}
dispatch_input(&mut app, AppInput::Esc, &mut store);
assert_eq!(app.screen, Screen::Login);
}
#[test]
fn smoke_rotate_password_type_and_cancel() {
let mut app = dashboard_app();
app.screen = Screen::RotatePassword;
app.rotate_step = 0;
let mut store = pw("old");
for c in "newpw123".chars() {
dispatch_input(&mut app, AppInput::Char(c), &mut store);
}
dispatch_input(&mut app, AppInput::Enter, &mut store);
assert_eq!(app.rotate_step, 1);
dispatch_input(&mut app, AppInput::Esc, &mut store);
assert_eq!(app.screen, Screen::Dashboard);
}
#[test]
fn smoke_dashboard_cursor_wraps_down_and_up() {
let mut app = dashboard_app();
let mut store = no_pw();
let n = app.visible_entries().len();
assert!(n > 0);
for _ in 0..n {
dispatch_input(&mut app, AppInput::Down, &mut store);
}
assert_eq!(app.secret_cursor, 0);
dispatch_input(&mut app, AppInput::Up, &mut store);
assert_eq!(app.secret_cursor, n - 1);
}
#[test]
fn smoke_theme_cycles_all_three() {
let mut app = dashboard_app();
assert_eq!(app.theme, Theme::Dark);
let mut store = no_pw();
dispatch_input(&mut app, AppInput::Char('T'), &mut store);
assert_eq!(app.theme, Theme::Light);
dispatch_input(&mut app, AppInput::Char('T'), &mut store);
assert_eq!(app.theme, Theme::HighContrast);
dispatch_input(&mut app, AppInput::Char('T'), &mut store);
assert_eq!(app.theme, Theme::Dark);
}
#[test]
fn smoke_confirm_delete_cancel_esc_and_n() {
let mut app = dashboard_app();
let mut store = no_pw();
dispatch_input(&mut app, AppInput::Char('d'), &mut store);
assert_eq!(app.screen, Screen::ConfirmDelete);
dispatch_input(&mut app, AppInput::Esc, &mut store);
assert_eq!(app.screen, Screen::Dashboard);
dispatch_input(&mut app, AppInput::Char('d'), &mut store);
assert_eq!(app.screen, Screen::ConfirmDelete);
dispatch_input(&mut app, AppInput::Char('n'), &mut store);
assert_eq!(app.screen, Screen::Dashboard);
}
#[test]
fn smoke_move_secret_open_and_cancel() {
let mut app = dashboard_app();
app.secret_keys = vec!["MY_KEY".into()];
app.secret_cursor = 0;
let mut store = no_pw();
dispatch_input(&mut app, AppInput::Char('m'), &mut store);
assert!(matches!(app.screen, Screen::MoveSecret { .. }));
dispatch_input(&mut app, AppInput::Esc, &mut store);
assert_eq!(app.screen, Screen::Dashboard);
}
#[test]
fn smoke_ns_bulk_copy_open_and_cancel() {
let mut app = dashboard_app();
app.secret_keys = vec!["prod/KEY".into()];
app.secret_cursor = 0;
app.collapsed_namespaces.clear();
let mut store = no_pw();
dispatch_input(&mut app, AppInput::Char('c'), &mut store);
assert!(matches!(app.screen, Screen::NsBulk { copy: true, .. }));
dispatch_input(&mut app, AppInput::Esc, &mut store);
assert_eq!(app.screen, Screen::Dashboard);
}
#[test]
fn smoke_login_type_password_clear_with_esc() {
let mut app = login_app();
let mut store = no_pw();
for c in "hunter2".chars() {
dispatch_input(&mut app, AppInput::Char(c), &mut store);
}
assert_eq!(app.password_buf.as_str(), "hunter2");
dispatch_input(&mut app, AppInput::Esc, &mut store);
assert!(app.password_buf.is_empty());
assert_eq!(app.screen, Screen::Login);
}
#[test]
fn smoke_login_tab_then_down_cycles_profiles() {
let mut app = App::new_for_test(vec!["alpha".into(), "beta".into()], Screen::Login);
let mut store = no_pw();
dispatch_input(&mut app, AppInput::Tab, &mut store);
assert_eq!(app.login_focus, LoginFocus::Profile);
dispatch_input(&mut app, AppInput::Down, &mut store);
assert_eq!(app.profile_cursor, 1);
dispatch_input(&mut app, AppInput::Down, &mut store);
assert_eq!(app.profile_cursor, 0); }