summaryrefslogtreecommitdiffstats
path: root/tests
diff options
context:
space:
mode:
authorsalmonik <salmonik@tuta.io>2026-10-29 22:16:15 +0100
committersalmonik <salmonik@tuta.io>2026-10-29 22:16:15 +0100
commitbc0de9955c375cf27d52c9934403964205afc182 (patch)
tree42799fb426fb0ec164f44c92a63525003c582f35 /tests
parentinitial connection timeout set to 10s (diff)
downloadzagan-bet-bc0de9955c375cf27d52c9934403964205afc182.tar.gz
zagan-bet-bc0de9955c375cf27d52c9934403964205afc182.zip
formatting
Diffstat (limited to 'tests')
-rw-r--r--tests/blackjack.rs7
-rw-r--r--tests/client_core.rs63
-rw-r--r--tests/client_ui.rs45
-rw-r--r--tests/crash.rs6
-rw-r--r--tests/protocol.rs10
-rw-r--r--tests/roulette.rs19
-rw-r--r--tests/server_core.rs92
-rw-r--r--tests/slots.rs7
-rw-r--r--tests/tofu.rs10
9 files changed, 120 insertions, 139 deletions
diff --git a/tests/blackjack.rs b/tests/blackjack.rs
index f90ca8d..36ed7cf 100644
--- a/tests/blackjack.rs
+++ b/tests/blackjack.rs
@@ -16,7 +16,12 @@ fn card_values_and_soft_totals_are_correct() {
16 assert_eq!(card_label("AS"), "A♠"); 16 assert_eq!(card_label("AS"), "A♠");
17 assert_eq!(card_label("10H"), "10♥"); 17 assert_eq!(card_label("10H"), "10♥");
18 18
19 let cards = |values: &[&str]| values.iter().map(|card| card.to_string()).collect::<Vec<_>>(); 19 let cards = |values: &[&str]| {
20 values
21 .iter()
22 .map(|card| card.to_string())
23 .collect::<Vec<_>>()
24 };
20 assert_eq!(hand_total(&cards(&["AS", "10H"])), 21); 25 assert_eq!(hand_total(&cards(&["AS", "10H"])), 21);
21 assert_eq!(hand_total(&cards(&["AS", "9H"])), 20); 26 assert_eq!(hand_total(&cards(&["AS", "9H"])), 20);
22 assert_eq!(hand_total(&cards(&["AS", "AS", "9H"])), 21); 27 assert_eq!(hand_total(&cards(&["AS", "AS", "9H"])), 21);
diff --git a/tests/client_core.rs b/tests/client_core.rs
index e8b5646..176791e 100644
--- a/tests/client_core.rs
+++ b/tests/client_core.rs
@@ -4,6 +4,7 @@
4//! logical `Effect`s that `ClientCore` emits, so assertions are about what a 4//! logical `Effect`s that `ClientCore` emits, so assertions are about what a
5//! player would see, not about ANSI sequences. 5//! player would see, not about ANSI sequences.
6 6
7use std::time::Duration;
7use zagan_bet::blackjack::BlackjackState; 8use zagan_bet::blackjack::BlackjackState;
8use zagan_bet::client::core::{ClientCore, Effect, InteractiveCommand, Key}; 9use zagan_bet::client::core::{ClientCore, Effect, InteractiveCommand, Key};
9use zagan_bet::client::games::response::{ 10use zagan_bet::client::games::response::{
@@ -11,7 +12,6 @@ use zagan_bet::client::games::response::{
11}; 12};
12use zagan_bet::client::ui::command_names; 13use zagan_bet::client::ui::command_names;
13use zagan_bet::crash::CrashState; 14use zagan_bet::crash::CrashState;
14use std::time::Duration;
15 15
16const WIN3: &str = "CCCCLLCLODGSDGS"; 16const WIN3: &str = "CCCCLLCLODGSDGS";
17const LOSS: &str = "CLOOGDSLCDCOGLS"; 17const LOSS: &str = "CLOOGDSLCDCOGLS";
@@ -67,10 +67,7 @@ impl Screen {
67 } 67 }
68 Effect::SendLine(line) => sent.push(line.clone()), 68 Effect::SendLine(line) => sent.push(line.clone()),
69 Effect::Interactive(command) => interactive.push(command.clone()), 69 Effect::Interactive(command) => interactive.push(command.clone()),
70 Effect::Quit 70 Effect::Quit | Effect::Disconnect | Effect::ListHosts | Effect::ForgetHost(_) => {}
71 | Effect::Disconnect
72 | Effect::ListHosts
73 | Effect::ForgetHost(_) => {}
74 } 71 }
75 } 72 }
76 } 73 }
@@ -276,7 +273,8 @@ fn a_mega_win_shows_all_winning_lines() {
276 273
277 assert_eq!(h.board().len(), 10); 274 assert_eq!(h.board().len(), 10);
278 assert!(h.board()[7].contains("MEGA WIN! 700 PLN")); 275 assert!(h.board()[7].contains("MEGA WIN! 700 PLN"));
279 assert!(h.board()[8].contains("Winning lines: 1 (top), 2 (middle), 3 (bottom), 4 (V), 5 (inverted V)")); 276 assert!(h.board()[8]
277 .contains("Winning lines: 1 (top), 2 (middle), 3 (bottom), 4 (V), 5 (inverted V)"));
280 assert_eq!(h.board()[2].matches("{ C }").count(), 5); 278 assert_eq!(h.board()[2].matches("{ C }").count(), 5);
281} 279}
282 280
@@ -423,10 +421,7 @@ fn help_lists_every_command() {
423 421
424 let transcript = h.screen.lines.join("\n"); 422 let transcript = h.screen.lines.join("\n");
425 for command in command_names() { 423 for command in command_names() {
426 assert!( 424 assert!(transcript.contains(command), "help is missing '{command}'");
427 transcript.contains(command),
428 "help is missing '{command}'"
429 );
430 } 425 }
431} 426}
432 427
@@ -548,11 +543,7 @@ fn send_prints_the_echo() {
548 h.line("send hello"); 543 h.line("send hello");
549 h.reply("hello"); 544 h.reply("hello");
550 545
551 assert!(h 546 assert!(h.screen.lines.iter().any(|line| line == "Echo: hello"));
552 .screen
553 .lines
554 .iter()
555 .any(|line| line == "Echo: hello"));
556} 547}
557 548
558#[test] 549#[test]
@@ -560,11 +551,7 @@ fn disconnect_clears_connection_state() {
560 let mut h = Harness::new(); 551 let mut h = Harness::new();
561 h.line("disconnect"); 552 h.line("disconnect");
562 553
563 assert!(h 554 assert!(h.screen.lines.iter().any(|line| line == "Disconnected"));
564 .screen
565 .lines
566 .iter()
567 .any(|line| line == "Disconnected"));
568 555
569 h.line("play slots"); 556 h.line("play slots");
570 assert!(h 557 assert!(h
@@ -636,8 +623,7 @@ fn roulette_split_bets_spin_and_show_the_result() {
636 "PLAY_START roulette 30 {\"red\":10,\"black\":0,\"even\":10,\"odd\":0,\"numbers\":[{\"number\":17,\"amount\":10}]}" 623 "PLAY_START roulette 30 {\"red\":10,\"black\":0,\"even\":10,\"odd\":0,\"numbers\":[{\"number\":17,\"amount\":10}]}"
637 ); 624 );
638 625
639 let bets = 626 let bets = r#"{"red":10,"black":0,"even":10,"odd":0,"numbers":[{"number":17,"amount":10}]}"#;
640 r#"{"red":10,"black":0,"even":10,"odd":0,"numbers":[{"number":17,"amount":10}]}"#;
641 h.reply(&format!( 627 h.reply(&format!(
642 "ROULETTE_RESULT session-1 360 30 1330 17 black {bets}" 628 "ROULETTE_RESULT session-1 360 30 1330 17 black {bets}"
643 )); 629 ));
@@ -655,7 +641,10 @@ fn roulette_rejects_an_empty_spin_and_accepts_the_equals_chip_key() {
655 641
656 h.key(Key::Char(' ')); 642 h.key(Key::Char(' '));
657 assert!(h.board()[4].contains("ERROR: Place at least one bet")); 643 assert!(h.board()[4].contains("ERROR: Place at least one bet"));
658 assert!(!h.sent.iter().any(|line| line.starts_with("PLAY_START roulette"))); 644 assert!(!h
645 .sent
646 .iter()
647 .any(|line| line.starts_with("PLAY_START roulette")));
659 648
660 h.key(Key::Char('=')); 649 h.key(Key::Char('='));
661 assert!(h.board()[1].contains("Chip: 20 PLN")); 650 assert!(h.board()[1].contains("Chip: 20 PLN"));
@@ -716,10 +705,7 @@ fn blackjack_deals_hits_and_settles() {
716 assert!(h.board()[4].contains("Press [SPACE] to deal.")); 705 assert!(h.board()[4].contains("Press [SPACE] to deal."));
717 706
718 h.key(Key::Char(' ')); 707 h.key(Key::Char(' '));
719 assert!(h 708 assert!(h.sent.iter().any(|line| line == "PLAY_START blackjack 10"));
720 .sent
721 .iter()
722 .any(|line| line == "PLAY_START blackjack 10"));
723 709
724 let player = bj_state( 710 let player = bj_state(
725 "bj1", 711 "bj1",
@@ -847,10 +833,7 @@ fn blackjack_ready_board_can_change_the_next_bet() {
847 assert!(h.board()[1].contains("Bet: 20 PLN")); 833 assert!(h.board()[1].contains("Bet: 20 PLN"));
848 834
849 h.key(Key::Char(' ')); 835 h.key(Key::Char(' '));
850 assert!(h 836 assert!(h.sent.iter().any(|line| line == "PLAY_START blackjack 20"));
851 .sent
852 .iter()
853 .any(|line| line == "PLAY_START blackjack 20"));
854} 837}
855 838
856#[test] 839#[test]
@@ -875,7 +858,13 @@ fn parses_a_blackjack_state() {
875 assert!(parse_blackjack_response("ERROR Insufficient balance").is_err()); 858 assert!(parse_blackjack_response("ERROR Insufficient balance").is_err());
876} 859}
877 860
878fn crash_state(phase: &str, multiplier: f64, crash_point: Option<f64>, payout: u64, balance: i64) -> CrashState { 861fn crash_state(
862 phase: &str,
863 multiplier: f64,
864 crash_point: Option<f64>,
865 payout: u64,
866 balance: i64,
867) -> CrashState {
879 CrashState { 868 CrashState {
880 session_id: "cr1".to_string(), 869 session_id: "cr1".to_string(),
881 phase: phase.to_string(), 870 phase: phase.to_string(),
@@ -920,10 +909,7 @@ fn crash_starts_a_round_and_draws_the_multiplier_locally() {
920 assert!(h.board()[3].contains("Potential: 20 PLN")); 909 assert!(h.board()[3].contains("Potential: 20 PLN"));
921 910
922 h.key(Key::Char('c')); 911 h.key(Key::Char('c'));
923 assert_eq!( 912 assert_eq!(h.sent.last().map(String::as_str), Some("CRASH_CASHOUT cr1"));
924 h.sent.last().map(String::as_str),
925 Some("CRASH_CASHOUT cr1")
926 );
927 913
928 let cashed = crash_state("cashed", 2.0, Some(3.0), 20, 1010); 914 let cashed = crash_state("cashed", 2.0, Some(3.0), 20, 1010);
929 h.reply(&crash_line("CRASH_STATE", &cashed)); 915 h.reply(&crash_line("CRASH_STATE", &cashed));
@@ -967,10 +953,7 @@ fn crash_q_forfeits_an_active_round() {
967 h.reply(&crash_line("CRASH_START", &running)); 953 h.reply(&crash_line("CRASH_START", &running));
968 954
969 h.key(Key::Char('q')); 955 h.key(Key::Char('q'));
970 assert_eq!( 956 assert_eq!(h.sent.last().map(String::as_str), Some("CRASH_FORFEIT cr1"));
971 h.sent.last().map(String::as_str),
972 Some("CRASH_FORFEIT cr1")
973 );
974 957
975 let forfeited = crash_state("forfeit", 1.2, Some(2.0), 0, 990); 958 let forfeited = crash_state("forfeit", 1.2, Some(2.0), 0, 990);
976 h.reply(&crash_line("CRASH_STATE", &forfeited)); 959 h.reply(&crash_line("CRASH_STATE", &forfeited));
diff --git a/tests/client_ui.rs b/tests/client_ui.rs
index 7a1b675..44febde 100644
--- a/tests/client_ui.rs
+++ b/tests/client_ui.rs
@@ -1,9 +1,9 @@
1use std::time::Duration;
1use zagan_bet::client::ui::{ 2use zagan_bet::client::ui::{
2 command_names, completion_lines, display_width, first_contact_lines, format_pong, 3 command_names, completion_lines, display_width, first_contact_lines, format_pong, help_lines,
3 help_lines, host_lines, input_cursor_column, lines_to_move_up, parse_bet_arg, record_history, 4 host_lines, input_cursor_column, lines_to_move_up, parse_bet_arg, record_history,
4 visual_line_count, 5 visual_line_count,
5}; 6};
6use std::time::Duration;
7 7
8#[test] 8#[test]
9fn help_has_no_source_indentation() { 9fn help_has_no_source_indentation() {
@@ -27,13 +27,7 @@ fn help_documents_every_command() {
27fn completion_knows_every_command() { 27fn completion_knows_every_command() {
28 let commands = command_names(); 28 let commands = command_names();
29 for expected in [ 29 for expected in [
30 "connect", 30 "connect", "games", "play", "hosts", "send", "logout", "quit",
31 "games",
32 "play",
33 "hosts",
34 "send",
35 "logout",
36 "quit",
37 ] { 31 ] {
38 assert!( 32 assert!(
39 commands.contains(&expected), 33 commands.contains(&expected),
@@ -109,19 +103,12 @@ fn display_width_ignores_ansi_sequences() {
109fn visual_line_count_ignores_ansi_sequences() { 103fn visual_line_count_ignores_ansi_sequences() {
110 let styled = format!("\x1b[31m{}\x1b[0m", "x".repeat(75)); 104 let styled = format!("\x1b[31m{}\x1b[0m", "x".repeat(75));
111 assert_eq!(visual_line_count(&[styled], 72), 2); 105 assert_eq!(visual_line_count(&[styled], 72), 2);
112 assert_eq!( 106 assert_eq!(visual_line_count(&["\x1b[31m18\x1b[0m".to_string()], 80), 1);
113 visual_line_count(&["\x1b[31m18\x1b[0m".to_string()], 80),
114 1
115 );
116} 107}
117 108
118#[test] 109#[test]
119fn visual_line_count_accounts_for_terminal_wrapping() { 110fn visual_line_count_accounts_for_terminal_wrapping() {
120 let lines = vec![ 111 let lines = vec!["12345".to_string(), "123456".to_string(), String::new()];
121 "12345".to_string(),
122 "123456".to_string(),
123 String::new(),
124 ];
125 112
126 assert_eq!(visual_line_count(&lines, 5), 4); // 1 + 2 + 1 113 assert_eq!(visual_line_count(&lines, 5), 4); // 1 + 2 + 1
127 assert_eq!(visual_line_count(&lines, 6), 3); // 1 + 1 + 1 114 assert_eq!(visual_line_count(&lines, 6), 3); // 1 + 1 + 1
@@ -146,7 +133,10 @@ fn completions_wrap_within_the_requested_width() {
146 let commands = command_names(); 133 let commands = command_names();
147 let lines = completion_lines(&commands, 40); 134 let lines = completion_lines(&commands, 40);
148 135
149 assert_eq!(lines.first().map(String::as_str), Some("Possible completions:")); 136 assert_eq!(
137 lines.first().map(String::as_str),
138 Some("Possible completions:")
139 );
150 for line in &lines[1..] { 140 for line in &lines[1..] {
151 assert!( 141 assert!(
152 line.len() <= 40, 142 line.len() <= 40,
@@ -208,10 +198,7 @@ fn pong_is_formatted_to_one_decimal() {
208fn host_lines_fit_an_80_column_terminal() { 198fn host_lines_fit_an_80_column_terminal() {
209 let fingerprint = "a4d6f80784154c2cb739a1df61ab3e9038ed731ee2e05c7611457ac741b3834c"; 199 let fingerprint = "a4d6f80784154c2cb739a1df61ab3e9038ed731ee2e05c7611457ac741b3834c";
210 let lines = host_lines(&[ 200 let lines = host_lines(&[
211 ( 201 ("127.0.0.1:11525".to_string(), Some(fingerprint.to_string())),
212 "127.0.0.1:11525".to_string(),
213 Some(fingerprint.to_string()),
214 ),
215 ("unknown.example:443".to_string(), None), 202 ("unknown.example:443".to_string(), None),
216 ]); 203 ]);
217 204
@@ -233,10 +220,7 @@ fn hosts_sharing_a_fingerprint_are_shown_as_one_server() {
233 let lines = host_lines(&[ 220 let lines = host_lines(&[
234 ("10.0.0.100:11525".to_string(), Some(key.to_string())), 221 ("10.0.0.100:11525".to_string(), Some(key.to_string())),
235 ("127.0.0.1:11525".to_string(), Some(key.to_string())), 222 ("127.0.0.1:11525".to_string(), Some(key.to_string())),
236 ( 223 ("other.example:11525".to_string(), Some("ff".repeat(32))),
237 "other.example:11525".to_string(),
238 Some("ff".repeat(32)),
239 ),
240 ]); 224 ]);
241 225
242 assert_eq!( 226 assert_eq!(
@@ -264,7 +248,10 @@ fn hosts_without_a_fingerprint_are_never_merged() {
264 assert!(lines.contains(&" a.example:443".to_string())); 248 assert!(lines.contains(&" a.example:443".to_string()));
265 assert!(lines.contains(&" b.example:443".to_string())); 249 assert!(lines.contains(&" b.example:443".to_string()));
266 assert_eq!( 250 assert_eq!(
267 lines.iter().filter(|line| line.contains("no fingerprint")).count(), 251 lines
252 .iter()
253 .filter(|line| line.contains("no fingerprint"))
254 .count(),
268 2 255 2
269 ); 256 );
270} 257}
diff --git a/tests/crash.rs b/tests/crash.rs
index f59c52f..49eaf89 100644
--- a/tests/crash.rs
+++ b/tests/crash.rs
@@ -18,7 +18,11 @@ fn the_multiplier_doubles_every_five_seconds() {
18 close(multiplier_at(Duration::ZERO), 1.0, 1e-9); 18 close(multiplier_at(Duration::ZERO), 1.0, 1e-9);
19 close(multiplier_at(Duration::from_secs(5)), 2.0, 1e-9); 19 close(multiplier_at(Duration::from_secs(5)), 2.0, 1e-9);
20 close(multiplier_at(Duration::from_secs(10)), 4.0, 1e-6); 20 close(multiplier_at(Duration::from_secs(10)), 4.0, 1e-6);
21 close(multiplier_at(Duration::from_millis(2500)), 1.414_213_562, 1e-6); 21 close(
22 multiplier_at(Duration::from_millis(2500)),
23 1.414_213_562,
24 1e-6,
25 );
22 26
23 // Never below 1x, even for nonsense input. 27 // Never below 1x, even for nonsense input.
24 assert_eq!(multiplier_at_secs(-3.0), 1.0); 28 assert_eq!(multiplier_at_secs(-3.0), 1.0);
diff --git a/tests/protocol.rs b/tests/protocol.rs
index 2962574..bf8a4fd 100644
--- a/tests/protocol.rs
+++ b/tests/protocol.rs
@@ -40,8 +40,14 @@ fn reads_multiple_lines_from_a_reader() {
40 let mut buffer = LineBuffer::new(); 40 let mut buffer = LineBuffer::new();
41 let mut reader = Cursor::new(b"one\ntwo\n".to_vec()); 41 let mut reader = Cursor::new(b"one\ntwo\n".to_vec());
42 42
43 assert_eq!(buffer.read_line(&mut reader).unwrap().as_deref(), Some("one")); 43 assert_eq!(
44 assert_eq!(buffer.read_line(&mut reader).unwrap().as_deref(), Some("two")); 44 buffer.read_line(&mut reader).unwrap().as_deref(),
45 Some("one")
46 );
47 assert_eq!(
48 buffer.read_line(&mut reader).unwrap().as_deref(),
49 Some("two")
50 );
45 assert_eq!(buffer.read_line(&mut reader).unwrap(), None); 51 assert_eq!(buffer.read_line(&mut reader).unwrap(), None);
46} 52}
47 53
diff --git a/tests/roulette.rs b/tests/roulette.rs
index 6b99d17..932bace 100644
--- a/tests/roulette.rs
+++ b/tests/roulette.rs
@@ -1,6 +1,4 @@
1use zagan_bet::roulette::{ 1use zagan_bet::roulette::{color, is_black, is_even, is_odd, is_red, rtp, Bets, NumberBet, WHEEL};
2 color, is_black, is_even, is_odd, is_red, rtp, Bets, NumberBet, WHEEL,
3};
4use zagan_bet::server::games::roulette::RouletteGame; 2use zagan_bet::server::games::roulette::RouletteGame;
5use zagan_bet::server::games::traits::Game; 3use zagan_bet::server::games::traits::Game;
6 4
@@ -171,14 +169,13 @@ fn rejects_bad_roulette_results() {
171 use zagan_bet::client::games::response::parse_roulette_response; 169 use zagan_bet::client::games::response::parse_roulette_response;
172 170
173 let valid_bets = r#"{"red":10}"#; 171 let valid_bets = r#"{"red":10}"#;
174 assert!(parse_roulette_response(&format!( 172 assert!(
175 "ROULETTE_RESULT s 1 1 1 37 green {valid_bets}" 173 parse_roulette_response(&format!("ROULETTE_RESULT s 1 1 1 37 green {valid_bets}")).is_err()
176 )) 174 );
177 .is_err()); 175 assert!(
178 assert!(parse_roulette_response(&format!( 176 parse_roulette_response(&format!("ROULETTE_RESULT s 1 1 1 17 purple {valid_bets}"))
179 "ROULETTE_RESULT s 1 1 1 17 purple {valid_bets}" 177 .is_err()
180 )) 178 );
181 .is_err());
182 assert!(parse_roulette_response("ROULETTE_RESULT s 1 1 1 17 black nope").is_err()); 179 assert!(parse_roulette_response("ROULETTE_RESULT s 1 1 1 17 black nope").is_err());
183 assert!(parse_roulette_response("").is_err()); 180 assert!(parse_roulette_response("").is_err());
184} 181}
diff --git a/tests/server_core.rs b/tests/server_core.rs
index 0350fb6..6b7b415 100644
--- a/tests/server_core.rs
+++ b/tests/server_core.rs
@@ -4,13 +4,13 @@ use std::sync::Arc;
4use rusqlite::Connection; 4use rusqlite::Connection;
5use zagan_bet::blackjack::BlackjackState; 5use zagan_bet::blackjack::BlackjackState;
6use zagan_bet::crash::CrashState; 6use zagan_bet::crash::CrashState;
7use zagan_bet::roulette::Bets;
7use zagan_bet::server::core::{ConnectionState, ServerCore}; 8use zagan_bet::server::core::{ConnectionState, ServerCore};
8use zagan_bet::server::database; 9use zagan_bet::server::database;
9use zagan_bet::server::games::crash::ManualClock; 10use zagan_bet::server::games::crash::ManualClock;
10use zagan_bet::server::games::registry::GameRegistry; 11use zagan_bet::server::games::registry::GameRegistry;
11use zagan_bet::server::games::roulette::RouletteGame; 12use zagan_bet::server::games::roulette::RouletteGame;
12use zagan_bet::server::games::slots::SlotsGame; 13use zagan_bet::server::games::slots::SlotsGame;
13use zagan_bet::roulette::Bets;
14 14
15fn setup(name: &str, grid: &str) -> (ServerCore, String) { 15fn setup(name: &str, grid: &str) -> (ServerCore, String) {
16 let path = PathBuf::from(env!("CARGO_TARGET_TMPDIR")) 16 let path = PathBuf::from(env!("CARGO_TARGET_TMPDIR"))
@@ -63,8 +63,7 @@ fn setup_blackjack(name: &str, deck: &[&str]) -> (ServerCore, String) {
63 63
64 let deck = deck.iter().map(|card| card.to_string()).collect(); 64 let deck = deck.iter().map(|card| card.to_string()).collect();
65 ( 65 (
66 ServerCore::with_registry(conn, GameRegistry::with_defaults(), 4) 66 ServerCore::with_registry(conn, GameRegistry::with_defaults(), 4).with_blackjack_deck(deck),
67 .with_blackjack_deck(deck),
68 path, 67 path,
69 ) 68 )
70} 69}
@@ -216,7 +215,10 @@ fn plays_record_the_game_type_and_game_specific_details() {
216 login(&mut core, &mut state, "alice"); 215 login(&mut core, &mut state, "alice");
217 216
218 let response = core.handle_message(&mut state, "PLAY_START slots 10"); 217 let response = core.handle_message(&mut state, "PLAY_START slots 10");
219 assert!(response.starts_with("GAME_RESULT"), "spin failed: {response}"); 218 assert!(
219 response.starts_with("GAME_RESULT"),
220 "spin failed: {response}"
221 );
220 222
221 let conn = Connection::open(&path).unwrap(); 223 let conn = Connection::open(&path).unwrap();
222 let (game_type, details): (String, String) = conn 224 let (game_type, details): (String, String) = conn
@@ -242,8 +244,7 @@ fn roulette_settles_split_bets_and_records_details() {
242 register(&mut core, &mut state, "alice"); 244 register(&mut core, &mut state, "alice");
243 login(&mut core, &mut state, "alice"); 245 login(&mut core, &mut state, "alice");
244 246
245 let bets = 247 let bets = r#"{"red":10,"black":10,"even":10,"odd":10,"numbers":[{"number":17,"amount":5}]}"#;
246 r#"{"red":10,"black":10,"even":10,"odd":10,"numbers":[{"number":17,"amount":5}]}"#;
247 let response = core.handle_message(&mut state, &format!("PLAY_START roulette 45 {bets}")); 248 let response = core.handle_message(&mut state, &format!("PLAY_START roulette 45 {bets}"));
248 249
249 assert!( 250 assert!(
@@ -254,7 +255,10 @@ fn roulette_settles_split_bets_and_records_details() {
254 response.contains(" 220 45 1175 17 black "), 255 response.contains(" 220 45 1175 17 black "),
255 "response: {response}" 256 "response: {response}"
256 ); 257 );
257 assert!(response.ends_with(&format!(" {bets}")), "response: {response}"); 258 assert!(
259 response.ends_with(&format!(" {bets}")),
260 "response: {response}"
261 );
258 assert_eq!(core.balance("alice"), Some(1175)); 262 assert_eq!(core.balance("alice"), Some(1175));
259 assert_eq!(core.play_count("alice"), 1); 263 assert_eq!(core.play_count("alice"), 1);
260 assert_eq!(core.session_count(), 0); 264 assert_eq!(core.session_count(), 0);
@@ -327,7 +331,10 @@ fn a_winning_spin_updates_the_balance_and_the_audit_row() {
327 login(&mut core, &mut state, "alice"); 331 login(&mut core, &mut state, "alice");
328 332
329 let response = core.handle_message(&mut state, "PLAY_START slots 10"); 333 let response = core.handle_message(&mut state, "PLAY_START slots 10");
330 assert!(response.starts_with("GAME_RESULT"), "spin failed: {response}"); 334 assert!(
335 response.starts_with("GAME_RESULT"),
336 "spin failed: {response}"
337 );
331 assert!(response.contains(" 10 10 1000 "), "response: {response}"); 338 assert!(response.contains(" 10 10 1000 "), "response: {response}");
332 assert!(response.ends_with(" win 1 0,3,6"), "response: {response}"); 339 assert!(response.ends_with(" win 1 0,3,6"), "response: {response}");
333 340
@@ -364,11 +371,8 @@ fn a_negative_balance_is_rejected_without_wrapping() {
364 login(&mut core, &mut state, "alice"); 371 login(&mut core, &mut state, "alice");
365 372
366 let conn = Connection::open(&path).unwrap(); 373 let conn = Connection::open(&path).unwrap();
367 conn.execute( 374 conn.execute("UPDATE users SET balance = -5 WHERE username = 'alice'", [])
368 "UPDATE users SET balance = -5 WHERE username = 'alice'", 375 .unwrap();
369 [],
370 )
371 .unwrap();
372 376
373 assert_eq!( 377 assert_eq!(
374 core.handle_message(&mut state, "PLAY_START slots 10"), 378 core.handle_message(&mut state, "PLAY_START slots 10"),
@@ -394,10 +398,7 @@ fn unknown_games_are_rejected_before_a_session_is_created() {
394 398
395#[test] 399#[test]
396fn blackjack_reserves_the_bet_and_settles_a_hit_hand() { 400fn blackjack_reserves_the_bet_and_settles_a_hit_hand() {
397 let (mut core, path) = setup_blackjack( 401 let (mut core, path) = setup_blackjack("blackjack-hit", &["10S", "9D", "6H", "8C", "5D"]);
398 "blackjack-hit",
399 &["10S", "9D", "6H", "8C", "5D"],
400 );
401 let mut state = ConnectionState::default(); 402 let mut state = ConnectionState::default();
402 register(&mut core, &mut state, "alice"); 403 register(&mut core, &mut state, "alice");
403 login(&mut core, &mut state, "alice"); 404 login(&mut core, &mut state, "alice");
@@ -442,10 +443,7 @@ fn blackjack_reserves_the_bet_and_settles_a_hit_hand() {
442 443
443#[test] 444#[test]
444fn blackjack_double_reserves_an_extra_bet() { 445fn blackjack_double_reserves_an_extra_bet() {
445 let (mut core, path) = setup_blackjack( 446 let (mut core, path) = setup_blackjack("blackjack-double", &["5S", "9D", "5H", "8C", "10D"]);
446 "blackjack-double",
447 &["5S", "9D", "5H", "8C", "10D"],
448 );
449 let mut state = ConnectionState::default(); 447 let mut state = ConnectionState::default();
450 register(&mut core, &mut state, "alice"); 448 register(&mut core, &mut state, "alice");
451 login(&mut core, &mut state, "alice"); 449 login(&mut core, &mut state, "alice");
@@ -483,8 +481,7 @@ fn blackjack_double_reserves_an_extra_bet() {
483 481
484#[test] 482#[test]
485fn blackjack_natural_settles_immediately() { 483fn blackjack_natural_settles_immediately() {
486 let (mut core, _path) = 484 let (mut core, _path) = setup_blackjack("blackjack-natural", &["AS", "9D", "KH", "8C"]);
487 setup_blackjack("blackjack-natural", &["AS", "9D", "KH", "8C"]);
488 let mut state = ConnectionState::default(); 485 let mut state = ConnectionState::default();
489 register(&mut core, &mut state, "alice"); 486 register(&mut core, &mut state, "alice");
490 login(&mut core, &mut state, "alice"); 487 login(&mut core, &mut state, "alice");
@@ -501,8 +498,7 @@ fn blackjack_natural_settles_immediately() {
501 498
502#[test] 499#[test]
503fn blackjack_forfeit_keeps_the_reserved_bet() { 500fn blackjack_forfeit_keeps_the_reserved_bet() {
504 let (mut core, _path) = 501 let (mut core, _path) = setup_blackjack("blackjack-cancel", &["10S", "9D", "6H", "8C"]);
505 setup_blackjack("blackjack-cancel", &["10S", "9D", "6H", "8C"]);
506 let mut state = ConnectionState::default(); 502 let mut state = ConnectionState::default();
507 register(&mut core, &mut state, "alice"); 503 register(&mut core, &mut state, "alice");
508 login(&mut core, &mut state, "alice"); 504 login(&mut core, &mut state, "alice");
@@ -524,8 +520,7 @@ fn blackjack_forfeit_keeps_the_reserved_bet() {
524 520
525#[test] 521#[test]
526fn blackjack_disconnect_forfeits_the_reserved_bet() { 522fn blackjack_disconnect_forfeits_the_reserved_bet() {
527 let (mut core, _path) = 523 let (mut core, _path) = setup_blackjack("blackjack-disconnect", &["10S", "9D", "6H", "8C"]);
528 setup_blackjack("blackjack-disconnect", &["10S", "9D", "6H", "8C"]);
529 let mut state = ConnectionState::default(); 524 let mut state = ConnectionState::default();
530 register(&mut core, &mut state, "alice"); 525 register(&mut core, &mut state, "alice");
531 login(&mut core, &mut state, "alice"); 526 login(&mut core, &mut state, "alice");
@@ -578,10 +573,9 @@ fn crash_cashout_pays_the_multiplier_from_the_server_clock() {
578 573
579 // Five seconds of round time: 2.00x, still below the 3.00x crash point. 574 // Five seconds of round time: 2.00x, still below the 3.00x crash point.
580 clock.advance(5.0); 575 clock.advance(5.0);
581 let cashed = crash_state(&core.handle_message( 576 let cashed = crash_state(
582 &mut state, 577 &core.handle_message(&mut state, &format!("CRASH_CASHOUT {}", started.session_id)),
583 &format!("CRASH_CASHOUT {}", started.session_id), 578 );
584 ));
585 579
586 assert_eq!(cashed.phase, "cashed"); 580 assert_eq!(cashed.phase, "cashed");
587 assert_eq!(cashed.multiplier, 2.0); 581 assert_eq!(cashed.multiplier, 2.0);
@@ -600,7 +594,15 @@ fn crash_cashout_pays_the_multiplier_from_the_server_clock() {
600 "SELECT game_type, bet_amount, winnings, balance_after, details 594 "SELECT game_type, bet_amount, winnings, balance_after, details
601 FROM plays WHERE username = 'alice'", 595 FROM plays WHERE username = 'alice'",
602 [], 596 [],
603 |row| Ok((row.get(0)?, row.get(1)?, row.get(2)?, row.get(3)?, row.get(4)?)), 597 |row| {
598 Ok((
599 row.get(0)?,
600 row.get(1)?,
601 row.get(2)?,
602 row.get(3)?,
603 row.get(4)?,
604 ))
605 },
604 ) 606 )
605 .unwrap(); 607 .unwrap();
606 assert_eq!(game_type, "crash"); 608 assert_eq!(game_type, "crash");
@@ -627,10 +629,9 @@ fn a_cashout_after_the_crash_time_loses_the_reserved_bet() {
627 let started = crash_state(&core.handle_message(&mut state, "PLAY_START crash 10")); 629 let started = crash_state(&core.handle_message(&mut state, "PLAY_START crash 10"));
628 630
629 clock.advance(20.0); 631 clock.advance(20.0);
630 let late = crash_state(&core.handle_message( 632 let late = crash_state(
631 &mut state, 633 &core.handle_message(&mut state, &format!("CRASH_CASHOUT {}", started.session_id)),
632 &format!("CRASH_CASHOUT {}", started.session_id), 634 );
633 ));
634 635
635 assert_eq!(late.phase, "crashed"); 636 assert_eq!(late.phase, "crashed");
636 assert_eq!(late.payout, 0); 637 assert_eq!(late.payout, 0);
@@ -679,10 +680,9 @@ fn crash_forfeit_keeps_the_reserved_bet() {
679 login(&mut core, &mut state, "alice"); 680 login(&mut core, &mut state, "alice");
680 681
681 let started = crash_state(&core.handle_message(&mut state, "PLAY_START crash 10")); 682 let started = crash_state(&core.handle_message(&mut state, "PLAY_START crash 10"));
682 let forfeited = crash_state(&core.handle_message( 683 let forfeited = crash_state(
683 &mut state, 684 &core.handle_message(&mut state, &format!("CRASH_FORFEIT {}", started.session_id)),
684 &format!("CRASH_FORFEIT {}", started.session_id), 685 );
685 ));
686 686
687 assert_eq!(forfeited.phase, "forfeit"); 687 assert_eq!(forfeited.phase, "forfeit");
688 assert_eq!(forfeited.payout, 0); 688 assert_eq!(forfeited.payout, 0);
@@ -747,10 +747,7 @@ fn crash_rounds_are_owned_by_the_player_who_started_them() {
747 747
748 clock.advance(5.0); 748 clock.advance(5.0);
749 assert_eq!( 749 assert_eq!(
750 core.handle_message( 750 core.handle_message(&mut bob, &format!("CRASH_CASHOUT {}", started.session_id)),
751 &mut bob,
752 &format!("CRASH_CASHOUT {}", started.session_id)
753 ),
754 "ERROR Invalid session" 751 "ERROR Invalid session"
755 ); 752 );
756 assert_eq!(core.balance("bob"), Some(1000)); 753 assert_eq!(core.balance("bob"), Some(1000));
@@ -775,10 +772,9 @@ fn a_crash_reservation_is_durable_until_the_round_settles() {
775 drop(conn); 772 drop(conn);
776 773
777 clock.advance(5.0); 774 clock.advance(5.0);
778 let cashed = crash_state(&core.handle_message( 775 let cashed = crash_state(
779 &mut state, 776 &core.handle_message(&mut state, &format!("CRASH_CASHOUT {}", started.session_id)),
780 &format!("CRASH_CASHOUT {}", started.session_id), 777 );
781 ));
782 assert_eq!(cashed.phase, "cashed"); 778 assert_eq!(cashed.phase, "cashed");
783 779
784 let conn = Connection::open(&path).unwrap(); 780 let conn = Connection::open(&path).unwrap();
diff --git a/tests/slots.rs b/tests/slots.rs
index 8bdd9af..7f0b6b5 100644
--- a/tests/slots.rs
+++ b/tests/slots.rs
@@ -1,8 +1,8 @@
1//! Regression tests for the server-side 5x3 slots game. 1//! Regression tests for the server-side 5x3 slots game.
2 2
3use zagan_bet::slots::{paytable_lines, LINE_NAMES, SHAPE_MULTIPLIERS, SYMBOL_MULTIPLIERS};
4use zagan_bet::server::games::slots::{LineWin, SlotsGame, PAYLINES, REELS, ROWS}; 3use zagan_bet::server::games::slots::{LineWin, SlotsGame, PAYLINES, REELS, ROWS};
5use zagan_bet::server::games::traits::{Game, GamePhase}; 4use zagan_bet::server::games::traits::{Game, GamePhase};
5use zagan_bet::slots::{paytable_lines, LINE_NAMES, SHAPE_MULTIPLIERS, SYMBOL_MULTIPLIERS};
6 6
7/// Convert row-major test rows into the column-major wire format. 7/// Convert row-major test rows into the column-major wire format.
8fn grid(rows: [&str; 3]) -> String { 8fn grid(rows: [&str; 3]) -> String {
@@ -62,7 +62,10 @@ fn four_of_a_kind_pays_more_than_three() {
62fn five_of_a_kind_pays_all_lines() { 62fn five_of_a_kind_pays_all_lines() {
63 let (winnings, wins) = payout(["SSSSS", "SSSSS", "SSSSS"], 10); 63 let (winnings, wins) = payout(["SSSSS", "SSSSS", "SSSSS"], 10);
64 64
65 assert_eq!(winnings, 7000, "five SSSSS lines: 5 * 10 * S(10) * shape5(14)"); 65 assert_eq!(
66 winnings, 7000,
67 "five SSSSS lines: 5 * 10 * S(10) * shape5(14)"
68 );
66 assert_eq!(lines(&wins), vec![1, 2, 3, 4, 5]); 69 assert_eq!(lines(&wins), vec![1, 2, 3, 4, 5]);
67 assert_eq!(wins[0].cells, vec![0, 3, 6, 9, 12]); 70 assert_eq!(wins[0].cells, vec![0, 3, 6, 9, 12]);
68 assert_eq!(wins[3].cells, vec![0, 4, 8, 10, 12]); 71 assert_eq!(wins[3].cells, vec![0, 4, 8, 10, 12]);
diff --git a/tests/tofu.rs b/tests/tofu.rs
index 4f59d6d..b644cc4 100644
--- a/tests/tofu.rs
+++ b/tests/tofu.rs
@@ -74,7 +74,10 @@ fn fingerprint_is_sha256_of_spki() {
74 let expected = openssl_spki_sha256(dir.join("primary.pem").to_str().unwrap()); 74 let expected = openssl_spki_sha256(dir.join("primary.pem").to_str().unwrap());
75 75
76 let fp = spki_fingerprint(&der).expect("must parse the test certificate"); 76 let fp = spki_fingerprint(&der).expect("must parse the test certificate");
77 assert_eq!(fp, expected, "fingerprint must equal openssl's SPKI SHA-256"); 77 assert_eq!(
78 fp, expected,
79 "fingerprint must equal openssl's SPKI SHA-256"
80 );
78} 81}
79 82
80#[test] 83#[test]
@@ -90,10 +93,7 @@ fn first_contact_trusts_and_records() {
90 93
91#[test] 94#[test]
92fn matching_key_is_trusted() { 95fn matching_key_is_trusted() {
93 assert_eq!( 96 assert_eq!(decide(Some("ab12cd34"), "ab12cd34"), TrustDecision::Trusted);
94 decide(Some("ab12cd34"), "ab12cd34"),
95 TrustDecision::Trusted
96 );
97} 97}
98 98
99#[test] 99#[test]