use chironaut::Game;
use chironaut::rules::GameRules;
use chironaut::action::Action;
use chironaut::game::GameStage;
fn main() {
println!("=== POKER ENGINE ERROR HANDLING TEST ===\n");
println!("Running error handling tests...\n");
let mut failed_tests = Vec::new();
run_test("Invalid bet below minimum", test_bet_below_minimum, &mut failed_tests);
run_test("Invalid bet above maximum", test_bet_above_maximum, &mut failed_tests);
run_test("Check when facing bet", test_check_facing_bet, &mut failed_tests);
run_test("Fold when already folded", test_fold_when_folded, &mut failed_tests);
run_test("Bet with insufficient chips", test_bet_insufficient_chips, &mut failed_tests);
run_test("Act when all-in", test_act_when_all_in, &mut failed_tests);
run_test("Invalid straddle amount", test_invalid_straddle_amount, &mut failed_tests);
run_test("Straddle from wrong position", test_straddle_wrong_position, &mut failed_tests);
run_test("Straddle with insufficient chips", test_straddle_insufficient_chips, &mut failed_tests);
run_test("Straddle in game without straddles", test_straddle_when_not_allowed, &mut failed_tests);
run_test("Action after showdown", test_action_after_showdown, &mut failed_tests);
run_test("Start hand with no players", test_start_hand_no_players, &mut failed_tests);
run_test("Start hand with one player", test_start_hand_one_player, &mut failed_tests);
if failed_tests.is_empty() {
println!("\n✅ All error handling tests passed successfully!");
} else {
println!("\n❌ {} tests failed:", failed_tests.len());
for test in &failed_tests {
println!(" - {}", test);
}
}
}
fn run_test<F>(test_name: &str, test_fn: F, failed_tests: &mut Vec<String>)
where
F: FnOnce() -> Result<(), String>,
{
println!("\n--- Running test: {} ---", test_name);
match test_fn() {
Ok(_) => println!("✅ Test passed: {}", test_name),
Err(e) => {
println!("❌ Test failed: {}", test_name);
println!(" Error: {}", e);
failed_tests.push(test_name.to_string());
}
}
}
fn setup_standard_game() -> Game {
let rules = GameRules::nlhe(10, 20);
let mut game = Game::new(rules);
for i in 0..4 {
game.add_player(format!("Player {}", i+1), 1000).unwrap();
}
game.start_hand().unwrap();
game
}
fn setup_straddle_game() -> Game {
let rules = GameRules::nlhe(10, 20).with_straddles();
let mut game = Game::new(rules);
for i in 0..4 {
game.add_player(format!("Player {}", i+1), 1000).unwrap();
}
game.start_hand().unwrap();
game
}
fn test_bet_below_minimum() -> Result<(), String> {
let mut game = setup_standard_game();
let result = game.handle_action(Action::Bet(5));
assert!(result.is_err(), "Should reject a bet below minimum");
if let Err(e) = result {
assert!(e.contains("Bet must be at least the big blind"),
"Expected error about minimum bet, got: {}", e);
} else {
return Err("Expected error for bet below minimum".to_string());
}
Ok(())
}
fn test_bet_above_maximum() -> Result<(), String> {
let mut game = setup_standard_game();
let current_pos = game.current_position;
let player_chips = game.players[current_pos].chips;
let result = game.handle_action(Action::Bet(player_chips + 100));
if result.is_ok() {
assert_eq!(game.players[current_pos].chips, 0, "Player should have no chips left");
assert!(game.players[current_pos].is_all_in, "Player should be all-in");
} else {
if let Err(e) = result {
assert!(e.contains("insufficient") || e.contains("enough chips"),
"Expected error about insufficient chips, got: {}", e);
}
}
Ok(())
}
fn test_check_facing_bet() -> Result<(), String> {
let mut game = setup_standard_game();
game.handle_action(Action::Bet(20)).unwrap();
let result = game.handle_action(Action::Bet(0));
assert!(result.is_err(), "Should reject checking when facing a bet");
if let Err(e) = result {
assert!(e.contains("Cannot check when there is a bet"),
"Expected error about checking when facing bet, got: {}", e);
} else {
return Err("Expected error for checking when facing bet".to_string());
}
Ok(())
}
fn test_fold_when_folded() -> Result<(), String> {
let mut game = setup_standard_game();
game.handle_action(Action::Fold).unwrap();
let folded_player_idx = 0;
assert!(game.players[folded_player_idx].is_folded, "Player should be folded");
assert_ne!(game.current_position, folded_player_idx,
"Current position should skip folded player");
Ok(())
}
fn test_bet_insufficient_chips() -> Result<(), String> {
let rules = GameRules::nlhe(10, 20);
let mut game = Game::new(rules);
game.add_player("Player 1".to_string(), 1000).unwrap();
game.add_player("Player 2".to_string(), 1000).unwrap();
game.add_player("Player 3".to_string(), 30).unwrap(); game.add_player("Player 4".to_string(), 1000).unwrap();
game.start_hand().unwrap();
let short_stack_pos = 2;
while game.current_position != short_stack_pos {
game.handle_action(Action::Bet(20)).unwrap(); }
let result = game.handle_action(Action::Bet(50));
if result.is_ok() {
assert_eq!(game.players[short_stack_pos].chips, 0, "Player should have no chips left");
assert!(game.players[short_stack_pos].is_all_in, "Player should be all-in");
} else {
if let Err(e) = result {
assert!(e.contains("insufficient") || e.contains("enough chips"),
"Expected error about insufficient chips, got: {}", e);
}
}
Ok(())
}
fn test_act_when_all_in() -> Result<(), String> {
let mut game = setup_standard_game();
let pos = game.current_position;
game.handle_action(Action::Bet(game.players[pos].chips)).unwrap();
assert!(game.players[pos].is_all_in, "Player should be all-in");
while game.stage == GameStage::Preflop {
let current_pos = game.current_position;
if game.current_bet > game.players[current_pos].bet_amount {
game.handle_action(Action::Bet(game.current_bet - game.players[current_pos].bet_amount)).unwrap();
} else {
game.handle_action(Action::Bet(0)).unwrap();
}
}
assert_ne!(game.current_position, pos, "All-in player should be skipped in position");
Ok(())
}
fn test_invalid_straddle_amount() -> Result<(), String> {
let mut game = setup_straddle_game();
let utg_pos = (game.get_big_blind_position() + 1) % game.players.len();
let result = game.post_straddle(utg_pos, 30);
assert!(result.is_err(), "Should reject straddle smaller than 2x BB");
if let Err(e) = result {
assert!(e.contains("must be at least 2x"),
"Expected error about straddle size, got: {}", e);
} else {
return Err("Expected error for invalid straddle amount".to_string());
}
Ok(())
}
fn test_straddle_wrong_position() -> Result<(), String> {
let mut game = setup_straddle_game();
let utg_pos = (game.get_big_blind_position() + 1) % game.players.len();
let wrong_pos = (utg_pos + 1) % game.players.len();
let result = game.post_straddle(wrong_pos, 40);
assert!(result.is_err(), "Should reject straddle from wrong position");
if let Err(e) = result {
assert!(e.contains("must be from the player after the big blind"),
"Expected error about straddle position, got: {}", e);
} else {
return Err("Expected error for straddle from wrong position".to_string());
}
Ok(())
}
fn test_straddle_insufficient_chips() -> Result<(), String> {
let rules = GameRules::nlhe(10, 20).with_straddles();
let mut game = Game::new(rules);
for i in 0..4 {
let chips = if i == 2 { 30 } else { 1000 }; game.add_player(format!("Player {}", i+1), chips).unwrap();
}
game.start_hand().unwrap();
let utg_pos = (game.get_big_blind_position() + 1) % game.players.len();
let result = game.post_straddle(utg_pos, 50);
if result.is_ok() {
assert_eq!(game.players[utg_pos].chips, 0, "Player should have no chips left");
assert!(game.players[utg_pos].is_all_in, "Player should be all-in");
} else {
if let Err(e) = result {
assert!(e.contains("doesn't have enough chips"),
"Expected error about insufficient chips, got: {}", e);
}
}
Ok(())
}
fn test_straddle_when_not_allowed() -> Result<(), String> {
let mut game = setup_standard_game();
let utg_pos = (game.get_big_blind_position() + 1) % game.players.len();
let result = game.post_straddle(utg_pos, 40);
assert!(result.is_err(), "Should reject straddle when not allowed");
if let Err(e) = result {
assert!(e.contains("not allowed"),
"Expected error about straddles not allowed, got: {}", e);
} else {
return Err("Expected error for straddle when not allowed".to_string());
}
Ok(())
}
fn test_action_after_showdown() -> Result<(), String> {
let mut game = setup_standard_game();
while game.stage != GameStage::Showdown {
let curr_pos = game.current_position;
if game.current_bet > game.players[curr_pos].bet_amount {
game.handle_action(Action::Bet(game.current_bet - game.players[curr_pos].bet_amount)).unwrap();
} else {
game.handle_action(Action::Bet(0)).unwrap();
}
}
assert_eq!(game.stage, GameStage::Showdown, "Game should be at showdown");
let result = game.handle_action(Action::Bet(0));
assert!(result.is_err(), "Should reject action after showdown");
if let Err(e) = result {
assert!(e.contains("already at showdown"),
"Expected error about hand being at showdown, got: {}", e);
} else {
return Err("Expected error for action after showdown".to_string());
}
Ok(())
}
fn test_start_hand_no_players() -> Result<(), String> {
let rules = GameRules::nlhe(10, 20);
let mut game = Game::new(rules);
let result = game.start_hand();
assert!(result.is_err(), "Should reject starting hand with no players");
if let Err(e) = result {
assert!(e.contains("enough players"),
"Expected error about not enough players, got: {}", e);
} else {
return Err("Expected error for starting hand with no players".to_string());
}
Ok(())
}
fn test_start_hand_one_player() -> Result<(), String> {
let rules = GameRules::nlhe(10, 20);
let mut game = Game::new(rules);
game.add_player("Player 1".to_string(), 1000).unwrap();
let result = game.start_hand();
assert!(result.is_err(), "Should reject starting hand with only one player");
if let Err(e) = result {
assert!(e.contains("enough players"),
"Expected error about not enough players, got: {}", e);
} else {
return Err("Expected error for starting hand with only one player".to_string());
}
Ok(())
}