Merge pull request #38 from turniere/ticket/TURNIERE-190
Implement stopping of matches
This commit is contained in:
commit
e281b1b23f
|
|
@ -5,9 +5,9 @@ addons:
|
|||
env:
|
||||
- RAILS_ENV=test
|
||||
script:
|
||||
- sonar-scanner
|
||||
- bundle exec rails db:migrate
|
||||
- bundle exec rails spec
|
||||
- sonar-scanner
|
||||
notifications:
|
||||
slack:
|
||||
secure: EDlQKAXSltE2d4vdOtwVdFhPScjFU2rsSSgAGSqV24br4Jb/KihpjracavZW5wnmloiWe0ulj19j7LOtJSCNJOGqeAnct+axNyBRTI+9ctpeBDMHHtiOH9IX2EBsnEBpHdL4gMgOrPFfMoyn+sqbZ7EJgOFU41f/c7X0XUf1QeJ02Gh/uY1+m8Qo0eT9x4u8W+wnCFYCQeTWOB9/4aemkgbELOEDCbLYr5n+HCGK1vi+glmYoyldVr2yQBnbfME2fcNSOb7ytPDzjBI00cdGVhj8e/AMsF84W+Q+U3RIF0zjestQeFp3lPtTcHDt/MRH39MV1fjRaZB4A8+QYrjuECJ6wjzvzXJbGWUjE++6OmbRmszPlkFxXDiiiAe/Vs1NzUr4i7c2aWZhq8Q/6HDwYXx+/OUJY3THpCHjel/PC49s+KZqMrmq53nd6NWSCtZSPCXN/1uqb3m/zUq7i4wSNFirN+9E8reYkEq6GrpG1VwZkpKp9SkjWnd88cgM0JQEpC/dxRrmeI3o+uPRSIXV+RIaGCXIAdWO7eWBIJdpVQNrA4GDjWc+zj0X02qgbn6d6iByFCDtXzB+ognZwmKUnpJ4tF3oh5xv7j6cFw/GNirgThTLwEoXMfC/Q9OmhlYByOsZ+PBApsj0hfs74YXfN753eCglmtOKGqkpRT6kwG8=
|
||||
|
|
|
|||
|
|
@ -13,12 +13,14 @@ class MatchesController < ApplicationController
|
|||
# PATCH/PUT /matches/1
|
||||
def update
|
||||
new_state = match_params['state']
|
||||
if new_state == 'finished'
|
||||
# implement logic to move the winning team into the next stage
|
||||
match_params['state'] = 'team1_won' # or 'team2_won' or 'undecided'
|
||||
render json: {}, status: :not_implemented
|
||||
end
|
||||
if @match.update(match_params)
|
||||
if new_state == 'finished'
|
||||
result = PopulateMatchBelowAndSave.call(match: @match) unless @match.group_match?
|
||||
unless result.success?
|
||||
render json: { error: 'Moving Team one stage down failed' }, status: :unprocessable_entity
|
||||
return
|
||||
end
|
||||
end
|
||||
render json: @match
|
||||
else
|
||||
render json: @match.errors, status: :unprocessable_entity
|
||||
|
|
|
|||
|
|
@ -38,14 +38,14 @@ class TournamentsController < ApplicationController
|
|||
if group_stage
|
||||
groups = organize_teams_in_groups(teams)
|
||||
# add groups to tournament
|
||||
result = AddGroupStageToTournamentAndSaveTournamentToDatabase.call(tournament: tournament, groups: groups)
|
||||
result = AddGroupStageToTournamentAndSave.call(tournament: tournament, groups: groups)
|
||||
else
|
||||
# convert teams parameter into Team objects
|
||||
teams = teams.map(&method(:find_or_create_team))
|
||||
# associate provided teams with tournament
|
||||
tournament.teams = teams
|
||||
# add playoff stage to tournament
|
||||
result = AddPlayoffsToTournamentAndSaveTournamentToDatabase.call(tournament: tournament)
|
||||
result = AddPlayoffsToTournamentAndSave.call(tournament: tournament)
|
||||
end
|
||||
# validate tournament
|
||||
unless tournament.valid?
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ class AddGroupStageToTournament
|
|||
begin
|
||||
group_stage = GroupStageService.generate_group_stage(groups)
|
||||
tournament.stages = [group_stage]
|
||||
context.tournament = tournament
|
||||
context.object_to_save = tournament
|
||||
rescue StandardError
|
||||
context.fail!
|
||||
end
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ class AddPlayoffsToTournament
|
|||
else
|
||||
tournament.stages.concat playoff_stages
|
||||
end
|
||||
context.tournament = tournament
|
||||
context.object_to_save = tournament
|
||||
else
|
||||
context.fail!
|
||||
end
|
||||
|
|
|
|||
|
|
@ -0,0 +1,15 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class PopulateMatchBelow
|
||||
include Interactor
|
||||
|
||||
def call
|
||||
match = context.match
|
||||
begin
|
||||
objects_to_save = PlayoffStageService.populate_match_below(match)
|
||||
context.object_to_save = objects_to_save
|
||||
rescue StandardError
|
||||
context.fail!
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class SaveApplicationRecordObject
|
||||
include Interactor
|
||||
|
||||
def call
|
||||
Array(context.object_to_save).flatten.each do |object|
|
||||
context.fail! unless object.save
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -1,13 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class SaveTournamentToDatabase
|
||||
include Interactor
|
||||
|
||||
def call
|
||||
if context.tournament.save
|
||||
nil
|
||||
else
|
||||
context.fail!
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class Match < ApplicationRecord
|
||||
enum state: %i[single_team not_ready not_started in_progress team1_won team2_won undecided]
|
||||
enum state: %i[single_team not_ready not_started in_progress finished undecided]
|
||||
|
||||
belongs_to :stage, optional: true
|
||||
belongs_to :group, optional: true
|
||||
|
|
@ -19,23 +19,20 @@ class Match < ApplicationRecord
|
|||
stage ? stage.owner : group.owner
|
||||
end
|
||||
|
||||
private
|
||||
def winner
|
||||
return nil unless finished?
|
||||
return nil if match_scores.first.points == match_scores.second.points
|
||||
|
||||
def stage_xor_group
|
||||
errors.add(:stage_xor_group, 'Stage and Group missing or both present') unless stage.present? ^ group.present?
|
||||
end
|
||||
|
||||
def evaluate_status
|
||||
if score_team1 < score_team2
|
||||
:team2_won
|
||||
elsif score_team2 < score_team1
|
||||
:team1_won
|
||||
else
|
||||
group_match? ? :undecided : :in_progress
|
||||
end
|
||||
match_scores.max_by(&:points).team
|
||||
end
|
||||
|
||||
def group_match?
|
||||
group.present?
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def stage_xor_group
|
||||
errors.add(:stage_xor_group, 'Stage and Group missing or both present') unless stage.present? ^ group.present?
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -0,0 +1,7 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class AddGroupStageToTournamentAndSave
|
||||
include Interactor::Organizer
|
||||
|
||||
organize AddGroupStageToTournament, SaveApplicationRecordObject
|
||||
end
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class AddGroupStageToTournamentAndSaveTournamentToDatabase
|
||||
include Interactor::Organizer
|
||||
|
||||
organize AddGroupStageToTournament, SaveTournamentToDatabase
|
||||
end
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class AddPlayoffsToTournamentAndSave
|
||||
include Interactor::Organizer
|
||||
|
||||
organize AddPlayoffsToTournament, SaveApplicationRecordObject
|
||||
end
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class AddPlayoffsToTournamentAndSaveTournamentToDatabase
|
||||
include Interactor::Organizer
|
||||
|
||||
organize AddPlayoffsToTournament, SaveTournamentToDatabase
|
||||
end
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class PopulateMatchBelowAndSave
|
||||
include Interactor::Organizer
|
||||
|
||||
organize PopulateMatchBelow, SaveApplicationRecordObject
|
||||
end
|
||||
|
|
@ -1,73 +1,149 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class PlayoffStageService
|
||||
# Generates the playoff stage given the tournament
|
||||
#
|
||||
# @param teams [Array] The teams to generate the playoff stages with
|
||||
# @return [Array] the generated playoff stages
|
||||
def self.generate_playoff_stages(teams)
|
||||
playoffs = []
|
||||
stage_count = calculate_required_stage_count(teams.size)
|
||||
# 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
|
||||
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
|
||||
class << self
|
||||
# Generates the playoff stage given the tournament
|
||||
#
|
||||
# @param teams [Array] The teams to generate the playoff stages with
|
||||
# @return [Array] the generated playoff stages
|
||||
def generate_playoff_stages(teams)
|
||||
playoffs = []
|
||||
stage_count = calculate_required_stage_count(teams.size)
|
||||
# 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
|
||||
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
|
||||
end
|
||||
playoffs
|
||||
end
|
||||
|
||||
# Generates the playoff stage given the tournament
|
||||
#
|
||||
# @param tournament [Tournament] The tournament to generate the playoff stages from
|
||||
# @return [Array] the generated playoff stages
|
||||
def self.generate_playoff_stages_from_tournament(tournament)
|
||||
generate_playoff_stages tournament.teams
|
||||
end
|
||||
|
||||
# Generates given number of empty stages
|
||||
#
|
||||
# @param stage_count [Integer] number of stages to generate
|
||||
# @return [Array] the generated stages
|
||||
def self.generate_stages_with_empty_matches(stage_count)
|
||||
empty_stages = []
|
||||
stage_count.times do |i|
|
||||
stage = Stage.new level: i, matches: generate_empty_matches(2**i)
|
||||
empty_stages << stage
|
||||
# Generates the playoff stage given the tournament
|
||||
#
|
||||
# @param tournament [Tournament] The tournament to generate the playoff stages from
|
||||
# @return [Array] the generated playoff stages
|
||||
def generate_playoff_stages_from_tournament(tournament)
|
||||
generate_playoff_stages tournament.teams
|
||||
end
|
||||
# as we are generating the stages in the wrong order (starting with the lowest number of matches (which is
|
||||
# the final stage)) they need to be reversed
|
||||
empty_stages.reverse!
|
||||
end
|
||||
|
||||
# Generates a number of empty matches to fill later stages
|
||||
#
|
||||
# @param amount [Integer] the amount of matches to generate
|
||||
# @return [Array] the generated matches
|
||||
def self.generate_empty_matches(amount)
|
||||
matches = []
|
||||
amount.times do |i|
|
||||
match = Match.new state: :not_ready, position: i
|
||||
matches << match
|
||||
# Generates given number of empty stages
|
||||
#
|
||||
# @param stage_count [Integer] number of stages to generate
|
||||
# @return [Array] the generated stages
|
||||
def generate_stages_with_empty_matches(stage_count)
|
||||
empty_stages = []
|
||||
stage_count.times do |i|
|
||||
stage = Stage.new level: i, matches: generate_empty_matches(2**i)
|
||||
empty_stages << stage
|
||||
end
|
||||
# as we are generating the stages in the wrong order (starting with the lowest number of matches (which is
|
||||
# the final stage)) they need to be reversed
|
||||
empty_stages.reverse!
|
||||
end
|
||||
matches
|
||||
end
|
||||
|
||||
# Calculates how many stages are required for given number of teams
|
||||
#
|
||||
# @param number_of_teams [Integer] the teams number of teams to calculate amount of stages
|
||||
# @return [Integer] amount of required stages
|
||||
def self.calculate_required_stage_count(number_of_teams)
|
||||
if number_of_teams == 1
|
||||
1
|
||||
else
|
||||
# black voodoo magic
|
||||
stage_count = Math.log(Utils.next_power_of_two(number_of_teams)) / Math.log(2)
|
||||
stage_count -= 1 if Utils.po2?(number_of_teams)
|
||||
stage_count.to_int
|
||||
# Generates a number of empty matches to fill later stages
|
||||
#
|
||||
# @param amount [Integer] the amount of matches to generate
|
||||
# @return [Array] the generated matches
|
||||
def generate_empty_matches(amount)
|
||||
matches = []
|
||||
amount.times do |i|
|
||||
match = Match.new state: :not_ready, position: i
|
||||
matches << match
|
||||
end
|
||||
matches
|
||||
end
|
||||
|
||||
# Calculates how many stages are required for given number of teams
|
||||
#
|
||||
# @param number_of_teams [Integer] the teams number of teams to calculate amount of stages
|
||||
# @return [Integer] amount of required stages
|
||||
def calculate_required_stage_count(number_of_teams)
|
||||
if number_of_teams == 1
|
||||
1
|
||||
else
|
||||
# black voodoo magic
|
||||
stage_count = Math.log(Utils.next_power_of_two(number_of_teams)) / Math.log(2)
|
||||
stage_count -= 1 if Utils.po2?(number_of_teams)
|
||||
stage_count.to_int
|
||||
end
|
||||
end
|
||||
|
||||
# Populates the match below given match with the winners of the matches above
|
||||
#
|
||||
# @param current_match [Match] The Match which finished, the match below it gets populated
|
||||
# @return [Array] the objects that changed and need to be saved
|
||||
def populate_match_below(current_match)
|
||||
current_stage = current_match.stage
|
||||
next_stage = current_stage.tournament.stages.find { |s| s.level == current_stage.level - 1 }
|
||||
# return if next stage does not exist (there are no matches after the finale)
|
||||
return if next_stage.nil?
|
||||
|
||||
current_position = current_match.position
|
||||
|
||||
# a "companion" match is the one that with the selected match makes up the two matches
|
||||
# of which the winners advance into the match below
|
||||
# depending on the position of the match, the companion match is either on the left or right of it
|
||||
companion_match = find_companion_match(current_position, current_stage)
|
||||
|
||||
match_below = next_stage.matches.find { |m| m.position == current_position / 2 }
|
||||
match_scores = match_below.match_scores.sort_by(&:id)
|
||||
|
||||
winners = get_winners_of(companion_match, current_match)
|
||||
|
||||
# depending on the amount of match_scores already present we need to do different things
|
||||
match_scores = assign_correct_match_scores!(match_scores, winners)
|
||||
|
||||
# If a match is not decided yet, it will return nil as winner.
|
||||
# This is not allowed in Database. The following code filters out MatchScores that contain nil as team.
|
||||
match_scores = match_scores.select { |ms| ms.team.present? }
|
||||
match_below.match_scores = match_scores
|
||||
match_below.state = if match_below.match_scores.empty? || match_below.match_scores.size == 1
|
||||
:not_ready
|
||||
elsif match_below.match_scores.size == 2
|
||||
:not_started
|
||||
else
|
||||
raise 'Unprocessable amount of match_scores found'
|
||||
end
|
||||
[match_below, match_scores].flatten
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def find_companion_match(current_position, current_stage)
|
||||
companion_match_position = current_position.even? ? current_position + 1 : current_position - 1
|
||||
current_stage.matches.find { |m| m.position == companion_match_position }
|
||||
end
|
||||
|
||||
def assign_correct_match_scores!(match_scores, winners)
|
||||
case match_scores.size
|
||||
when 0
|
||||
# when 0 match_scores are already there we create both of them with the respective winner from above
|
||||
match_scores = winners.map { |winner| MatchScore.new(team: winner) }
|
||||
when 1
|
||||
# when 1 match_score is present, we need to check which team is contained within and add the other team as well
|
||||
if match_scores.first.team == winners.first
|
||||
match_scores.push MatchScore.new(team: winners.second)
|
||||
elsif match_scores.first.team == winners.second
|
||||
match_scores.push MatchScore.new(team: winners.first)
|
||||
else
|
||||
match_scores.first.destroy
|
||||
match_scores = winners.map { |winner| MatchScore.new(team: winner) }
|
||||
end
|
||||
when 2
|
||||
# when 2 match_scores are present, the teams just get overwritten
|
||||
match_scores.first.team = winners.first
|
||||
match_scores.second.team = winners.second
|
||||
end
|
||||
match_scores
|
||||
end
|
||||
|
||||
def get_winners_of(companion_match, current_match)
|
||||
matches = [current_match, companion_match].sort_by(&:position)
|
||||
matches.map(&:winner)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -5,6 +5,9 @@ require 'rails_helper'
|
|||
RSpec.describe MatchesController, type: :controller do
|
||||
before do
|
||||
@match = create(:match, state: :not_started)
|
||||
@amount_of_stages = 2
|
||||
@tournament = create(:stage_tournament, stage_count: @amount_of_stages)
|
||||
@running_playoff_match = @tournament.stages.find_by(level: @amount_of_stages).matches.first
|
||||
@match.match_scores = create_pair(:match_score)
|
||||
end
|
||||
|
||||
|
|
@ -32,7 +35,7 @@ RSpec.describe MatchesController, type: :controller do
|
|||
|
||||
let(:invalid_update) do
|
||||
{
|
||||
state: 'team1_won'
|
||||
state: 'finished'
|
||||
}
|
||||
end
|
||||
|
||||
|
|
@ -55,6 +58,48 @@ RSpec.describe MatchesController, type: :controller do
|
|||
body = deserialize_response response
|
||||
expect(body[:state]).to eq(valid_update[:state])
|
||||
end
|
||||
|
||||
context 'on a running playoff match' do
|
||||
let(:finished) do
|
||||
{
|
||||
state: 'finished'
|
||||
}
|
||||
end
|
||||
|
||||
before(:each) do
|
||||
apply_authentication_headers_for @running_playoff_match.owner
|
||||
end
|
||||
|
||||
before do
|
||||
@running_playoff_match.match_scores.each_with_index do |ms, i|
|
||||
ms.points = i
|
||||
ms.save!
|
||||
end
|
||||
put :update, params: { id: @running_playoff_match.to_param }.merge(finished)
|
||||
@running_playoff_match.reload
|
||||
end
|
||||
|
||||
it 'updates the matches status' do
|
||||
expect(response).to be_successful
|
||||
expect(@running_playoff_match.state).to eq(finished[:state])
|
||||
end
|
||||
|
||||
describe 'updates the match below' do
|
||||
before do
|
||||
@match_below = @tournament.stages.find_by(level: @amount_of_stages - 1).matches
|
||||
.find_by(position: @running_playoff_match.position / 2).reload
|
||||
end
|
||||
|
||||
it 'with the right teams' do
|
||||
expect(@running_playoff_match.winner).to be_a(Team)
|
||||
expect(@match_below.teams).to include(@running_playoff_match.winner)
|
||||
end
|
||||
|
||||
it 'with the right status' do
|
||||
expect(@match_below.state).to eq('not_ready')
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'with invalid params' do
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ FactoryBot.define do
|
|||
# random number generated by blapplications
|
||||
match.match_scores.first.points += 1
|
||||
end
|
||||
state { :team1_won }
|
||||
state { :finished }
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -51,7 +51,7 @@ FactoryBot.define do
|
|||
after(:create) do |match, evaluator|
|
||||
match.match_scores = create_list(:match_score, evaluator.match_scores_count, points: 3)
|
||||
end
|
||||
state { :team1_won }
|
||||
state { :finished }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -32,8 +32,14 @@ FactoryBot.define do
|
|||
:playoff_stage,
|
||||
level: level,
|
||||
match_count: -1,
|
||||
match_type: evaluator.stage_count ? :running_playoff_match : :empty_prepared_playoff_match
|
||||
match_type: level == evaluator.stage_count ? :running_playoff_match : :empty_prepared_playoff_match
|
||||
)
|
||||
tournament.stages.each do |stage|
|
||||
stage.matches.each_with_index do |match, i|
|
||||
match.position = i
|
||||
match.save!
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -10,8 +10,8 @@ RSpec.describe AddGroupStageToTournament do
|
|||
end
|
||||
|
||||
before do
|
||||
@empty_tournament = create(:stage_tournament, stage_count: 0)
|
||||
@group_stage_tournament = create(:group_stage_tournament)
|
||||
@empty_tournament = create(:stageless_tournament)
|
||||
@group_stage_tournament = create(:group_stage_only_tournament, group_count: 0)
|
||||
@group_stage = create(:group_stage)
|
||||
@groups = Hash[1 => create_list(:team, 4), 2 => create_list(:team, 4)].values
|
||||
end
|
||||
|
|
|
|||
|
|
@ -14,10 +14,10 @@ RSpec.describe AddPlayoffsToTournament do
|
|||
end
|
||||
|
||||
before do
|
||||
@group_stage_tournament = create(:stage_tournament)
|
||||
@playoff_stage_tournament = create(:tournament)
|
||||
@full_tournament = create(:stage_tournament, stage_count: 5)
|
||||
@stages = create_list(:stage, 5)
|
||||
@group_stage_tournament = create(:group_stage_only_tournament, group_count: 0)
|
||||
@playoff_stage_tournament = create(:stageless_tournament)
|
||||
@full_tournament = create(:dummy_stage_tournament)
|
||||
@stages = create_list(:stage, 3)
|
||||
end
|
||||
|
||||
context 'PlayoffStageService mocked' do
|
||||
|
|
|
|||
|
|
@ -0,0 +1,43 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
RSpec.describe PopulateMatchBelow do
|
||||
before do
|
||||
@match = create(:match)
|
||||
@objects_to_save = [create(:match), create_list(:match_score, 2)]
|
||||
end
|
||||
|
||||
context 'no exception' do
|
||||
let(:context) do
|
||||
PopulateMatchBelow.call(match: @match)
|
||||
end
|
||||
before do
|
||||
allow(PlayoffStageService)
|
||||
.to receive(:populate_match_below).with(@match)
|
||||
.and_return(@objects_to_save)
|
||||
end
|
||||
|
||||
it 'succeeds' do
|
||||
expect(context).to be_a_success
|
||||
end
|
||||
|
||||
it 'provides the objects to save' do
|
||||
expect(context.object_to_save).to match_array(@objects_to_save)
|
||||
end
|
||||
end
|
||||
|
||||
context 'exception is thrown' do
|
||||
let(:context) do
|
||||
PopulateMatchBelow.call(match: @match)
|
||||
end
|
||||
before do
|
||||
allow(PlayoffStageService)
|
||||
.to receive(:populate_match_below).with(@match)
|
||||
.and_throw('This failed :(')
|
||||
end
|
||||
|
||||
it 'fails' do
|
||||
test = context.failure?
|
||||
expect(test).to eq(true)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -1,13 +1,13 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
RSpec.describe SaveTournamentToDatabase do
|
||||
RSpec.describe SaveApplicationRecordObject do
|
||||
before do
|
||||
@tournament = create(:tournament)
|
||||
end
|
||||
|
||||
context 'save succeeds' do
|
||||
let(:context) do
|
||||
SaveTournamentToDatabase.call(tournament: @tournament)
|
||||
SaveApplicationRecordObject.call(object_to_save: @tournament)
|
||||
end
|
||||
before do
|
||||
expect_any_instance_of(Tournament)
|
||||
|
|
@ -19,13 +19,13 @@ RSpec.describe SaveTournamentToDatabase do
|
|||
end
|
||||
|
||||
it 'provides the tournament' do
|
||||
expect(context.tournament).to eq(@tournament)
|
||||
expect(context.object_to_save).to eq(@tournament)
|
||||
end
|
||||
end
|
||||
|
||||
context 'save fails' do
|
||||
let(:context) do
|
||||
SaveTournamentToDatabase.call(tournament: @tournament)
|
||||
SaveApplicationRecordObject.call(object_to_save: @tournament)
|
||||
end
|
||||
before do
|
||||
expect_any_instance_of(Tournament)
|
||||
|
|
@ -43,6 +43,20 @@ RSpec.describe Match, type: :model do
|
|||
end
|
||||
end
|
||||
|
||||
context '#winner' do
|
||||
it 'returns a winner Team for a decided match' do
|
||||
decided_playoff_match = create(:decided_playoff_match)
|
||||
winning_team_match_score = decided_playoff_match.match_scores.first
|
||||
winning_team_match_score.points = 9999
|
||||
winning_team = winning_team_match_score.team
|
||||
expect(decided_playoff_match.winner).to be winning_team
|
||||
end
|
||||
|
||||
it 'returns nil for an undecided match' do
|
||||
expect(create(:undecided_group_match).winner).to be(nil)
|
||||
end
|
||||
end
|
||||
|
||||
context '#teams' do
|
||||
before do
|
||||
@playoff_match = create(:running_playoff_match)
|
||||
|
|
|
|||
|
|
@ -80,4 +80,108 @@ RSpec.describe PlayoffStageService do
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#populate_match_below' do
|
||||
before :each do
|
||||
@tournament = create(:stage_tournament, stage_count: 2)
|
||||
@match = @tournament.stages.find { |s| s.level == 2 }.matches.first
|
||||
@match.state = :finished
|
||||
@match.match_scores.each_with_index do |ms, i|
|
||||
ms.points = i
|
||||
ms.save
|
||||
end
|
||||
@match.save
|
||||
@companion_match = @tournament.stages.find { |s| s.level == 2 }.matches.second
|
||||
@companion_match.match_scores.each_with_index do |ms, i|
|
||||
ms.points = i
|
||||
ms.save
|
||||
end
|
||||
@match_to_find = @tournament.stages.find { |s| s.level == 1 }.matches.first
|
||||
end
|
||||
|
||||
context 'match below has no match_scores' do
|
||||
before do
|
||||
@match_to_find.match_scores = []
|
||||
@match_to_find.save
|
||||
@test = PlayoffStageService.populate_match_below(@match).first
|
||||
end
|
||||
|
||||
it 'finds the correct match and adds two new match_scores to it' do
|
||||
expect(@match_to_find.teams).to match_array(@match.winner)
|
||||
end
|
||||
|
||||
it 'finds the correct match and changes its state' do
|
||||
expect(@match_to_find.state).to eq('not_ready')
|
||||
end
|
||||
end
|
||||
|
||||
context 'match below has one match_score with the winning team' do
|
||||
before do
|
||||
@match_to_find.match_scores = create_list(:match_score, 1, team: @match.winner)
|
||||
@match_to_find.save
|
||||
@test = PlayoffStageService.populate_match_below(@match).first
|
||||
end
|
||||
|
||||
it 'finds the correct match and adds no match_score' do
|
||||
expect(@test.teams).to match_array(@match.winner)
|
||||
end
|
||||
|
||||
it 'finds the correct match and changes its state' do
|
||||
expect(@test.state).to eq('not_ready')
|
||||
end
|
||||
end
|
||||
|
||||
context 'match below has one match_score with an unknown team' do
|
||||
before do
|
||||
@match_to_find.match_scores = create_list(:match_score, 1, team: create(:team), points: 1337)
|
||||
@match_to_find.save
|
||||
@test = PlayoffStageService.populate_match_below(@match).first
|
||||
end
|
||||
|
||||
it 'finds the correct match and replaces the match_score' do
|
||||
expect(@test.teams).to match_array(@match.winner)
|
||||
expect(@test.match_scores.first.points).to_not be(1337)
|
||||
end
|
||||
|
||||
it 'finds the correct match and changes its state' do
|
||||
expect(@test.state).to eq('not_ready')
|
||||
end
|
||||
end
|
||||
|
||||
context 'match below has one match_score with the correct team' do
|
||||
before do
|
||||
@match_to_find.match_scores = create_list(:match_score, 1, team: @match.winner, points: 42)
|
||||
@match_to_find.save
|
||||
@test = PlayoffStageService.populate_match_below(@match).first
|
||||
end
|
||||
|
||||
it 'finds the correct match and replaces nothing' do
|
||||
expect(@test.teams).to match_array(@match.winner)
|
||||
expect(@test.match_scores.first.points).to be(42)
|
||||
end
|
||||
|
||||
it 'finds the correct match and changes its state' do
|
||||
expect(@test.state).to eq('not_ready')
|
||||
end
|
||||
end
|
||||
|
||||
context 'match below has two match_scores with the correct teams' do
|
||||
before do
|
||||
@companion_match.state = :finished
|
||||
@companion_match.save
|
||||
@match_to_find.match_scores = [create(:match_score, team: @match.winner),
|
||||
create(:match_score, team: @companion_match.winner)]
|
||||
@match_to_find.save
|
||||
@test = PlayoffStageService.populate_match_below(@match).first
|
||||
end
|
||||
|
||||
it 'finds the correct match and replaces nothing' do
|
||||
expect(@test.teams).to match_array([@match.winner, @companion_match.winner])
|
||||
end
|
||||
|
||||
it 'finds the correct match and changes its state' do
|
||||
expect(@test.state).to eq('not_started')
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
Loading…
Reference in New Issue