From 24e3bb54db579fb9aad8df2a591527dc77fe4cb4 Mon Sep 17 00:00:00 2001 From: salmonik Date: Sun, 1 Nov 2026 19:19:49 +0100 Subject: v4.0.7: finish the Phase 1 boundary with a command shell The terminal command line moves to client::shell: a typed line becomes a core Command or an adapter action (connect, login, register, set-credential, host maintenance). ClientCore loses submit_line and the last TUI-shaped effects (Interactive, SetPrompt, ListHosts, ForgetHost, AnimateRoulette); the adapter animates roulette from the Result view. Adds forget-login, which drops the stored credential for the pinned key and keeps trust, distinct from logout (session only) and forget-host (trust too). --- tests/client_shell.rs | 152 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 152 insertions(+) create mode 100644 tests/client_shell.rs (limited to 'tests/client_shell.rs') diff --git a/tests/client_shell.rs b/tests/client_shell.rs new file mode 100644 index 0000000..d286b46 --- /dev/null +++ b/tests/client_shell.rs @@ -0,0 +1,152 @@ +//! The terminal command line is a thin parser over `Command`. +//! +//! Anything the core can do on its own becomes `Shell::Run`; connecting, +//! prompting for credentials and host maintenance stay with the adapter. + +use zagan_bet::client::shell::{Shell, parse}; +use zagan_bet::client::view::Command; +use zagan_bet::history::{DEFAULT_HISTORY, MAX_HISTORY}; + +fn run(line: &str) -> Command { + match parse(line) { + Shell::Run(command) => command, + other => panic!("expected a command, got {other:?}"), + } +} + +#[test] +fn blank_lines_do_nothing() { + assert_eq!(parse(""), Shell::Empty); + assert_eq!(parse(" \t "), Shell::Empty); +} + +#[test] +fn play_without_a_game_prints_usage() { + assert_eq!( + parse("play"), + Shell::Usage("Usage: play [bet]".to_string()) + ); +} + +#[test] +fn play_carries_the_game_and_an_optional_bet() { + assert_eq!( + run("play slots"), + Command::Play { + game: "slots".to_string(), + bet: None, + } + ); + assert_eq!( + run("play roulette 25"), + Command::Play { + game: "roulette".to_string(), + bet: Some(25), + } + ); +} + +#[test] +fn play_rejects_a_bet_below_the_table_minimum() { + assert_eq!( + parse("play slots 5"), + Shell::Usage("Bet must be at least 10 Kr".to_string()) + ); + assert_eq!( + parse("play slots ten"), + Shell::Usage("Invalid bet: ten".to_string()) + ); +} + +#[test] +fn history_defaults_to_the_recent_limit() { + let expected = Command::History { + limit: DEFAULT_HISTORY, + }; + assert_eq!(run("history"), expected); + assert_eq!(run("history 5"), Command::History { limit: 5 }); +} + +#[test] +fn history_rejects_counts_outside_the_range() { + let usage = Shell::Usage(format!("Usage: history [1-{MAX_HISTORY}]")); + assert_eq!(parse("history 0"), usage); + assert_eq!(parse(&format!("history {}", MAX_HISTORY + 1)), usage); + assert_eq!(parse("history many"), usage); +} + +#[test] +fn simple_commands_are_pure_core_commands() { + assert_eq!(run("games"), Command::Games); + assert_eq!(run("logout"), Command::Logout); + assert_eq!(run("ping"), Command::Ping); + assert_eq!(run("info"), Command::Info); +} + +#[test] +fn send_keeps_the_whole_message_including_extra_spaces() { + assert_eq!( + run("send hello there friend"), + Command::Echo("hello there friend".to_string()) + ); + assert_eq!( + parse("send"), + Shell::Usage("Usage: send ".to_string()) + ); +} + +#[test] +fn connection_flows_stay_with_the_adapter() { + assert_eq!(parse("connect"), Shell::Connect(None)); + assert_eq!( + parse("connect 127.0.0.1:11525"), + Shell::Connect(Some("127.0.0.1:11525".to_string())) + ); + assert_eq!(parse("login"), Shell::Login); + assert_eq!(parse("register"), Shell::Register); + assert_eq!(parse("setcred"), Shell::SetCred); + assert_eq!(parse("disconnect"), Shell::Disconnect); +} + +#[test] +fn host_maintenance_is_adapter_owned() { + assert_eq!(parse("hosts"), Shell::Hosts); + assert_eq!(parse("forget-host"), Shell::ForgetHost(None)); + assert_eq!( + parse("forget-host 10.0.0.2:11525"), + Shell::ForgetHost(Some("10.0.0.2:11525".to_string())) + ); +} + +#[test] +fn forgetting_a_saved_login_is_separate_from_forgetting_trust() { + assert_eq!(parse("forget-login"), Shell::ForgetLogin(None)); + assert_eq!( + parse("forget-login 10.0.0.2:11525"), + Shell::ForgetLogin(Some("10.0.0.2:11525".to_string())) + ); +} + +#[test] +fn help_and_quit_are_terminal_only() { + assert_eq!(parse("help"), Shell::Help); + assert_eq!(parse("quit"), Shell::Quit); +} + +#[test] +fn an_unknown_word_falls_back_to_help() { + assert_eq!(parse("frobnicate"), Shell::Help); + assert_eq!(parse("quit now"), Shell::Quit); +} + +#[test] +fn surrounding_whitespace_is_ignored() { + assert_eq!(run(" ping "), Command::Ping); + assert_eq!( + run(" play slots 30 "), + Command::Play { + game: "slots".to_string(), + bet: Some(30), + } + ); +} -- cgit v1.2.3