use zagan_bet::roulette::{ color, is_black, is_even, is_odd, is_red, rtp, Bets, NumberBet, WHEEL, }; use zagan_bet::server::games::roulette::RouletteGame; use zagan_bet::server::games::traits::Game; #[test] fn the_european_wheel_has_all_37_unique_pockets() { let mut numbers = WHEEL.to_vec(); numbers.sort_unstable(); assert_eq!(numbers, (0..=36).collect::>()); assert_eq!(color(0), "green"); assert_eq!(WHEEL.len(), 37); } #[test] fn colors_and_even_odd_are_correct() { assert!(is_red(1)); assert!(is_black(2)); assert!(!is_red(0)); assert!(!is_black(0)); assert!(is_even(2)); assert!(is_odd(3)); assert!(!is_even(0)); assert!(!is_odd(0)); assert_eq!(color(17), "black"); assert_eq!(color(18), "red"); } #[test] fn european_rtp_is_36_over_37() { assert!((rtp() - 36.0 / 37.0).abs() < 1e-12); } #[test] fn bets_total_and_winning_number_payouts() { let bets = Bets { red: 10, black: 10, even: 10, odd: 10, numbers: vec![NumberBet { number: 17, amount: 5, }], }; assert_eq!(bets.total(), 45); // 17 is black and odd: 10*2 + 10*2 + 5*36. assert_eq!(bets.winnings(17), 220); // 18 is red and even. assert_eq!(bets.winnings(18), 40); } #[test] fn zero_loses_all_outside_bets_but_pays_a_straight_zero() { let outside = Bets { red: 10, black: 10, even: 10, odd: 10, numbers: vec![], }; assert_eq!(outside.winnings(0), 0); let straight = Bets { red: 10, black: 10, even: 10, odd: 10, numbers: vec![NumberBet { number: 0, amount: 5, }], }; assert_eq!(straight.total(), 45); assert_eq!(straight.winnings(0), 180); } #[test] fn bets_validate_empty_balance_and_invalid_numbers() { assert!(Bets::default().validate(1000).is_err()); let too_big = Bets { red: 2000, ..Bets::default() }; assert!(too_big.validate(1000).is_err()); let bad_number = Bets { numbers: vec![NumberBet { number: 37, amount: 10, }], ..Bets::default() }; assert!(bad_number.validate(1000).is_err()); } #[test] fn the_server_settles_a_split_bet() { let bets = Bets { red: 10, black: 10, even: 10, odd: 10, numbers: vec![NumberBet { number: 17, amount: 5, }], }; let payload = serde_json::to_string(&bets).unwrap(); let game = RouletteGame::with_result(17); let state = game.prepare_state("alice", bets.total()); let state = game.process_action(&state, "spin", &[&payload]).unwrap(); let result = game.calculate_result(&state).unwrap(); assert_eq!(result.winnings, 220); assert_eq!(result.result_data.get("number").unwrap(), "17"); assert_eq!(result.result_data.get("color").unwrap(), "black"); assert_eq!(result.result_data.get("bets").unwrap(), &payload); } #[test] fn the_server_rejects_a_total_that_does_not_match_the_bets() { let payload = serde_json::to_string(&Bets { red: 10, ..Bets::default() }) .unwrap(); let game = RouletteGame::with_result(17); let state = game.prepare_state("alice", 99); assert!(game.process_action(&state, "spin", &[&payload]).is_err()); } #[test] fn the_server_rejects_an_invalid_number() { let payload = serde_json::to_string(&Bets { numbers: vec![NumberBet { number: 37, amount: 10, }], ..Bets::default() }) .unwrap(); let game = RouletteGame::with_result(17); let state = game.prepare_state("alice", 10); assert!(game.process_action(&state, "spin", &[&payload]).is_err()); } #[test] fn parses_a_roulette_result() { let bets = r#"{"red":10,"black":0,"even":10,"odd":0,"numbers":[{"number":17,"amount":10}]}"#; let response = format!("ROULETTE_RESULT session-1 360 30 1330 17 black {bets}"); let result = zagan_bet::client::games::response::parse_roulette_response(&response).unwrap(); assert_eq!(result.session_id, "session-1"); assert_eq!(result.winnings, 360); assert_eq!(result.bet_amount, 30); assert_eq!(result.balance, 1330); assert_eq!(result.number, 17); assert_eq!(result.color, "black"); assert_eq!(result.bets, bets); } #[test] fn rejects_bad_roulette_results() { use zagan_bet::client::games::response::parse_roulette_response; let valid_bets = r#"{"red":10}"#; assert!(parse_roulette_response(&format!( "ROULETTE_RESULT s 1 1 1 37 green {valid_bets}" )) .is_err()); assert!(parse_roulette_response(&format!( "ROULETTE_RESULT s 1 1 1 17 purple {valid_bets}" )) .is_err()); assert!(parse_roulette_response("ROULETTE_RESULT s 1 1 1 17 black nope").is_err()); assert!(parse_roulette_response("").is_err()); }