diff options
Diffstat (limited to '')
| -rw-r--r-- | tests/blackjack.rs | 139 | ||||
| -rw-r--r-- | tests/client_core.rs | 201 | ||||
| -rw-r--r-- | tests/server_core.rs | 159 |
3 files changed, 498 insertions, 1 deletions
diff --git a/tests/blackjack.rs b/tests/blackjack.rs new file mode 100644 index 0000000..79721d3 --- /dev/null +++ b/tests/blackjack.rs | |||
| @@ -0,0 +1,139 @@ | |||
| 1 | use zagan_bet::blackjack::{ | ||
| 2 | card_label, card_value, fresh_shoe, hand_total, is_blackjack, BlackjackState, | ||
| 3 | }; | ||
| 4 | use zagan_bet::server::games::blackjack::BlackjackGame; | ||
| 5 | |||
| 6 | fn hand(deck: &[&str], bet: u64) -> zagan_bet::server::games::blackjack::BlackjackHand { | ||
| 7 | let deck = deck.iter().map(|card| card.to_string()).collect(); | ||
| 8 | BlackjackGame::with_deck(deck).deal(bet) | ||
| 9 | } | ||
| 10 | |||
| 11 | #[test] | ||
| 12 | fn card_values_and_soft_totals_are_correct() { | ||
| 13 | assert_eq!(card_value("AS"), 11); | ||
| 14 | assert_eq!(card_value("10H"), 10); | ||
| 15 | assert_eq!(card_value("KD"), 10); | ||
| 16 | assert_eq!(card_label("AS"), "A♠"); | ||
| 17 | assert_eq!(card_label("10H"), "10♥"); | ||
| 18 | |||
| 19 | let cards = |values: &[&str]| values.iter().map(|card| card.to_string()).collect::<Vec<_>>(); | ||
| 20 | assert_eq!(hand_total(&cards(&["AS", "10H"])), 21); | ||
| 21 | assert_eq!(hand_total(&cards(&["AS", "9H"])), 20); | ||
| 22 | assert_eq!(hand_total(&cards(&["AS", "AS", "9H"])), 21); | ||
| 23 | assert!(is_blackjack(&cards(&["AS", "KH"]))); | ||
| 24 | assert!(!is_blackjack(&cards(&["AS", "9H"]))); | ||
| 25 | } | ||
| 26 | |||
| 27 | #[test] | ||
| 28 | fn a_six_deck_shoe_has_312_cards() { | ||
| 29 | let shoe = fresh_shoe(); | ||
| 30 | assert_eq!(shoe.len(), 312); | ||
| 31 | assert_eq!(shoe.iter().filter(|card| card.as_str() == "AS").count(), 6); | ||
| 32 | } | ||
| 33 | |||
| 34 | #[test] | ||
| 35 | fn a_natural_blackjack_settles_immediately() { | ||
| 36 | let hand = hand(&["AS", "9D", "KH", "8C"], 10); | ||
| 37 | |||
| 38 | assert!(hand.is_done()); | ||
| 39 | assert_eq!(hand.result, "blackjack"); | ||
| 40 | assert_eq!(hand.winnings, 25); // 10 + 15 | ||
| 41 | assert_eq!(hand.player_total(), 21); | ||
| 42 | } | ||
| 43 | |||
| 44 | #[test] | ||
| 45 | fn a_player_hand_can_hit_to_21() { | ||
| 46 | let mut hand = hand(&["10S", "9D", "6H", "8C", "5D"], 10); | ||
| 47 | assert_eq!(hand.player_total(), 16); | ||
| 48 | assert_eq!(hand.dealer_total(), 17); | ||
| 49 | |||
| 50 | hand.hit().unwrap(); | ||
| 51 | |||
| 52 | assert!(hand.is_done()); | ||
| 53 | assert_eq!(hand.player_total(), 21); | ||
| 54 | assert_eq!(hand.result, "win"); | ||
| 55 | assert_eq!(hand.winnings, 20); | ||
| 56 | } | ||
| 57 | |||
| 58 | #[test] | ||
| 59 | fn hitting_over_21_busts() { | ||
| 60 | let mut hand = hand(&["10S", "9D", "6H", "8C", "KD"], 10); | ||
| 61 | hand.hit().unwrap(); | ||
| 62 | |||
| 63 | assert!(hand.is_done()); | ||
| 64 | assert_eq!(hand.result, "bust"); | ||
| 65 | assert_eq!(hand.winnings, 0); | ||
| 66 | } | ||
| 67 | |||
| 68 | #[test] | ||
| 69 | fn doubling_adds_the_base_bet_and_draws_one_card() { | ||
| 70 | let mut hand = hand(&["5S", "9D", "5H", "8C", "10D"], 10); | ||
| 71 | assert_eq!(hand.player_total(), 10); | ||
| 72 | |||
| 73 | hand.double().unwrap(); | ||
| 74 | |||
| 75 | assert_eq!(hand.player.len(), 3); | ||
| 76 | assert_eq!(hand.bet, 20); | ||
| 77 | assert_eq!(hand.player_total(), 20); | ||
| 78 | assert_eq!(hand.result, "win"); | ||
| 79 | assert_eq!(hand.winnings, 40); | ||
| 80 | } | ||
| 81 | |||
| 82 | #[test] | ||
| 83 | fn the_dealer_busts_and_the_player_wins() { | ||
| 84 | let mut hand = hand(&["10S", "7H", "8H", "9C", "10H"], 10); | ||
| 85 | assert_eq!(hand.player_total(), 18); | ||
| 86 | assert_eq!(hand.dealer_total(), 16); | ||
| 87 | |||
| 88 | hand.stand(); | ||
| 89 | |||
| 90 | assert_eq!(hand.dealer_total(), 26); | ||
| 91 | assert_eq!(hand.result, "win"); | ||
| 92 | assert_eq!(hand.winnings, 20); | ||
| 93 | } | ||
| 94 | |||
| 95 | #[test] | ||
| 96 | fn the_dealer_stands_on_soft_17() { | ||
| 97 | let mut hand = hand(&["10S", "AD", "8H", "6C"], 10); | ||
| 98 | assert_eq!(hand.dealer_total(), 17); | ||
| 99 | |||
| 100 | hand.stand(); | ||
| 101 | |||
| 102 | assert_eq!(hand.dealer.len(), 2, "dealer must stand on soft 17"); | ||
| 103 | assert_eq!(hand.result, "win"); | ||
| 104 | assert_eq!(hand.winnings, 20); | ||
| 105 | } | ||
| 106 | |||
| 107 | #[test] | ||
| 108 | fn equal_totals_push() { | ||
| 109 | let mut hand = hand(&["10S", "10D", "8H", "8C"], 10); | ||
| 110 | hand.stand(); | ||
| 111 | |||
| 112 | assert_eq!(hand.result, "push"); | ||
| 113 | assert_eq!(hand.winnings, 10); | ||
| 114 | } | ||
| 115 | |||
| 116 | #[test] | ||
| 117 | fn dealer_blackjack_wins_unless_player_also_has_it() { | ||
| 118 | let dealer_blackjack = hand(&["10S", "AS", "8H", "KD"], 10); | ||
| 119 | assert_eq!(dealer_blackjack.result, "lose"); | ||
| 120 | assert_eq!(dealer_blackjack.winnings, 0); | ||
| 121 | |||
| 122 | let both = hand(&["AS", "AD", "KH", "KC"], 10); | ||
| 123 | assert_eq!(both.result, "push"); | ||
| 124 | assert_eq!(both.winnings, 10); | ||
| 125 | } | ||
| 126 | |||
| 127 | #[test] | ||
| 128 | fn the_player_state_hides_the_hole_card() { | ||
| 129 | let hand = hand(&["10S", "9D", "6H", "8C"], 10); | ||
| 130 | let state: BlackjackState = hand.state("session-1", 990); | ||
| 131 | |||
| 132 | assert_eq!(state.phase, "player"); | ||
| 133 | assert_eq!(state.dealer, vec!["9D".to_string(), "?".to_string()]); | ||
| 134 | assert_eq!(state.dealer_total, 9); | ||
| 135 | assert_eq!(state.player_total, 16); | ||
| 136 | assert_eq!(state.actions, vec!["hit", "stand", "double"]); | ||
| 137 | assert_eq!(state.bet, 10); | ||
| 138 | assert_eq!(state.balance, 990); | ||
| 139 | } | ||
diff --git a/tests/client_core.rs b/tests/client_core.rs index 7fb7e29..cfcf608 100644 --- a/tests/client_core.rs +++ b/tests/client_core.rs | |||
| @@ -4,8 +4,9 @@ | |||
| 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 | ||
| 7 | use zagan_bet::blackjack::BlackjackState; | ||
| 7 | use zagan_bet::client::core::{ClientCore, Effect, InteractiveCommand, Key}; | 8 | use zagan_bet::client::core::{ClientCore, Effect, InteractiveCommand, Key}; |
| 8 | use zagan_bet::client::games::response::parse_spin_response; | 9 | use zagan_bet::client::games::response::{parse_blackjack_response, parse_spin_response}; |
| 9 | use zagan_bet::client::ui::command_names; | 10 | use zagan_bet::client::ui::command_names; |
| 10 | 11 | ||
| 11 | const WIN3: &str = "CCCCLLCLODGSDGS"; | 12 | const WIN3: &str = "CCCCLLCLODGSDGS"; |
| @@ -139,6 +140,11 @@ impl Harness { | |||
| 139 | self.line("play roulette"); | 140 | self.line("play roulette"); |
| 140 | self.reply("INFO alice 1 1000"); | 141 | self.reply("INFO alice 1 1000"); |
| 141 | } | 142 | } |
| 143 | |||
| 144 | fn enter_blackjack(&mut self) { | ||
| 145 | self.line("play blackjack"); | ||
| 146 | self.reply("INFO alice 1 1000"); | ||
| 147 | } | ||
| 142 | } | 148 | } |
| 143 | 149 | ||
| 144 | #[test] | 150 | #[test] |
| @@ -647,3 +653,196 @@ fn roulette_rejects_an_out_of_range_number() { | |||
| 647 | assert!(h.board()[4].contains("ERROR: Number must be 0-36")); | 653 | assert!(h.board()[4].contains("ERROR: Number must be 0-36")); |
| 648 | assert!(h.board()[2].contains("Bets: (none)")); | 654 | assert!(h.board()[2].contains("Bets: (none)")); |
| 649 | } | 655 | } |
| 656 | |||
| 657 | fn bj_state( | ||
| 658 | session: &str, | ||
| 659 | phase: &str, | ||
| 660 | player: &[&str], | ||
| 661 | dealer: &[&str], | ||
| 662 | player_total: u8, | ||
| 663 | dealer_total: u8, | ||
| 664 | bet: u64, | ||
| 665 | base_bet: u64, | ||
| 666 | balance: i64, | ||
| 667 | actions: &[&str], | ||
| 668 | result: &str, | ||
| 669 | winnings: u64, | ||
| 670 | ) -> BlackjackState { | ||
| 671 | BlackjackState { | ||
| 672 | session_id: session.to_string(), | ||
| 673 | phase: phase.to_string(), | ||
| 674 | player: player.iter().map(|card| card.to_string()).collect(), | ||
| 675 | dealer: dealer.iter().map(|card| card.to_string()).collect(), | ||
| 676 | player_total, | ||
| 677 | dealer_total, | ||
| 678 | bet, | ||
| 679 | base_bet, | ||
| 680 | balance, | ||
| 681 | actions: actions.iter().map(|action| action.to_string()).collect(), | ||
| 682 | result: result.to_string(), | ||
| 683 | winnings, | ||
| 684 | } | ||
| 685 | } | ||
| 686 | |||
| 687 | #[test] | ||
| 688 | fn blackjack_deals_hits_and_settles() { | ||
| 689 | let mut h = Harness::new(); | ||
| 690 | h.enter_blackjack(); | ||
| 691 | |||
| 692 | assert_eq!(h.board().len(), 6); | ||
| 693 | assert!(h.board()[4].contains("Press [SPACE] to deal.")); | ||
| 694 | |||
| 695 | h.key(Key::Char(' ')); | ||
| 696 | assert!(h | ||
| 697 | .sent | ||
| 698 | .iter() | ||
| 699 | .any(|line| line == "PLAY_START blackjack 10")); | ||
| 700 | |||
| 701 | let player = bj_state( | ||
| 702 | "bj1", | ||
| 703 | "player", | ||
| 704 | &["10S", "6H"], | ||
| 705 | &["9D", "?"], | ||
| 706 | 16, | ||
| 707 | 9, | ||
| 708 | 10, | ||
| 709 | 10, | ||
| 710 | 990, | ||
| 711 | &["hit", "stand", "double"], | ||
| 712 | "playing", | ||
| 713 | 0, | ||
| 714 | ); | ||
| 715 | h.reply(&format!( | ||
| 716 | "BLACKJACK_STATE {}", | ||
| 717 | serde_json::to_string(&player).unwrap() | ||
| 718 | )); | ||
| 719 | |||
| 720 | assert_eq!(h.board().len(), 6); | ||
| 721 | assert!(h.board()[2].contains("Dealer: [9♦] [ ? ] 9")); | ||
| 722 | assert!(h.board()[3].contains("You: [10♠] [6♥] 16")); | ||
| 723 | assert!(h.board()[4].contains("Hit, stand or double.")); | ||
| 724 | assert!(h.board()[5].contains("[D]ouble")); | ||
| 725 | |||
| 726 | h.key(Key::Char('h')); | ||
| 727 | assert_eq!( | ||
| 728 | h.sent.last().map(String::as_str), | ||
| 729 | Some("BLACKJACK_ACTION bj1 hit") | ||
| 730 | ); | ||
| 731 | |||
| 732 | let done = bj_state( | ||
| 733 | "bj1", | ||
| 734 | "done", | ||
| 735 | &["10S", "6H", "5D"], | ||
| 736 | &["9D", "8C"], | ||
| 737 | 21, | ||
| 738 | 17, | ||
| 739 | 10, | ||
| 740 | 10, | ||
| 741 | 1010, | ||
| 742 | &[], | ||
| 743 | "win", | ||
| 744 | 20, | ||
| 745 | ); | ||
| 746 | h.reply(&format!( | ||
| 747 | "BLACKJACK_STATE {}", | ||
| 748 | serde_json::to_string(&done).unwrap() | ||
| 749 | )); | ||
| 750 | |||
| 751 | assert!(h.board()[4].contains("Win! Won 20 PLN")); | ||
| 752 | assert!(h.board()[5].contains("[SPACE] New hand")); | ||
| 753 | |||
| 754 | h.key(Key::Char('q')); | ||
| 755 | assert!(h.board().is_empty()); | ||
| 756 | assert!(h.screen.lines.iter().any(|line| line == "Game exited.")); | ||
| 757 | } | ||
| 758 | |||
| 759 | #[test] | ||
| 760 | fn blackjack_cancel_sends_a_cancel_action() { | ||
| 761 | let mut h = Harness::new(); | ||
| 762 | h.enter_blackjack(); | ||
| 763 | h.key(Key::Char(' ')); | ||
| 764 | |||
| 765 | let player = bj_state( | ||
| 766 | "bj1", | ||
| 767 | "player", | ||
| 768 | &["10S", "6H"], | ||
| 769 | &["9D", "?"], | ||
| 770 | 16, | ||
| 771 | 9, | ||
| 772 | 10, | ||
| 773 | 10, | ||
| 774 | 990, | ||
| 775 | &["hit", "stand", "double"], | ||
| 776 | "playing", | ||
| 777 | 0, | ||
| 778 | ); | ||
| 779 | h.reply(&format!( | ||
| 780 | "BLACKJACK_STATE {}", | ||
| 781 | serde_json::to_string(&player).unwrap() | ||
| 782 | )); | ||
| 783 | |||
| 784 | h.key(Key::Char('q')); | ||
| 785 | assert_eq!( | ||
| 786 | h.sent.last().map(String::as_str), | ||
| 787 | Some("BLACKJACK_ACTION bj1 cancel") | ||
| 788 | ); | ||
| 789 | |||
| 790 | let cancelled = bj_state( | ||
| 791 | "bj1", | ||
| 792 | "done", | ||
| 793 | &["10S", "6H"], | ||
| 794 | &["9D", "?"], | ||
| 795 | 16, | ||
| 796 | 9, | ||
| 797 | 10, | ||
| 798 | 10, | ||
| 799 | 1000, | ||
| 800 | &[], | ||
| 801 | "cancelled", | ||
| 802 | 0, | ||
| 803 | ); | ||
| 804 | h.reply(&format!( | ||
| 805 | "BLACKJACK_STATE {}", | ||
| 806 | serde_json::to_string(&cancelled).unwrap() | ||
| 807 | )); | ||
| 808 | |||
| 809 | assert!(h.board().is_empty()); | ||
| 810 | assert!(h.screen.lines.iter().any(|line| line == "Game exited.")); | ||
| 811 | } | ||
| 812 | |||
| 813 | #[test] | ||
| 814 | fn blackjack_ready_board_can_change_the_next_bet() { | ||
| 815 | let mut h = Harness::new(); | ||
| 816 | h.enter_blackjack(); | ||
| 817 | |||
| 818 | h.key(Key::Char('=')); | ||
| 819 | assert!(h.board()[1].contains("Bet: 20 PLN")); | ||
| 820 | |||
| 821 | h.key(Key::Char(' ')); | ||
| 822 | assert!(h | ||
| 823 | .sent | ||
| 824 | .iter() | ||
| 825 | .any(|line| line == "PLAY_START blackjack 20")); | ||
| 826 | } | ||
| 827 | |||
| 828 | #[test] | ||
| 829 | fn parses_a_blackjack_state() { | ||
| 830 | let state = bj_state( | ||
| 831 | "bj1", | ||
| 832 | "player", | ||
| 833 | &["10S", "6H"], | ||
| 834 | &["9D", "?"], | ||
| 835 | 16, | ||
| 836 | 9, | ||
| 837 | 10, | ||
| 838 | 10, | ||
| 839 | 990, | ||
| 840 | &["hit", "stand"], | ||
| 841 | "playing", | ||
| 842 | 0, | ||
| 843 | ); | ||
| 844 | let response = format!("BLACKJACK_STATE {}", serde_json::to_string(&state).unwrap()); | ||
| 845 | |||
| 846 | assert_eq!(parse_blackjack_response(&response).unwrap(), state); | ||
| 847 | assert!(parse_blackjack_response("ERROR Insufficient balance").is_err()); | ||
| 848 | } | ||
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 @@ | |||
| 1 | use std::path::PathBuf; | 1 | use std::path::PathBuf; |
| 2 | 2 | ||
| 3 | use rusqlite::Connection; | 3 | use rusqlite::Connection; |
| 4 | use zagan_bet::blackjack::BlackjackState; | ||
| 4 | use zagan_bet::server::core::{ConnectionState, ServerCore}; | 5 | use zagan_bet::server::core::{ConnectionState, ServerCore}; |
| 5 | use zagan_bet::server::database; | 6 | use zagan_bet::server::database; |
| 6 | use zagan_bet::server::games::registry::GameRegistry; | 7 | use zagan_bet::server::games::registry::GameRegistry; |
| @@ -47,6 +48,31 @@ fn setup_roulette(name: &str, number: u8) -> (ServerCore, String) { | |||
| 47 | (ServerCore::with_registry(conn, registry, 4), path) | 48 | (ServerCore::with_registry(conn, registry, 4), path) |
| 48 | } | 49 | } |
| 49 | 50 | ||
| 51 | fn setup_blackjack(name: &str, deck: &[&str]) -> (ServerCore, String) { | ||
| 52 | let path = PathBuf::from(env!("CARGO_TARGET_TMPDIR")) | ||
| 53 | .join(format!("server-core-{name}.db")) | ||
| 54 | .to_string_lossy() | ||
| 55 | .into_owned(); | ||
| 56 | let _ = std::fs::remove_file(&path); | ||
| 57 | |||
| 58 | let conn = Connection::open(&path).unwrap(); | ||
| 59 | database::init_db(&conn); | ||
| 60 | |||
| 61 | let deck = deck.iter().map(|card| card.to_string()).collect(); | ||
| 62 | ( | ||
| 63 | ServerCore::with_registry(conn, GameRegistry::with_defaults(), 4) | ||
| 64 | .with_blackjack_deck(deck), | ||
| 65 | path, | ||
| 66 | ) | ||
| 67 | } | ||
| 68 | |||
| 69 | fn blackjack_state(response: &str) -> BlackjackState { | ||
| 70 | let raw = response | ||
| 71 | .strip_prefix("BLACKJACK_STATE ") | ||
| 72 | .expect("blackjack state response"); | ||
| 73 | serde_json::from_str(raw).expect("valid blackjack state") | ||
| 74 | } | ||
| 75 | |||
| 50 | fn register(core: &mut ServerCore, state: &mut ConnectionState, user: &str) { | 76 | fn register(core: &mut ServerCore, state: &mut ConnectionState, user: &str) { |
| 51 | assert_eq!( | 77 | assert_eq!( |
| 52 | core.handle_message(state, &format!("REGISTER {user} credential")), | 78 | core.handle_message(state, &format!("REGISTER {user} credential")), |
| @@ -337,3 +363,136 @@ fn unknown_games_are_rejected_before_a_session_is_created() { | |||
| 337 | ); | 363 | ); |
| 338 | assert_eq!(core.session_count(), 0); | 364 | assert_eq!(core.session_count(), 0); |
| 339 | } | 365 | } |
| 366 | |||
| 367 | #[test] | ||
| 368 | fn blackjack_reserves_the_bet_and_settles_a_hit_hand() { | ||
| 369 | let (mut core, path) = setup_blackjack( | ||
| 370 | "blackjack-hit", | ||
| 371 | &["10S", "9D", "6H", "8C", "5D"], | ||
| 372 | ); | ||
| 373 | let mut state = ConnectionState::default(); | ||
| 374 | register(&mut core, &mut state, "alice"); | ||
| 375 | login(&mut core, &mut state, "alice"); | ||
| 376 | |||
| 377 | let response = core.handle_message(&mut state, "PLAY_START blackjack 10"); | ||
| 378 | let current = blackjack_state(&response); | ||
| 379 | |||
| 380 | assert_eq!(current.phase, "player"); | ||
| 381 | assert_eq!(current.player_total, 16); | ||
| 382 | assert_eq!(current.dealer, vec!["9D".to_string(), "?".to_string()]); | ||
| 383 | assert_eq!(current.balance, 990); | ||
| 384 | assert_eq!(current.actions, vec!["hit", "stand", "double"]); | ||
| 385 | assert_eq!(core.session_count(), 1); | ||
| 386 | |||
| 387 | let response = core.handle_message( | ||
| 388 | &mut state, | ||
| 389 | &format!("BLACKJACK_ACTION {} hit", current.session_id), | ||
| 390 | ); | ||
| 391 | let done = blackjack_state(&response); | ||
| 392 | |||
| 393 | assert_eq!(done.phase, "done"); | ||
| 394 | assert_eq!(done.result, "win"); | ||
| 395 | assert_eq!(done.winnings, 20); | ||
| 396 | assert_eq!(done.balance, 1010); | ||
| 397 | assert_eq!(core.balance("alice"), Some(1010)); | ||
| 398 | assert_eq!(core.play_count("alice"), 1); | ||
| 399 | assert_eq!(core.session_count(), 0); | ||
| 400 | |||
| 401 | let conn = Connection::open(&path).unwrap(); | ||
| 402 | let (game_type, details): (String, String) = conn | ||
| 403 | .query_row( | ||
| 404 | "SELECT game_type, details FROM plays WHERE username = 'alice'", | ||
| 405 | [], | ||
| 406 | |row| Ok((row.get(0)?, row.get(1)?)), | ||
| 407 | ) | ||
| 408 | .unwrap(); | ||
| 409 | assert_eq!(game_type, "blackjack"); | ||
| 410 | let details: BlackjackState = serde_json::from_str(&details).unwrap(); | ||
| 411 | assert_eq!(details.result, "win"); | ||
| 412 | assert_eq!(details.winnings, 20); | ||
| 413 | } | ||
| 414 | |||
| 415 | #[test] | ||
| 416 | fn blackjack_double_reserves_an_extra_bet() { | ||
| 417 | let (mut core, _path) = setup_blackjack( | ||
| 418 | "blackjack-double", | ||
| 419 | &["5S", "9D", "5H", "8C", "10D"], | ||
| 420 | ); | ||
| 421 | let mut state = ConnectionState::default(); | ||
| 422 | register(&mut core, &mut state, "alice"); | ||
| 423 | login(&mut core, &mut state, "alice"); | ||
| 424 | |||
| 425 | let current = blackjack_state(&core.handle_message(&mut state, "PLAY_START blackjack 10")); | ||
| 426 | assert_eq!(current.balance, 990); | ||
| 427 | |||
| 428 | let done = blackjack_state(&core.handle_message( | ||
| 429 | &mut state, | ||
| 430 | &format!("BLACKJACK_ACTION {} double", current.session_id), | ||
| 431 | )); | ||
| 432 | |||
| 433 | assert_eq!(done.phase, "done"); | ||
| 434 | assert_eq!(done.bet, 20); | ||
| 435 | assert_eq!(done.winnings, 40); | ||
| 436 | assert_eq!(done.balance, 1020); | ||
| 437 | assert_eq!(core.balance("alice"), Some(1020)); | ||
| 438 | assert_eq!(core.play_count("alice"), 1); | ||
| 439 | } | ||
| 440 | |||
| 441 | #[test] | ||
| 442 | fn blackjack_natural_settles_immediately() { | ||
| 443 | let (mut core, _path) = | ||
| 444 | setup_blackjack("blackjack-natural", &["AS", "9D", "KH", "8C"]); | ||
| 445 | let mut state = ConnectionState::default(); | ||
| 446 | register(&mut core, &mut state, "alice"); | ||
| 447 | login(&mut core, &mut state, "alice"); | ||
| 448 | |||
| 449 | let done = blackjack_state(&core.handle_message(&mut state, "PLAY_START blackjack 10")); | ||
| 450 | |||
| 451 | assert_eq!(done.phase, "done"); | ||
| 452 | assert_eq!(done.result, "blackjack"); | ||
| 453 | assert_eq!(done.winnings, 25); | ||
| 454 | assert_eq!(done.balance, 1015); | ||
| 455 | assert_eq!(core.session_count(), 0); | ||
| 456 | assert_eq!(core.play_count("alice"), 1); | ||
| 457 | } | ||
| 458 | |||
| 459 | #[test] | ||
| 460 | fn blackjack_cancel_refunds_the_reserved_bet() { | ||
| 461 | let (mut core, _path) = | ||
| 462 | setup_blackjack("blackjack-cancel", &["10S", "9D", "6H", "8C"]); | ||
| 463 | let mut state = ConnectionState::default(); | ||
| 464 | register(&mut core, &mut state, "alice"); | ||
| 465 | login(&mut core, &mut state, "alice"); | ||
| 466 | |||
| 467 | let current = blackjack_state(&core.handle_message(&mut state, "PLAY_START blackjack 10")); | ||
| 468 | assert_eq!(current.balance, 990); | ||
| 469 | |||
| 470 | let cancelled = blackjack_state(&core.handle_message( | ||
| 471 | &mut state, | ||
| 472 | &format!("BLACKJACK_ACTION {} cancel", current.session_id), | ||
| 473 | )); | ||
| 474 | |||
| 475 | assert_eq!(cancelled.result, "cancelled"); | ||
| 476 | assert_eq!(cancelled.balance, 1000); | ||
| 477 | assert_eq!(core.balance("alice"), Some(1000)); | ||
| 478 | assert_eq!(core.play_count("alice"), 0); | ||
| 479 | assert_eq!(core.session_count(), 0); | ||
| 480 | } | ||
| 481 | |||
| 482 | #[test] | ||
| 483 | fn blackjack_disconnect_refunds_the_reserved_bet() { | ||
| 484 | let (mut core, _path) = | ||
| 485 | setup_blackjack("blackjack-disconnect", &["10S", "9D", "6H", "8C"]); | ||
| 486 | let mut state = ConnectionState::default(); | ||
| 487 | register(&mut core, &mut state, "alice"); | ||
| 488 | login(&mut core, &mut state, "alice"); | ||
| 489 | |||
| 490 | let current = blackjack_state(&core.handle_message(&mut state, "PLAY_START blackjack 10")); | ||
| 491 | assert_eq!(current.balance, 990); | ||
| 492 | |||
| 493 | core.disconnect(&state); | ||
| 494 | |||
| 495 | assert_eq!(core.balance("alice"), Some(1000)); | ||
| 496 | assert_eq!(core.play_count("alice"), 0); | ||
| 497 | assert_eq!(core.session_count(), 0); | ||
| 498 | } | ||
