//! Regression tests for the server-side slots game logic. use zagan_bet::server_game_traits::{Game, GamePhase}; use zagan_bet::server_slots::SlotsGame; fn make_game() -> SlotsGame { SlotsGame::new() } /// Three of a kind pays the three-of-a-kind multiplier. #[test] fn three_of_a_kind_pays() { let game = make_game(); // CCC = 5x assert_eq!(game.calculate_payout(&['C', 'C', 'C'], 100), 500); // SSS = 100x assert_eq!(game.calculate_payout(&['S', 'S', 'S'], 10), 1000); } /// Two adjacent matching symbols pays the two-of-a-kind multiplier. #[test] fn two_adjacent_pays() { let game = make_game(); // CC? = 2x assert_eq!(game.calculate_payout(&['C', 'C', 'D'], 100), 200); // ?LL = 3x assert_eq!(game.calculate_payout(&['D', 'L', 'L'], 50), 150); } /// Non-adjacent matching symbols (e.g. [C,D,C]) must also pay. /// This was a bug: the old code only checked pairs (0,1) and (1,2). #[test] fn two_non_adjacent_pays() { let game = make_game(); // C,D,C — two cherries at positions 0 and 2 assert_eq!(game.calculate_payout(&['C', 'D', 'C'], 100), 200); // S,X,S — two sevens at positions 0 and 2 assert_eq!(game.calculate_payout(&['S', 'L', 'S'], 10), 200); // L,O,L assert_eq!(game.calculate_payout(&['L', 'O', 'L'], 50), 150); } /// No matching symbols returns 0. #[test] fn no_match_returns_zero() { let game = make_game(); assert_eq!(game.calculate_payout(&['C', 'L', 'O'], 100), 0); assert_eq!(game.calculate_payout(&['G', 'D', 'S'], 50), 0); } /// process_action produces a finished state with correct reels and win_type. #[test] fn process_action_spin_sets_finished() { let game = make_game(); let state = game.prepare_state("alice", 100); assert_eq!(state.phase, GamePhase::WaitingToStart); // Manually set reels by going through process_action. // Since spin is random, we test the state machine rather than the outcome. let result = game.process_action(&state, "spin", &[]); assert!(result.is_ok()); let new_state = result.unwrap(); assert_eq!(new_state.phase, GamePhase::Finished); assert!(new_state.data.contains_key("reels")); assert!(new_state.data.contains_key("win_type")); } /// calculate_result on a finished state returns valid GameResult. #[test] fn calculate_result_on_finished_state() { let game = make_game(); let mut state = game.prepare_state("alice", 100); state.phase = GamePhase::Finished; state .data .insert("reels".to_string(), "CCD".to_string()); state .data .insert("win_type".to_string(), "two".to_string()); let result = game.calculate_result(&state).unwrap(); assert_eq!(result.bet_amount, 100); assert_eq!(result.winnings, 200); // CC = 2x } /// calculate_result on a non-finished state returns an error. #[test] fn calculate_result_on_unfinished_state() { let game = make_game(); let state = game.prepare_state("alice", 100); assert!(game.calculate_result(&state).is_err()); }