# frozen_string_literal: true require 'spec_helper' RSpec.describe 'Tournament Partnership Analytics', type: :acceptance do let(:rom) { EuchreCamp::App['persistence.rom'] } let(:db) { rom.gateways[:default].connection } before do # Clear all data before each test in correct order (due to foreign keys) db[:partnership_games].delete db[:partnership_stats].delete db[:elo_snapshots].delete db[:bracket_matchups].delete db[:tournament_rounds].delete db[:matches].delete db[:teams].delete db[:event_participants].delete db[:events].delete db[:players].delete end describe 'Tournament Creation and Match Recording' do let!(:emma) { db[:players].insert(name: 'Emma', rating: 1000, current_elo: 1000) } let!(:alice) { db[:players].insert(name: 'Alice', rating: 1000, current_elo: 1000) } let!(:bob) { db[:players].insert(name: 'Bob', rating: 1000, current_elo: 1000) } let!(:charlie) { db[:players].insert(name: 'Charlie', rating: 1000, current_elo: 1000) } let!(:david) { db[:players].insert(name: 'David', rating: 1000, current_elo: 1000) } it 'creates a tournament with teams and matches' do # Create tournament tournament_id = db[:events].insert( name: 'Test Tournament', format: 'round_robin', status: 'active' ) # Create teams team_1_id = db[:teams].insert( event_id: tournament_id, team_name: 'Ace High', player_1_id: emma, player_2_id: alice ) team_2_id = db[:teams].insert( event_id: tournament_id, team_name: 'Kings', player_1_id: bob, player_2_id: charlie ) # Create a match match_id = db[:matches].insert( event_id: tournament_id, team_1_p1_id: emma, team_1_p2_id: alice, team_2_p1_id: bob, team_2_p2_id: charlie, team_1_score: 10, team_2_score: 7, status: 'completed' ) # Verify match was created match = db[:matches].where(id: match_id).first expect(match[:team_1_p1_id]).to eq(emma) expect(match[:team_1_p2_id]).to eq(alice) expect(match[:team_2_p1_id]).to eq(bob) expect(match[:team_2_p2_id]).to eq(charlie) expect(match[:team_1_score]).to eq(10) expect(match[:team_2_score]).to eq(7) end it 'calculates Elo for a match' do match_id = db[:matches].insert( team_1_p1_id: emma, team_1_p2_id: alice, team_2_p1_id: bob, team_2_p2_id: charlie, team_1_score: 10, team_2_score: 7, status: 'completed' ) # Run the Elo calculation job EuchreCamp::Jobs::CalculateEloJob.new.perform(match_id) # Verify Elo snapshots were created for all 4 players snapshots = db[:elo_snapshots].where(match_id: match_id).to_a expect(snapshots.length).to eq(4) # Verify player ELOs were updated emma_player = db[:players].where(id: emma).first expect(emma_player[:current_elo]).not_to eq(1000) end it 'records partnerships when match completes' do match_id = db[:matches].insert( team_1_p1_id: emma, team_1_p2_id: alice, team_2_p1_id: bob, team_2_p2_id: charlie, team_1_score: 10, team_2_score: 7, status: 'completed' ) # Run the full job (Elo + partnerships) EuchreCamp::Jobs::CalculateEloJob.new.perform(match_id) # Verify partnership games were recorded partnership_games = db[:partnership_games].where(match_id: match_id).to_a expect(partnership_games.length).to eq(2) # One for each team # Verify team 1 partnership (emma + alice) won team_1_game = partnership_games.find { |pg| pg[:team_number] == 1 } expect(team_1_game[:player_1_id]).to eq(emma) expect(team_1_game[:player_2_id]).to eq(alice) expect(team_1_game[:won_match]).to eq(1) # Verify team 2 partnership (bob + charlie) lost team_2_game = partnership_games.find { |pg| pg[:team_number] == 2 } expect(team_2_game[:player_1_id]).to eq(bob) expect(team_2_game[:player_2_id]).to eq(charlie) expect(team_2_game[:won_match]).to eq(0) # Verify partnership stats were updated stats = db[:partnership_stats].to_a expect(stats.length).to eq(2) # Check emma+alice stats emma_alice_stats = stats.find do |s| (s[:player_1_id] == emma && s[:player_2_id] == alice) || (s[:player_1_id] == alice && s[:player_2_id] == emma) end expect(emma_alice_stats[:games_played]).to eq(1) expect(emma_alice_stats[:wins]).to eq(1) expect(emma_alice_stats[:losses]).to eq(0) end it 'tracks multiple partnership statistics correctly' do # Match 1: emma+alice wins match_1_id = db[:matches].insert( team_1_p1_id: emma, team_1_p2_id: alice, team_2_p1_id: bob, team_2_p2_id: charlie, team_1_score: 10, team_2_score: 7, status: 'completed' ) EuchreCamp::Jobs::CalculateEloJob.new.perform(match_1_id) # Match 2: emma+alice wins again match_2_id = db[:matches].insert( team_1_p1_id: emma, team_1_p2_id: alice, team_2_p1_id: david, team_2_p2_id: bob, team_1_score: 10, team_2_score: 5, status: 'completed' ) EuchreCamp::Jobs::CalculateEloJob.new.perform(match_2_id) # Match 3: emma+alice loses match_3_id = db[:matches].insert( team_1_p1_id: emma, team_1_p2_id: alice, team_2_p1_id: bob, team_2_p2_id: charlie, team_1_score: 7, team_2_score: 10, status: 'completed' ) EuchreCamp::Jobs::CalculateEloJob.new.perform(match_3_id) # Check emma+alice stats emma_alice_stats = db[:partnership_stats].find do |s| (s[:player_1_id] == emma && s[:player_2_id] == alice) || (s[:player_1_id] == alice && s[:player_2_id] == emma) end expect(emma_alice_stats[:games_played]).to eq(3) expect(emma_alice_stats[:wins]).to eq(2) expect(emma_alice_stats[:losses]).to eq(1) end it 'calculates partnership win rates correctly' do # Create 5 matches for emma+alice with 3 wins, 2 losses matches = [ { p1: emma, p2: alice, opp1: bob, opp2: charlie, score1: 10, score2: 7 }, { p1: emma, p2: alice, opp1: david, opp2: bob, score1: 10, score2: 5 }, { p1: emma, p2: alice, opp1: bob, opp2: charlie, score1: 7, score2: 10 }, { p1: emma, p2: alice, opp1: david, opp2: charlie, score1: 10, score2: 8 }, { p1: emma, p2: alice, opp1: bob, opp2: david, score1: 6, score2: 10 }, ] matches.each do |m| match_id = db[:matches].insert( team_1_p1_id: m[:p1], team_1_p2_id: m[:p2], team_2_p1_id: m[:opp1], team_2_p2_id: m[:opp2], team_1_score: m[:score1], team_2_score: m[:score2], status: 'completed' ) EuchreCamp::Jobs::CalculateEloJob.new.perform(match_id) end # Check stats stats = db[:partnership_stats].find do |s| (s[:player_1_id] == emma && s[:player_2_id] == alice) || (s[:player_1_id] == alice && s[:player_2_id] == emma) end expect(stats[:games_played]).to eq(5) expect(stats[:wins]).to eq(3) expect(stats[:losses]).to eq(2) # Calculate expected win rate win_rate = stats[:wins].to_f / stats[:games_played] expect(win_rate).to be_within(0.01).of(0.600) end it 'retrieves partnership statistics for a player' do # Create matches with different partners for emma matches_data = [ # emma+alice (2 wins, 1 loss) { team_1: [emma, alice], team_2: [bob, charlie], score1: 10, score2: 7 }, { team_1: [emma, alice], team_2: [david, bob], score1: 10, score2: 5 }, { team_1: [emma, alice], team_2: [bob, charlie], score1: 7, score2: 10 }, # emma+bob (1 win, 0 losses) { team_1: [emma, bob], team_2: [alice, charlie], score1: 10, score2: 6 }, ] matches_data.each do |data| match_id = db[:matches].insert( team_1_p1_id: data[:team_1][0], team_1_p2_id: data[:team_1][1], team_2_p1_id: data[:team_2][0], team_2_p2_id: data[:team_2][1], team_1_score: data[:score1], team_2_score: data[:score2], status: 'completed' ) EuchreCamp::Jobs::CalculateEloJob.new.perform(match_id) end # Get partnerships for emma partnerships = EuchreCamp::Services::PartnershipTracker.get_player_partnerships(emma) expect(partnerships.length).to eq(2) # Check alice partnership alice_partner = partnerships.find { |p| p[:partner][:id] == alice } expect(alice_partner).not_to be_nil expect(alice_partner[:games_played]).to eq(3) expect(alice_partner[:wins]).to eq(2) expect(alice_partner[:losses]).to eq(1) expect(alice_partner[:win_rate]).to be_within(0.01).of(0.667) # Check bob partnership bob_partner = partnerships.find { |p| p[:partner][:id] == bob } expect(bob_partner).not_to be_nil expect(bob_partner[:games_played]).to eq(1) expect(bob_partner[:wins]).to eq(1) expect(bob_partner[:losses]).to eq(0) expect(bob_partner[:win_rate]).to eq(1.0) end end describe 'Round Robin Tournament Workflow' do let!(:emma) { db[:players].insert(name: 'Emma', rating: 1000, current_elo: 1000) } let!(:alice) { db[:players].insert(name: 'Alice', rating: 1000, current_elo: 1000) } let!(:bob) { db[:players].insert(name: 'Bob', rating: 1000, current_elo: 1000) } let!(:charlie) { db[:players].insert(name: 'Charlie', rating: 1000, current_elo: 1000) } it 'completes a full round-robin tournament workflow' do # 1. Create tournament tournament_id = db[:events].insert( name: 'Spring Championship', format: 'round_robin', status: 'active' ) # 2. Register participants [emma, alice, bob, charlie].each do |player_id| db[:event_participants].insert( event_id: tournament_id, player_id: player_id, status: 'registered' ) end # 3. Create teams (4 teams for round robin) teams = [ ['Ace High', emma, alice], ['Kings', bob, charlie], ['Queen High', emma, bob], ['Jacks', alice, charlie] ] team_ids = teams.map do |name, p1, p2| db[:teams].insert(event_id: tournament_id, team_name: name, player_1_id: p1, player_2_id: p2) end # 4. Generate schedule (using TournamentGenerator) rounds = EuchreCamp::Services::TournamentGenerator.round_robin( teams.map.with_index { |t, i| { id: team_ids[i], name: t[0] } } ) expect(rounds.length).to eq(3) # 4 teams = 3 rounds # 5. Create rounds and matchups rounds.each do |round_data| round_id = db[:tournament_rounds].insert( event_id: tournament_id, round_number: round_data[:round_number], status: round_data[:round_number] == 1 ? 'active' : 'pending' ) round_data[:matchups].each_with_index do |matchup, idx| db[:bracket_matchups].insert( event_id: tournament_id, round_id: round_id, team_1_id: matchup[:team_1_id], team_2_id: matchup[:team_2_id], bracket_position: idx + 1 ) end end # 6. Play round 1 matches round_1 = db[:tournament_rounds].where(round_number: 1, event_id: tournament_id).first round_1_matchups = db[:bracket_matchups].where(round_id: round_1[:id]).to_a round_1_matchups.each do |matchup| team_1 = db[:teams].where(id: matchup[:team_1_id]).first team_2 = db[:teams].where(id: matchup[:team_2_id]).first match_id = db[:matches].insert( event_id: tournament_id, team_1_p1_id: team_1[:player_1_id], team_1_p2_id: team_1[:player_2_id], team_2_p1_id: team_2[:player_1_id], team_2_p2_id: team_2[:player_2_id], team_1_score: 10, team_2_score: 7, status: 'completed' ) EuchreCamp::Jobs::CalculateEloJob.new.perform(match_id) end # 7. Verify all partnerships were recorded partnership_games = db[:partnership_games].to_a # For 2 matchups in round 1, we should have 4 partnership games (2 per matchup) # But if emma appears in multiple teams, we might see fewer unique games expect(partnership_games.length).to be >= 2 # 8. Verify player profiles can be retrieved emma_partnerships = EuchreCamp::Services::PartnershipTracker.get_player_partnerships(emma) expect(emma_partnerships.length).to be >= 1 end end describe 'Player Profile Page' do let!(:emma) { db[:players].insert(name: 'Emma', rating: 1000, current_elo: 1000) } let!(:alice) { db[:players].insert(name: 'Alice', rating: 1000, current_elo: 1000) } it 'displays player profile with partnership data' do # Create additional players for opponents bob = db[:players].insert(name: 'Bob', rating: 1000, current_elo: 1000) charlie = db[:players].insert(name: 'Charlie', rating: 1000, current_elo: 1000) # Create some matches 5.times do |i| match_id = db[:matches].insert( team_1_p1_id: emma, team_1_p2_id: alice, team_2_p1_id: bob, team_2_p2_id: charlie, team_1_score: 10, team_2_score: 7, status: 'completed' ) EuchreCamp::Jobs::CalculateEloJob.new.perform(match_id) end # Get partnerships for emma partnerships = EuchreCamp::Services::PartnershipTracker.get_player_partnerships(emma) # Verify data structure expect(partnerships).not_to be_empty partnership = partnerships.first expect(partnership[:partner][:name]).to eq('Alice') expect(partnership[:games_played]).to eq(5) expect(partnership[:wins]).to eq(5) expect(partnership[:losses]).to eq(0) expect(partnership[:win_rate]).to eq(1.0) end end end