summaryrefslogtreecommitdiffstats
path: root/tests/client_shell.rs
diff options
context:
space:
mode:
authorsalmonik <salmonik@tuta.io>2026-11-01 19:19:49 +0100
committersalmonik <salmonik@tuta.io>2026-11-01 19:19:49 +0100
commit24e3bb54db579fb9aad8df2a591527dc77fe4cb4 (patch)
treeaf9a0c290f2675068e8396823f641f24bbe4d308 /tests/client_shell.rs
parentv4.0.6: move the prompt editor and key mapping out of the core (diff)
downloadzagan-bet-24e3bb54db579fb9aad8df2a591527dc77fe4cb4.tar.gz
zagan-bet-24e3bb54db579fb9aad8df2a591527dc77fe4cb4.zip
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).
Diffstat (limited to '')
-rw-r--r--tests/client_shell.rs152
1 files changed, 152 insertions, 0 deletions
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 @@
1//! The terminal command line is a thin parser over `Command`.
2//!
3//! Anything the core can do on its own becomes `Shell::Run`; connecting,
4//! prompting for credentials and host maintenance stay with the adapter.
5
6use zagan_bet::client::shell::{Shell, parse};
7use zagan_bet::client::view::Command;
8use zagan_bet::history::{DEFAULT_HISTORY, MAX_HISTORY};
9
10fn run(line: &str) -> Command {
11 match parse(line) {
12 Shell::Run(command) => command,
13 other => panic!("expected a command, got {other:?}"),
14 }
15}
16
17#[test]
18fn blank_lines_do_nothing() {
19 assert_eq!(parse(""), Shell::Empty);
20 assert_eq!(parse(" \t "), Shell::Empty);
21}
22
23#[test]
24fn play_without_a_game_prints_usage() {
25 assert_eq!(
26 parse("play"),
27 Shell::Usage("Usage: play <game> [bet]".to_string())
28 );
29}
30
31#[test]
32fn play_carries_the_game_and_an_optional_bet() {
33 assert_eq!(
34 run("play slots"),
35 Command::Play {
36 game: "slots".to_string(),
37 bet: None,
38 }
39 );
40 assert_eq!(
41 run("play roulette 25"),
42 Command::Play {
43 game: "roulette".to_string(),
44 bet: Some(25),
45 }
46 );
47}
48
49#[test]
50fn play_rejects_a_bet_below_the_table_minimum() {
51 assert_eq!(
52 parse("play slots 5"),
53 Shell::Usage("Bet must be at least 10 Kr".to_string())
54 );
55 assert_eq!(
56 parse("play slots ten"),
57 Shell::Usage("Invalid bet: ten".to_string())
58 );
59}
60
61#[test]
62fn history_defaults_to_the_recent_limit() {
63 let expected = Command::History {
64 limit: DEFAULT_HISTORY,
65 };
66 assert_eq!(run("history"), expected);
67 assert_eq!(run("history 5"), Command::History { limit: 5 });
68}
69
70#[test]
71fn history_rejects_counts_outside_the_range() {
72 let usage = Shell::Usage(format!("Usage: history [1-{MAX_HISTORY}]"));
73 assert_eq!(parse("history 0"), usage);
74 assert_eq!(parse(&format!("history {}", MAX_HISTORY + 1)), usage);
75 assert_eq!(parse("history many"), usage);
76}
77
78#[test]
79fn simple_commands_are_pure_core_commands() {
80 assert_eq!(run("games"), Command::Games);
81 assert_eq!(run("logout"), Command::Logout);
82 assert_eq!(run("ping"), Command::Ping);
83 assert_eq!(run("info"), Command::Info);
84}
85
86#[test]
87fn send_keeps_the_whole_message_including_extra_spaces() {
88 assert_eq!(
89 run("send hello there friend"),
90 Command::Echo("hello there friend".to_string())
91 );
92 assert_eq!(
93 parse("send"),
94 Shell::Usage("Usage: send <message>".to_string())
95 );
96}
97
98#[test]
99fn connection_flows_stay_with_the_adapter() {
100 assert_eq!(parse("connect"), Shell::Connect(None));
101 assert_eq!(
102 parse("connect 127.0.0.1:11525"),
103 Shell::Connect(Some("127.0.0.1:11525".to_string()))
104 );
105 assert_eq!(parse("login"), Shell::Login);
106 assert_eq!(parse("register"), Shell::Register);
107 assert_eq!(parse("setcred"), Shell::SetCred);
108 assert_eq!(parse("disconnect"), Shell::Disconnect);
109}
110
111#[test]
112fn host_maintenance_is_adapter_owned() {
113 assert_eq!(parse("hosts"), Shell::Hosts);
114 assert_eq!(parse("forget-host"), Shell::ForgetHost(None));
115 assert_eq!(
116 parse("forget-host 10.0.0.2:11525"),
117 Shell::ForgetHost(Some("10.0.0.2:11525".to_string()))
118 );
119}
120
121#[test]
122fn forgetting_a_saved_login_is_separate_from_forgetting_trust() {
123 assert_eq!(parse("forget-login"), Shell::ForgetLogin(None));
124 assert_eq!(
125 parse("forget-login 10.0.0.2:11525"),
126 Shell::ForgetLogin(Some("10.0.0.2:11525".to_string()))
127 );
128}
129
130#[test]
131fn help_and_quit_are_terminal_only() {
132 assert_eq!(parse("help"), Shell::Help);
133 assert_eq!(parse("quit"), Shell::Quit);
134}
135
136#[test]
137fn an_unknown_word_falls_back_to_help() {
138 assert_eq!(parse("frobnicate"), Shell::Help);
139 assert_eq!(parse("quit now"), Shell::Quit);
140}
141
142#[test]
143fn surrounding_whitespace_is_ignored() {
144 assert_eq!(run(" ping "), Command::Ping);
145 assert_eq!(
146 run(" play slots 30 "),
147 Command::Play {
148 game: "slots".to_string(),
149 bet: Some(30),
150 }
151 );
152}