Merge branch 'codex/TUR-81-decider-advancing-seeding' into 'master'
TUR-81 require deciders for advancing seeding ties Closes TUR-81 See merge request turniere/turniere-backend!46
This commit is contained in:
commit
ed0484c79c
|
|
@ -224,12 +224,11 @@ class GroupStageService
|
||||||
nil
|
nil
|
||||||
end
|
end
|
||||||
|
|
||||||
def unresolved_tie_crosses_advancing_cutoff?(sorted_group_scores, group_score, advancing_slots)
|
def unresolved_tie_affects_advancing_placements?(sorted_group_scores, group_score, advancing_slots)
|
||||||
return false if advancing_slots.zero?
|
return false if advancing_slots.zero?
|
||||||
|
|
||||||
cluster = tie_cluster_for(sorted_group_scores, group_score)
|
cluster = tie_cluster_for(sorted_group_scores, group_score)
|
||||||
occupied_slots = (cluster[:start_index] + 1)..(cluster[:end_index] + 1)
|
cluster[:start_index] < advancing_slots
|
||||||
occupied_slots.begin <= advancing_slots && occupied_slots.end > advancing_slots
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def tie_cluster_for(sorted_group_scores, group_score)
|
def tie_cluster_for(sorted_group_scores, group_score)
|
||||||
|
|
@ -298,7 +297,7 @@ class GroupStageService
|
||||||
tied: comparison_reason[:tied],
|
tied: comparison_reason[:tied],
|
||||||
needs_decider_match: group_ready_for_decider_check &&
|
needs_decider_match: group_ready_for_decider_check &&
|
||||||
comparison_reason[:tied] &&
|
comparison_reason[:tied] &&
|
||||||
unresolved_tie_crosses_advancing_cutoff?(sorted_group_scores, group_score, advancing_slots)
|
unresolved_tie_affects_advancing_placements?(sorted_group_scores, group_score, advancing_slots)
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -201,5 +201,41 @@ RSpec.describe StagesController, type: :controller do
|
||||||
expect(body[:blocking_ties].first[:team_names]).to eq(%w[A B])
|
expect(body[:blocking_ties].first[:team_names]).to eq(%w[A B])
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context 'when tied advancing teams still need a decider to fix playoff seeding' do
|
||||||
|
let(:group_stage) do
|
||||||
|
create(:group_stage, tournament: create(:tournament, teams_count: 0), group_count: 0)
|
||||||
|
end
|
||||||
|
|
||||||
|
before do
|
||||||
|
tournament = group_stage.tournament
|
||||||
|
tournament.update!(playoff_teams_amount: 2, instant_finalists_amount: 2, intermediate_round_participants_amount: 0)
|
||||||
|
group = create(:group, stage: group_stage, match_count: 0, number: 1)
|
||||||
|
group_stage.groups = [group]
|
||||||
|
team_a = create(:team, tournament: tournament, name: 'A')
|
||||||
|
team_b = create(:team, tournament: tournament, name: 'B')
|
||||||
|
create(:group_match, group: group, state: :finished).tap do |match|
|
||||||
|
create(:match_score, match: match, team: team_a, points: 2)
|
||||||
|
create(:match_score, match: match, team: team_b, points: 2)
|
||||||
|
end
|
||||||
|
score_a = create(:group_score, group: group, team: team_a, group_points: 7, scored_points: 4, received_points: 1)
|
||||||
|
score_b = create(:group_score, group: group, team: team_b, group_points: 7, scored_points: 4, received_points: 1)
|
||||||
|
GroupStageService.send(:recalculate_position_of_group_scores!, [score_a, score_b]).each(&:save!)
|
||||||
|
|
||||||
|
apply_authentication_headers_for group_stage.owner
|
||||||
|
put :update, params: { id: group_stage.to_param }.merge(finished)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns unprocessable entity' do
|
||||||
|
expect(response).to have_http_status(:unprocessable_entity)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns a helpful decider error' do
|
||||||
|
body = deserialize_response(response)
|
||||||
|
expect(body[:error]).to eq('Playoffs cannot be generated yet because a decider match is required for an advancing place')
|
||||||
|
expect(body[:blocking_ties].size).to eq(1)
|
||||||
|
expect(body[:blocking_ties].first[:team_names]).to eq(%w[A B])
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -268,7 +268,7 @@ RSpec.describe GroupStageService do
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'keeps shared positions and requests a decider only when an unresolved tie crosses the cutoff' do
|
it 'keeps shared positions and requests a decider when an unresolved tie affects advancing placements' do
|
||||||
tournament.update!(playoff_teams_amount: 1, instant_finalists_amount: 1, intermediate_round_participants_amount: 0)
|
tournament.update!(playoff_teams_amount: 1, instant_finalists_amount: 1, intermediate_round_participants_amount: 0)
|
||||||
|
|
||||||
create(:group_match, group: group, state: :finished).tap do |match|
|
create(:group_match, group: group, state: :finished).tap do |match|
|
||||||
|
|
@ -300,6 +300,38 @@ RSpec.describe GroupStageService do
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it 'requests a decider when tied teams both advance but their seeding stays unresolved' do
|
||||||
|
tournament.update!(playoff_teams_amount: 2, instant_finalists_amount: 2, intermediate_round_participants_amount: 0)
|
||||||
|
|
||||||
|
create(:group_match, group: group, state: :finished).tap do |match|
|
||||||
|
create(:match_score, match: match, team: team_a, points: 2)
|
||||||
|
create(:match_score, match: match, team: team_b, points: 2)
|
||||||
|
end
|
||||||
|
|
||||||
|
score_a = create(:group_score, group: group, team: team_a, group_points: 7, scored_points: 4, received_points: 1)
|
||||||
|
score_b = create(:group_score, group: group, team: team_b, group_points: 7, scored_points: 4, received_points: 1)
|
||||||
|
score_c = create(:group_score, group: group, team: team_c, group_points: 3, scored_points: 2, received_points: 3)
|
||||||
|
score_d = create(:group_score, group: group, team: team_d, group_points: 0, scored_points: 1, received_points: 7)
|
||||||
|
|
||||||
|
GroupStageService.send(:recalculate_position_of_group_scores!, [score_a, score_b, score_c, score_d]).each(&:save!)
|
||||||
|
|
||||||
|
expect([score_a.position, score_b.position, score_c.position, score_d.position]).to eq([1, 1, 3, 4])
|
||||||
|
expect(GroupStageService.ranking_decision_for(score_a)).to include(
|
||||||
|
resolved_by: 'tie',
|
||||||
|
direct_comparison_result: 'draw',
|
||||||
|
hidden_points_result: 'not_played',
|
||||||
|
tied: true,
|
||||||
|
needs_decider_match: true
|
||||||
|
)
|
||||||
|
expect(GroupStageService.ranking_decision_for(score_b)).to include(
|
||||||
|
resolved_by: 'tie',
|
||||||
|
direct_comparison_result: 'draw',
|
||||||
|
hidden_points_result: 'not_played',
|
||||||
|
tied: true,
|
||||||
|
needs_decider_match: true
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
it 'does not expose unresolved tie metadata while regular matches in the group are still unfinished' do
|
it 'does not expose unresolved tie metadata while regular matches in the group are still unfinished' do
|
||||||
tournament.update!(playoff_teams_amount: 1, instant_finalists_amount: 1, intermediate_round_participants_amount: 0)
|
tournament.update!(playoff_teams_amount: 1, instant_finalists_amount: 1, intermediate_round_participants_amount: 0)
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue