From b5a52fc0312afdf5d5fbe53275dc88d6d881c2ff Mon Sep 17 00:00:00 2001 From: salmonik Date: Thu, 29 Oct 2026 18:22:40 +0100 Subject: v2.2.0: add blackjack with reserved bets and hit/stand/double --- tests/server_core.rs | 159 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 159 insertions(+) (limited to 'tests/server_core.rs') diff --git a/tests/server_core.rs b/tests/server_core.rs index cf05102..3ad277a 100644 --- a/tests/server_core.rs +++ b/tests/server_core.rs @@ -1,6 +1,7 @@ use std::path::PathBuf; use rusqlite::Connection; +use zagan_bet::blackjack::BlackjackState; use zagan_bet::server::core::{ConnectionState, ServerCore}; use zagan_bet::server::database; use zagan_bet::server::games::registry::GameRegistry; @@ -47,6 +48,31 @@ fn setup_roulette(name: &str, number: u8) -> (ServerCore, String) { (ServerCore::with_registry(conn, registry, 4), path) } +fn setup_blackjack(name: &str, deck: &[&str]) -> (ServerCore, String) { + let path = PathBuf::from(env!("CARGO_TARGET_TMPDIR")) + .join(format!("server-core-{name}.db")) + .to_string_lossy() + .into_owned(); + let _ = std::fs::remove_file(&path); + + let conn = Connection::open(&path).unwrap(); + database::init_db(&conn); + + let deck = deck.iter().map(|card| card.to_string()).collect(); + ( + ServerCore::with_registry(conn, GameRegistry::with_defaults(), 4) + .with_blackjack_deck(deck), + path, + ) +} + +fn blackjack_state(response: &str) -> BlackjackState { + let raw = response + .strip_prefix("BLACKJACK_STATE ") + .expect("blackjack state response"); + serde_json::from_str(raw).expect("valid blackjack state") +} + fn register(core: &mut ServerCore, state: &mut ConnectionState, user: &str) { assert_eq!( core.handle_message(state, &format!("REGISTER {user} credential")), @@ -337,3 +363,136 @@ fn unknown_games_are_rejected_before_a_session_is_created() { ); assert_eq!(core.session_count(), 0); } + +#[test] +fn blackjack_reserves_the_bet_and_settles_a_hit_hand() { + let (mut core, path) = setup_blackjack( + "blackjack-hit", + &["10S", "9D", "6H", "8C", "5D"], + ); + let mut state = ConnectionState::default(); + register(&mut core, &mut state, "alice"); + login(&mut core, &mut state, "alice"); + + let response = core.handle_message(&mut state, "PLAY_START blackjack 10"); + let current = blackjack_state(&response); + + assert_eq!(current.phase, "player"); + assert_eq!(current.player_total, 16); + assert_eq!(current.dealer, vec!["9D".to_string(), "?".to_string()]); + assert_eq!(current.balance, 990); + assert_eq!(current.actions, vec!["hit", "stand", "double"]); + assert_eq!(core.session_count(), 1); + + let response = core.handle_message( + &mut state, + &format!("BLACKJACK_ACTION {} hit", current.session_id), + ); + let done = blackjack_state(&response); + + assert_eq!(done.phase, "done"); + assert_eq!(done.result, "win"); + assert_eq!(done.winnings, 20); + assert_eq!(done.balance, 1010); + assert_eq!(core.balance("alice"), Some(1010)); + assert_eq!(core.play_count("alice"), 1); + assert_eq!(core.session_count(), 0); + + let conn = Connection::open(&path).unwrap(); + let (game_type, details): (String, String) = conn + .query_row( + "SELECT game_type, details FROM plays WHERE username = 'alice'", + [], + |row| Ok((row.get(0)?, row.get(1)?)), + ) + .unwrap(); + assert_eq!(game_type, "blackjack"); + let details: BlackjackState = serde_json::from_str(&details).unwrap(); + assert_eq!(details.result, "win"); + assert_eq!(details.winnings, 20); +} + +#[test] +fn blackjack_double_reserves_an_extra_bet() { + let (mut core, _path) = setup_blackjack( + "blackjack-double", + &["5S", "9D", "5H", "8C", "10D"], + ); + let mut state = ConnectionState::default(); + register(&mut core, &mut state, "alice"); + login(&mut core, &mut state, "alice"); + + let current = blackjack_state(&core.handle_message(&mut state, "PLAY_START blackjack 10")); + assert_eq!(current.balance, 990); + + let done = blackjack_state(&core.handle_message( + &mut state, + &format!("BLACKJACK_ACTION {} double", current.session_id), + )); + + assert_eq!(done.phase, "done"); + assert_eq!(done.bet, 20); + assert_eq!(done.winnings, 40); + assert_eq!(done.balance, 1020); + assert_eq!(core.balance("alice"), Some(1020)); + assert_eq!(core.play_count("alice"), 1); +} + +#[test] +fn blackjack_natural_settles_immediately() { + let (mut core, _path) = + setup_blackjack("blackjack-natural", &["AS", "9D", "KH", "8C"]); + let mut state = ConnectionState::default(); + register(&mut core, &mut state, "alice"); + login(&mut core, &mut state, "alice"); + + let done = blackjack_state(&core.handle_message(&mut state, "PLAY_START blackjack 10")); + + assert_eq!(done.phase, "done"); + assert_eq!(done.result, "blackjack"); + assert_eq!(done.winnings, 25); + assert_eq!(done.balance, 1015); + assert_eq!(core.session_count(), 0); + assert_eq!(core.play_count("alice"), 1); +} + +#[test] +fn blackjack_cancel_refunds_the_reserved_bet() { + let (mut core, _path) = + setup_blackjack("blackjack-cancel", &["10S", "9D", "6H", "8C"]); + let mut state = ConnectionState::default(); + register(&mut core, &mut state, "alice"); + login(&mut core, &mut state, "alice"); + + let current = blackjack_state(&core.handle_message(&mut state, "PLAY_START blackjack 10")); + assert_eq!(current.balance, 990); + + let cancelled = blackjack_state(&core.handle_message( + &mut state, + &format!("BLACKJACK_ACTION {} cancel", current.session_id), + )); + + assert_eq!(cancelled.result, "cancelled"); + assert_eq!(cancelled.balance, 1000); + assert_eq!(core.balance("alice"), Some(1000)); + assert_eq!(core.play_count("alice"), 0); + assert_eq!(core.session_count(), 0); +} + +#[test] +fn blackjack_disconnect_refunds_the_reserved_bet() { + let (mut core, _path) = + setup_blackjack("blackjack-disconnect", &["10S", "9D", "6H", "8C"]); + let mut state = ConnectionState::default(); + register(&mut core, &mut state, "alice"); + login(&mut core, &mut state, "alice"); + + let current = blackjack_state(&core.handle_message(&mut state, "PLAY_START blackjack 10")); + assert_eq!(current.balance, 990); + + core.disconnect(&state); + + assert_eq!(core.balance("alice"), Some(1000)); + assert_eq!(core.play_count("alice"), 0); + assert_eq!(core.session_count(), 0); +} -- cgit v1.2.3