//! Integration tests for poker data structures //! //! These tests verify that the holdem_core poker structures work together correctly, //! including serialization round-trips and performance benchmarks. use holdem_core::{board, card, deck, hand, hole_cards}; use std::time::Instant; #[test] fn test_integration_hand_from_hole_cards_and_board() { // Test creating hands from various hole card and board combinations // Test with preflop (no board cards) let hole_cards = hole_cards::HoleCards::new( card::Card::new(12, 0).unwrap(), // As card::Card::new(11, 1).unwrap(), // Kh ) .unwrap(); let empty_board = board::Board::new(); let preflop_hand = hand::Hand::from_hole_cards_and_board(&hole_cards, &empty_board).unwrap(); assert_eq!(preflop_hand.len, 2); assert_eq!(preflop_hand.cards()[0], card::Card::new(12, 0).unwrap()); assert_eq!(preflop_hand.cards()[1], card::Card::new(11, 1).unwrap()); // Test with flop let mut flop_board = board::Board::new(); flop_board .deal_flop(vec![ card::Card::new(10, 2).unwrap(), // Qd card::Card::new(9, 3).unwrap(), // Js card::Card::new(8, 0).unwrap(), // Tc ]) .unwrap(); let flop_hand = hand::Hand::from_hole_cards_and_board(&hole_cards, &flop_board).unwrap(); assert_eq!(flop_hand.len, 5); assert_eq!(flop_hand.cards()[0], card::Card::new(12, 0).unwrap()); // As assert_eq!(flop_hand.cards()[1], card::Card::new(11, 1).unwrap()); // Kh assert_eq!(flop_hand.cards()[2], card::Card::new(10, 2).unwrap()); // Qd assert_eq!(flop_hand.cards()[3], card::Card::new(9, 3).unwrap()); // Js assert_eq!(flop_hand.cards()[4], card::Card::new(8, 0).unwrap()); // Tc // Test with turn let mut turn_board = flop_board.clone(); turn_board .deal_turn(card::Card::new(7, 1).unwrap()) .unwrap(); // 9h let turn_hand = hand::Hand::from_hole_cards_and_board(&hole_cards, &turn_board).unwrap(); assert_eq!(turn_hand.len, 6); // Test with river let mut river_board = turn_board.clone(); river_board .deal_river(card::Card::new(6, 2).unwrap()) .unwrap(); // 8d let river_hand = hand::Hand::from_hole_cards_and_board(&hole_cards, &river_board).unwrap(); assert_eq!(river_hand.len, 7); } #[test] fn test_integration_hand_from_hole_cards_and_board_edge_cases() { // Test edge cases for hand creation from hole cards and board // Test with pocket aces and board let pocket_aces = hole_cards::HoleCards::new( card::Card::new(12, 0).unwrap(), // As card::Card::new(12, 1).unwrap(), // Ah ) .unwrap(); let mut board = board::Board::new(); board .deal_flop(vec![ card::Card::new(10, 2).unwrap(), // Qd card::Card::new(9, 3).unwrap(), // Js card::Card::new(8, 0).unwrap(), // Tc ]) .unwrap(); let hand = hand::Hand::from_hole_cards_and_board(&pocket_aces, &board).unwrap(); assert_eq!(hand.len, 5); // Test with suited connectors let suited_connectors = hole_cards::HoleCards::new( card::Card::new(11, 0).unwrap(), // Ks card::Card::new(10, 0).unwrap(), // Qs ) .unwrap(); let hand = hand::Hand::from_hole_cards_and_board(&suited_connectors, &board).unwrap(); assert_eq!(hand.len, 5); // Test with empty board (should work) let empty_board = board::Board::new(); let hand = hand::Hand::from_hole_cards_and_board(&pocket_aces, &empty_board).unwrap(); assert_eq!(hand.len, 2); } #[test] fn test_integration_serialization_round_trips() { // Test that all structures can be serialized and deserialized correctly // Test Card serialization round-trip let original_card = card::Card::new(12, 3).unwrap(); // Ace of Spades let json = serde_json::to_string(&original_card).unwrap(); let deserialized_card: card::Card = serde_json::from_str(&json).unwrap(); assert_eq!(original_card, deserialized_card); // Test HoleCards serialization round-trip let original_hole_cards = hole_cards::HoleCards::new( card::Card::new(12, 0).unwrap(), card::Card::new(11, 1).unwrap(), ) .unwrap(); let json = serde_json::to_string(&original_hole_cards).unwrap(); let deserialized_hole_cards: hole_cards::HoleCards = serde_json::from_str(&json).unwrap(); assert_eq!(original_hole_cards, deserialized_hole_cards); // Test Board serialization round-trip let mut original_board = board::Board::new(); original_board .deal_flop(vec![ card::Card::new(10, 2).unwrap(), card::Card::new(9, 3).unwrap(), card::Card::new(8, 0).unwrap(), ]) .unwrap(); original_board .deal_turn(card::Card::new(7, 1).unwrap()) .unwrap(); original_board .deal_river(card::Card::new(6, 2).unwrap()) .unwrap(); let json = serde_json::to_string(&original_board).unwrap(); let deserialized_board: board::Board = serde_json::from_str(&json).unwrap(); assert_eq!(original_board, deserialized_board); // Test Hand serialization round-trip let original_hand = hand::Hand::from_hole_cards_and_board(&original_hole_cards, &original_board).unwrap(); let json = serde_json::to_string(&original_hand).unwrap(); let deserialized_hand: hand::Hand = serde_json::from_str(&json).unwrap(); assert_eq!(original_hand, deserialized_hand); // Test Deck serialization round-trip let original_deck = deck::Deck::new(); let json = serde_json::to_string(&original_deck).unwrap(); let deserialized_deck: deck::Deck = serde_json::from_str(&json).unwrap(); assert_eq!(original_deck.cards(), deserialized_deck.cards()); } #[test] fn test_integration_serialization_round_trips_comprehensive() { // Test serialization round-trips with various data formats and edge cases // Test with TOML serialization let card = card::Card::new(12, 3).unwrap(); let toml = toml::to_string(&card).unwrap(); let deserialized: card::Card = toml::from_str(&toml).unwrap(); assert_eq!(card, deserialized); // Test complex hand serialization let hole_cards = hole_cards::HoleCards::new( card::Card::new(12, 0).unwrap(), card::Card::new(11, 1).unwrap(), ) .unwrap(); let mut board = board::Board::new(); board .deal_flop(vec![ card::Card::new(10, 2).unwrap(), card::Card::new(9, 3).unwrap(), card::Card::new(8, 0).unwrap(), ]) .unwrap(); let hand = hand::Hand::from_hole_cards_and_board(&hole_cards, &board).unwrap(); // JSON round-trip let json = serde_json::to_string(&hand).unwrap(); let deserialized: hand::Hand = serde_json::from_str(&json).unwrap(); assert_eq!(hand, deserialized); // TOML round-trip let toml = toml::to_string(&hand).unwrap(); let deserialized: hand::Hand = toml::from_str(&toml).unwrap(); assert_eq!(hand, deserialized); // Test empty structures let empty_hand = hand::Hand::new(vec![]).unwrap(); let json = serde_json::to_string(&empty_hand).unwrap(); let deserialized: hand::Hand = serde_json::from_str(&json).unwrap(); assert_eq!(empty_hand, deserialized); } #[test] fn test_integration_performance_benchmarks() { // Performance benchmarks for common operations let iterations = 1000; // Benchmark hand creation from hole cards and board let hole_cards = hole_cards::HoleCards::new( card::Card::new(12, 0).unwrap(), card::Card::new(11, 1).unwrap(), ) .unwrap(); let mut board = board::Board::new(); board .deal_flop(vec![ card::Card::new(10, 2).unwrap(), card::Card::new(9, 3).unwrap(), card::Card::new(8, 0).unwrap(), ]) .unwrap(); let start = Instant::now(); for _ in 0..iterations { let _hand = hand::Hand::from_hole_cards_and_board(&hole_cards, &board).unwrap(); } let duration = start.elapsed(); println!("Hand creation ({} iterations): {:?}", iterations, duration); assert!( duration.as_millis() < 100, "Hand creation too slow: {:?}", duration ); // Benchmark serialization round-trips let hand = hand::Hand::from_hole_cards_and_board(&hole_cards, &board).unwrap(); let start = Instant::now(); for _ in 0..iterations { let json = serde_json::to_string(&hand).unwrap(); let _: hand::Hand = serde_json::from_str(&json).unwrap(); } let duration = start.elapsed(); println!( "Serialization round-trip ({} iterations): {:?}", iterations, duration ); assert!( duration.as_millis() < 500, "Serialization too slow: {:?}", duration ); // Benchmark card creation and operations let start = Instant::now(); let mut cards = Vec::new(); for i in 0..iterations { cards.push(card::Card::new((i % 13) as u8, (i % 4) as u8)); } let duration = start.elapsed(); println!("Card creation ({} iterations): {:?}", iterations, duration); assert!( duration.as_millis() < 50, "Card creation too slow: {:?}", duration ); // Benchmark deck operations let start = Instant::now(); for _ in 0..(iterations / 10) { // Fewer iterations for deck operations let mut deck = deck::Deck::new(); let _ = deck.shuffle(&mut rand::rng()); let _ = deck.deal(5); } let duration = start.elapsed(); println!( "Deck shuffle and deal ({} iterations): {:?}", iterations / 10, duration ); assert!( duration.as_millis() < 200, "Deck operations too slow: {:?}", duration ); } #[test] fn test_integration_complex_scenarios() { // Test complex real-world scenarios // Test multiple hands from same board let mut board = board::Board::new(); board .deal_flop(vec![ card::Card::new(12, 2).unwrap(), // Ad card::Card::new(11, 3).unwrap(), // Ks card::Card::new(10, 0).unwrap(), // Qc ]) .unwrap(); board.deal_turn(card::Card::new(9, 1).unwrap()).unwrap(); // Jh board.deal_river(card::Card::new(8, 2).unwrap()).unwrap(); // Td let hole_cards1 = hole_cards::HoleCards::new( card::Card::new(12, 0).unwrap(), // As card::Card::new(12, 1).unwrap(), // Ah ) .unwrap(); // Pocket aces let hole_cards2 = hole_cards::HoleCards::new( card::Card::new(11, 0).unwrap(), // Kc card::Card::new(11, 1).unwrap(), // Kh ) .unwrap(); // Pocket kings let hand1 = hand::Hand::from_hole_cards_and_board(&hole_cards1, &board).unwrap(); let hand2 = hand::Hand::from_hole_cards_and_board(&hole_cards2, &board).unwrap(); assert_eq!(hand1.len, 7); assert_eq!(hand2.len, 7); // Both hands should contain all board cards plus their hole cards assert!(hand1.cards().contains(&card::Card::new(12, 0).unwrap())); // As in hand1 assert!(hand1.cards().contains(&card::Card::new(12, 1).unwrap())); // Ah in hand1 assert!(hand2.cards().contains(&card::Card::new(11, 0).unwrap())); // Kc in hand2 assert!(hand2.cards().contains(&card::Card::new(11, 1).unwrap())); // Kh in hand2 // Both hands should contain all board cards for card in board.visible_cards() { assert!(hand1.cards().contains(card)); assert!(hand2.cards().contains(card)); } } #[test] fn test_integration_error_handling() { // Test error handling in integration scenarios // Test duplicate cards between hole cards and board let hole_cards = hole_cards::HoleCards::new( card::Card::new(12, 0).unwrap(), // As card::Card::new(11, 1).unwrap(), // Kh ) .unwrap(); let mut board = board::Board::new(); board .deal_flop(vec![ card::Card::new(12, 0).unwrap(), // Duplicate As card::Card::new(9, 3).unwrap(), card::Card::new(8, 0).unwrap(), ]) .unwrap(); // This should fail due to duplicate cards assert!(hand::Hand::from_hole_cards_and_board(&hole_cards, &board).is_err()); // Test serialization of invalid states (should still work) let invalid_hand = hand::Hand::new(vec![ card::Card::new(12, 0).unwrap(), card::Card::new(12, 0).unwrap(), // Duplicate ]); assert!(invalid_hand.is_err()); } #[test] fn test_integration_data_consistency() { // Test that data remains consistent across operations // Create a complex scenario and verify all data stays consistent let hole_cards = hole_cards::HoleCards::new( card::Card::new(12, 0).unwrap(), card::Card::new(11, 1).unwrap(), ) .unwrap(); let mut board = board::Board::new(); let flop_cards = vec![ card::Card::new(10, 2).unwrap(), card::Card::new(9, 3).unwrap(), card::Card::new(8, 0).unwrap(), ]; board.deal_flop(flop_cards.clone()).unwrap(); let hand = hand::Hand::from_hole_cards_and_board(&hole_cards, &board).unwrap(); // Verify hand contains exactly the expected cards let expected_cards = vec![ card::Card::new(12, 0).unwrap(), // hole card::Card::new(11, 1).unwrap(), // hole card::Card::new(10, 2).unwrap(), // flop card::Card::new(9, 3).unwrap(), // flop card::Card::new(8, 0).unwrap(), // flop ]; assert_eq!(hand.len, 5); for expected in expected_cards { assert!(hand.cards().contains(&expected)); } // Test serialization preserves data let json = serde_json::to_string(&hand).unwrap(); let deserialized: hand::Hand = serde_json::from_str(&json).unwrap(); assert_eq!(hand, deserialized); assert_eq!(hand.len, deserialized.len); for i in 0..hand.len { assert_eq!(hand.cards()[i], deserialized.cards()[i]); } } // # Integration Tests for Math Evaluator // // This module provides integration tests for the advanced evaluator system, // ensuring compatibility and correct interaction with the holdem_core evaluator. use holdem_core::evaluator::integration::MathEvaluator; use holdem_core::Card; use std::str::FromStr; /// Integration test suite for the math evaluator pub struct IntegrationTestSuite { /// Math evaluator instance math_evaluator: MathEvaluator, /// Holdem_core evaluator instance for comparison core_evaluator: std::sync::Arc, } /// Test results for integration testing #[derive(Debug, Clone)] pub struct IntegrationTestResults { /// Total number of tests run pub total_tests: usize, /// Number of passed tests pub passed_tests: usize, /// Number of failed tests pub failed_tests: usize, /// Test execution time in microseconds pub execution_time_us: u64, } impl IntegrationTestSuite { /// Create a new integration test suite pub fn new() -> Result { Ok(Self { math_evaluator: MathEvaluator::new()?, core_evaluator: holdem_core::evaluator::Evaluator::instance().clone(), }) } /// Run all integration tests pub fn run_all_tests( &mut self, ) -> Result { let start_time = std::time::Instant::now(); println!("Running integration tests for math evaluator..."); let mut total_tests = 0; let mut passed_tests = 0; let mut failed_tests = 0; // Test 5-card evaluation compatibility let (passed, failed) = self.test_5_card_compatibility()?; total_tests += passed + failed; passed_tests += passed; failed_tests += failed; // Test 6-card evaluation compatibility let (passed, failed) = self.test_6_card_compatibility()?; total_tests += passed + failed; passed_tests += passed; failed_tests += failed; // Test 7-card evaluation compatibility let (passed, failed) = self.test_7_card_compatibility()?; total_tests += passed + failed; passed_tests += passed; failed_tests += failed; // Test type conversion compatibility let (passed, failed) = self.test_type_conversion_compatibility()?; total_tests += passed + failed; passed_tests += passed; failed_tests += failed; let elapsed = start_time.elapsed(); let results = IntegrationTestResults { total_tests, passed_tests, failed_tests, execution_time_us: elapsed.as_micros() as u64, }; println!("Integration tests completed: {:?}", results); Ok(results) } /// Test 5-card evaluation compatibility between math and core evaluators fn test_5_card_compatibility( &mut self, ) -> Result<(usize, usize), holdem_core::evaluator::errors::EvaluatorError> { println!("Testing 5-card evaluation compatibility..."); let test_hands = generate_test_hands(); let mut passed = 0; let mut failed = 0; for (i, cards) in test_hands.iter().enumerate() { if cards.len() >= 5 { let math_result = self .math_evaluator .evaluate_5_card(&cards[..5].try_into().unwrap()); let core_result = self .core_evaluator .evaluate_5_card(&cards[..5].try_into().unwrap()); if math_result == core_result { passed += 1; } else { failed += 1; println!( "5-card mismatch at test {}: math={:?}, core={:?}", i, math_result, core_result ); } } } println!("5-card compatibility: {} passed, {} failed", passed, failed); Ok((passed, failed)) } /// Test 6-card evaluation compatibility between math and core evaluators fn test_6_card_compatibility( &mut self, ) -> Result<(usize, usize), holdem_core::evaluator::errors::EvaluatorError> { println!("Testing 6-card evaluation compatibility..."); let test_hands = generate_test_hands(); let mut passed = 0; let mut failed = 0; for (i, cards) in test_hands.iter().enumerate() { if cards.len() >= 6 { let math_result = self .math_evaluator .evaluate_6_card(&cards[..6].try_into().unwrap()); let core_result = self .core_evaluator .evaluate_6_card(&cards[..6].try_into().unwrap()); if math_result == core_result { passed += 1; } else { failed += 1; println!( "6-card mismatch at test {}: math={:?}, core={:?}", i, math_result, core_result ); } } } println!("6-card compatibility: {} passed, {} failed", passed, failed); Ok((passed, failed)) } /// Test 7-card evaluation compatibility between math and core evaluators fn test_7_card_compatibility( &mut self, ) -> Result<(usize, usize), holdem_core::evaluator::errors::EvaluatorError> { println!("Testing 7-card evaluation compatibility..."); let test_hands = generate_test_hands(); let mut passed = 0; let mut failed = 0; for (i, cards) in test_hands.iter().enumerate() { let math_result = self.math_evaluator.evaluate_7_card(cards); let core_result = self.core_evaluator.evaluate_7_card(cards); if math_result == core_result { passed += 1; } else { failed += 1; println!( "7-card mismatch at test {}: math={:?}, core={:?}", i, math_result, core_result ); } } println!("7-card compatibility: {} passed, {} failed", passed, failed); Ok((passed, failed)) } /// Test type conversion compatibility fn test_type_conversion_compatibility( &mut self, ) -> Result<(usize, usize), holdem_core::evaluator::errors::EvaluatorError> { println!("Testing type conversion compatibility..."); let mut passed = 0; let mut failed = 0; // Test conversion between Card and PackedCard let original_cards = [ Card::from_str("As").unwrap(), Card::from_str("Ks").unwrap(), Card::from_str("Qs").unwrap(), Card::from_str("Js").unwrap(), Card::from_str("Ts").unwrap(), ]; // Convert to PackedCard and back let packed_cards = holdem_core::evaluator::integration::convert_cards(&original_cards); let converted_back = holdem_core::evaluator::integration::convert_cards_back(&packed_cards)?; if original_cards.len() == converted_back.len() { passed += 1; } else { failed += 1; println!( "Type conversion length mismatch: {} vs {}", original_cards.len(), converted_back.len() ); } // Test that evaluation works with converted cards let math_result_1 = self.math_evaluator.evaluate_5_card(&original_cards); let math_result_2 = self .math_evaluator .evaluate_5_card(&converted_back[..5].try_into().unwrap()); if math_result_1 == math_result_2 { passed += 1; } else { failed += 1; println!( "Evaluation mismatch after conversion: {:?} vs {:?}", math_result_1, math_result_2 ); } println!( "Type conversion compatibility: {} passed, {} failed", passed, failed ); Ok((passed, failed)) } } /// Generate test hands for integration testing fn generate_test_hands() -> Vec<[Card; 7]> { vec![ // Royal flush [ Card::from_str("As").unwrap(), Card::from_str("Ks").unwrap(), Card::from_str("Qs").unwrap(), Card::from_str("Js").unwrap(), Card::from_str("Ts").unwrap(), Card::from_str("7h").unwrap(), Card::from_str("6d").unwrap(), ], // Straight flush [ Card::from_str("9h").unwrap(), Card::from_str("8h").unwrap(), Card::from_str("7h").unwrap(), Card::from_str("6h").unwrap(), Card::from_str("5h").unwrap(), Card::from_str("4h").unwrap(), Card::from_str("3h").unwrap(), ], // Four of a kind [ Card::from_str("Ah").unwrap(), Card::from_str("Ac").unwrap(), Card::from_str("Ad").unwrap(), Card::from_str("As").unwrap(), Card::from_str("Kh").unwrap(), Card::from_str("Qh").unwrap(), Card::from_str("Jh").unwrap(), ], // Full house [ Card::from_str("Ah").unwrap(), Card::from_str("Ac").unwrap(), Card::from_str("Ad").unwrap(), Card::from_str("Ks").unwrap(), Card::from_str("Kh").unwrap(), Card::from_str("7h").unwrap(), Card::from_str("6d").unwrap(), ], // Flush [ Card::from_str("Ah").unwrap(), Card::from_str("Kh").unwrap(), Card::from_str("Qh").unwrap(), Card::from_str("9h").unwrap(), Card::from_str("7h").unwrap(), Card::from_str("5h").unwrap(), Card::from_str("3h").unwrap(), ], ] } /// Run integration tests pub fn run_integration_tests( ) -> Result { let mut suite = IntegrationTestSuite::new()?; suite.run_all_tests() } #[cfg(test)] mod tests { use super::*; #[test] fn test_integration_test_suite_creation() { let suite = IntegrationTestSuite::new(); assert!(suite.is_ok()); } #[test] fn test_test_hands_generation() { let hands = generate_test_hands(); assert_eq!(hands.len(), 5); // Should generate 5 test hands for hand in &hands { assert_eq!(hand.len(), 7); // All should be 7-card hands } } #[test] fn test_integration_test_runner() { let results = run_integration_tests(); assert!(results.is_ok()); let results = results.unwrap(); assert!(results.total_tests > 0); assert!(results.execution_time_us > 0); } }