Merge pull request #56 from turniere/ticket/TURNIERE-246
Fix teams not being put into second stage if part of :single_team match
This commit is contained in:
commit
30e852aba9
|
|
@ -13,7 +13,7 @@ class AddGroupStageToTournament
|
|||
tournament.instant_finalists_amount, tournament.intermediate_round_participants_amount =
|
||||
TournamentService.calculate_default_amount_of_teams_advancing(tournament.playoff_teams_amount,
|
||||
group_stage.groups.size)
|
||||
context.object_to_save = tournament
|
||||
(context.object_to_save ||= []) << tournament
|
||||
rescue StandardError
|
||||
context.fail!
|
||||
end
|
||||
|
|
|
|||
|
|
@ -12,7 +12,8 @@ class AddPlayoffsToTournament
|
|||
else
|
||||
tournament.stages.concat playoff_stages
|
||||
end
|
||||
context.object_to_save = tournament
|
||||
context.intermediate_stage = tournament.stages.find(&:intermediate_stage?)
|
||||
(context.object_to_save ||= []) << tournament
|
||||
else
|
||||
context.fail!
|
||||
end
|
||||
|
|
|
|||
|
|
@ -0,0 +1,16 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class AdvanceTeamsInIntermediateStage
|
||||
include Interactor
|
||||
|
||||
def call
|
||||
intermediate_stage = context.intermediate_stage
|
||||
return if intermediate_stage.nil?
|
||||
|
||||
intermediate_stage.matches.select { |m| m.state == 'single_team' }
|
||||
.each do |match|
|
||||
context.fail! unless PopulateMatchBelowAndSave.call(match: match).success?
|
||||
end
|
||||
(context.object_to_save ||= []) << intermediate_stage
|
||||
end
|
||||
end
|
||||
|
|
@ -7,7 +7,7 @@ class PopulateMatchBelow
|
|||
match = context.match
|
||||
begin
|
||||
objects_to_save = PlayoffStageService.populate_match_below(match)
|
||||
context.object_to_save = objects_to_save
|
||||
(context.object_to_save ||= []) << objects_to_save
|
||||
rescue StandardError
|
||||
context.fail!
|
||||
end
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ class UpdateGroupsGroupScores
|
|||
include Interactor
|
||||
|
||||
def call
|
||||
context.object_to_save = GroupStageService.update_group_scores(context.group)
|
||||
(context.object_to_save ||= []) << GroupStageService.update_group_scores(context.group)
|
||||
rescue StandardError
|
||||
context.fail!
|
||||
end
|
||||
|
|
|
|||
|
|
@ -28,6 +28,8 @@ class Match < ApplicationRecord
|
|||
end
|
||||
|
||||
def winner
|
||||
return teams.first if single_team?
|
||||
|
||||
finished? ? current_leading_team : nil
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class Stage < ApplicationRecord
|
||||
enum state: %i[playoff_stage in_progress finished]
|
||||
enum state: %i[playoff_stage intermediate_stage in_progress finished]
|
||||
|
||||
belongs_to :tournament
|
||||
has_many :matches, dependent: :destroy
|
||||
|
|
|
|||
|
|
@ -3,5 +3,5 @@
|
|||
class AddPlayoffsToTournamentAndSave
|
||||
include Interactor::Organizer
|
||||
|
||||
organize AddPlayoffsToTournament, SaveApplicationRecordObject
|
||||
organize AddPlayoffsToTournament, AdvanceTeamsInIntermediateStage, SaveApplicationRecordObject
|
||||
end
|
||||
|
|
|
|||
|
|
@ -45,12 +45,14 @@ class MatchService
|
|||
end
|
||||
|
||||
# the start point is to compensate for all the teams that are already within a "normal" match
|
||||
startpoint = matches.size
|
||||
i = team_offset = matches.size
|
||||
until matches.size >= needed_games
|
||||
# while we do not have enough matches in general we need to fill the array with "single team" matches
|
||||
i = matches.size + startpoint
|
||||
match = Match.new state: :single_team, position: i, match_scores: [MatchScore.create(team: teams[i])]
|
||||
match = Match.new state: :single_team,
|
||||
position: i,
|
||||
match_scores: [MatchScore.create(team: teams[i + team_offset])]
|
||||
matches << match
|
||||
i += 1
|
||||
end
|
||||
matches
|
||||
end
|
||||
|
|
|
|||
|
|
@ -12,12 +12,11 @@ class PlayoffStageService
|
|||
# initial_matches are the matches in the first stage; this is the only stage filled with teams from the start on
|
||||
initial_matches = MatchService.generate_matches(teams)
|
||||
initial_stage = Stage.new level: stage_count - 1, matches: initial_matches
|
||||
initial_stage.state = :intermediate_stage unless initial_stage.matches.find(&:single_team?).nil?
|
||||
playoffs << initial_stage
|
||||
# empty stages are the stages, the tournament is filled with to have the matches ready for later
|
||||
empty_stages = generate_stages_with_empty_matches(stage_count - 1)
|
||||
empty_stages.each do |stage|
|
||||
playoffs << stage
|
||||
end
|
||||
playoffs.concat empty_stages
|
||||
playoffs
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -14,6 +14,13 @@ FactoryBot.define do
|
|||
state { :in_progress }
|
||||
end
|
||||
|
||||
factory :single_team_match do
|
||||
state { :single_team }
|
||||
after(:create) do |match|
|
||||
match.match_scores = [create(:match_score, points: 0)]
|
||||
end
|
||||
end
|
||||
|
||||
factory :empty_prepared_playoff_match do
|
||||
state { :not_ready }
|
||||
end
|
||||
|
|
|
|||
|
|
@ -0,0 +1,52 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
RSpec.describe AdvanceTeamsInIntermediateStage do
|
||||
shared_examples_for 'succeeding context' do
|
||||
it 'succeeds' do
|
||||
expect(context).to be_a_success
|
||||
end
|
||||
end
|
||||
|
||||
shared_examples_for 'failing context' do
|
||||
it 'fails' do
|
||||
expect(context).to be_a_failure
|
||||
end
|
||||
end
|
||||
|
||||
context 'intermediate_stage is nil' do
|
||||
let(:context) do
|
||||
AdvanceTeamsInIntermediateStage.call(intermediate_stage: nil)
|
||||
end
|
||||
|
||||
it_behaves_like 'succeeding context'
|
||||
|
||||
it 'doesn\'t call PopulateMatchBelow' do
|
||||
expect(PopulateMatchBelowAndSave).not_to receive(:call)
|
||||
context
|
||||
end
|
||||
end
|
||||
|
||||
context 'intermediate_stage is a realistic stage' do
|
||||
let(:context) do
|
||||
AdvanceTeamsInIntermediateStage.call(intermediate_stage: create(:playoff_stage, match_type: :single_team_match))
|
||||
end
|
||||
|
||||
context 'PopulateMatchBelow succeeds' do
|
||||
before do
|
||||
expect(class_double('PopulateMatchBelowAndSave').as_stubbed_const(transfer_nested_constants: true))
|
||||
.to receive(:call).exactly(4).times.and_return(double(:context, success?: true))
|
||||
end
|
||||
|
||||
it_behaves_like 'succeeding context'
|
||||
end
|
||||
|
||||
context 'PopulateMatchBelow fails' do
|
||||
before do
|
||||
expect(class_double('PopulateMatchBelowAndSave').as_stubbed_const(transfer_nested_constants: true))
|
||||
.to receive(:call).and_return(double(:context, success?: false))
|
||||
end
|
||||
|
||||
it_behaves_like 'failing context'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -21,7 +21,7 @@ RSpec.describe PopulateMatchBelow, type: :interactor do
|
|||
end
|
||||
|
||||
it 'provides the objects to save' do
|
||||
expect(context.object_to_save).to match_array(@objects_to_save)
|
||||
expect(context.object_to_save.flatten).to match_array(@objects_to_save.flatten)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ RSpec.describe UpdateGroupsGroupScores, type: :interactor do
|
|||
end
|
||||
|
||||
it 'provides the objects to save' do
|
||||
expect(context.object_to_save).to eq(@group_scores)
|
||||
expect(context.object_to_save.flatten).to eq(@group_scores)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -117,5 +117,16 @@ RSpec.describe MatchService do
|
|||
it 'raises an exception for for 0 teams' do
|
||||
expect { MatchService.generate_matches([]) }. to raise_error 'Cannot generate Matches without teams'
|
||||
end
|
||||
|
||||
it 'generates matches with consecutive positions' do
|
||||
MatchService.generate_matches(create_list(:team, 7)).sort_by(&:position).each_with_index do |match, i|
|
||||
expect(match.position).to eq(i)
|
||||
end
|
||||
end
|
||||
|
||||
it 'places all given teams into the matches exactly once' do
|
||||
teams = create_list(:team, 11)
|
||||
expect(MatchService.generate_matches(teams).map(&:teams).flatten).to match_array(teams)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -51,7 +51,7 @@ RSpec.describe PlayoffStageService do
|
|||
end
|
||||
end
|
||||
|
||||
describe 'generates playoff stages for' do
|
||||
describe '#generate_playoff_stages' do
|
||||
[
|
||||
{ team_size: 1, expected_amount_of_playoff_stages: 1 },
|
||||
{ team_size: 2, expected_amount_of_playoff_stages: 1 },
|
||||
|
|
@ -66,7 +66,7 @@ RSpec.describe PlayoffStageService do
|
|||
{ team_size: 64, expected_amount_of_playoff_stages: 6 },
|
||||
{ team_size: 111, expected_amount_of_playoff_stages: 7 }
|
||||
].each do |parameters|
|
||||
it "#{parameters[:team_size]} teams" do
|
||||
it "generates playoff stages for #{parameters[:team_size]} teams" do
|
||||
amount_of_teams = parameters[:team_size]
|
||||
expected_amount_of_playoff_stages = parameters[:expected_amount_of_playoff_stages]
|
||||
teams = build_list(:team, amount_of_teams)
|
||||
|
|
@ -79,6 +79,38 @@ RSpec.describe PlayoffStageService do
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'number of teams isn\'t a power of two' do
|
||||
let(:generated_stages) do
|
||||
PlayoffStageService.generate_playoff_stages(create_list(:team, 12))
|
||||
end
|
||||
|
||||
let(:intermediate_stage) do
|
||||
generated_stages.max_by(&:level)
|
||||
end
|
||||
|
||||
it 'generates an intermediate stage at the top level' do
|
||||
expect(intermediate_stage.state).to eq('intermediate_stage')
|
||||
end
|
||||
|
||||
it 'generates normal playoff_stage state stages elsewhere' do
|
||||
(generated_stages - [intermediate_stage]).each do |stage|
|
||||
expect(stage.state).to eq('playoff_stage')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'number of teams is a power of two' do
|
||||
let(:generated_stages) do
|
||||
PlayoffStageService.generate_playoff_stages(create_list(:team, 16))
|
||||
end
|
||||
|
||||
it 'generates only normal playoff_stage state stages' do
|
||||
generated_stages.each do |stage|
|
||||
expect(stage.state).to eq('playoff_stage')
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#populate_match_below' do
|
||||
|
|
|
|||
Loading…
Reference in New Issue