feat(matches): stop in-progress matches
This commit is contained in:
parent
0141d78e4e
commit
df227f9397
|
|
@ -45,12 +45,8 @@ class MatchesController < ApplicationController
|
||||||
Match.transaction do
|
Match.transaction do
|
||||||
if @match.update(match_params)
|
if @match.update(match_params)
|
||||||
handle_match_end if new_state == 'finished'
|
handle_match_end if new_state == 'finished'
|
||||||
if @match.group_match? && new_state == 'in_progress'
|
handle_match_stop if new_state == 'not_started'
|
||||||
group = @match.group
|
update_group_scores if @match.group_match? && %w[in_progress not_started].include?(new_state)
|
||||||
unless UpdateGroupsGroupScoresAndSave.call(group: group).success?
|
|
||||||
logger.warn "Updating groups group score failed for #{group}"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
render json: @match
|
render json: @match
|
||||||
synced_tournament = @match.tournament
|
synced_tournament = @match.tournament
|
||||||
|
|
@ -113,6 +109,19 @@ class MatchesController < ApplicationController
|
||||||
raise ActiveRecord::Rollback
|
raise ActiveRecord::Rollback
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def handle_match_stop
|
||||||
|
@match.match_scores.each do |match_score|
|
||||||
|
match_score.update!(points: 0, hidden_points: 0)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def update_group_scores
|
||||||
|
group = @match.group
|
||||||
|
return if UpdateGroupsGroupScoresAndSave.call(group: group).success?
|
||||||
|
|
||||||
|
logger.warn "Updating groups group score failed for #{group}"
|
||||||
|
end
|
||||||
|
|
||||||
def validate_params
|
def validate_params
|
||||||
case match_params['state']
|
case match_params['state']
|
||||||
when 'in_progress'
|
when 'in_progress'
|
||||||
|
|
@ -121,6 +130,9 @@ class MatchesController < ApplicationController
|
||||||
when 'finished'
|
when 'finished'
|
||||||
render json: { error: 'Match can\'t finish in this state' }, status: :unprocessable_content \
|
render json: { error: 'Match can\'t finish in this state' }, status: :unprocessable_content \
|
||||||
unless @match.in_progress?
|
unless @match.in_progress?
|
||||||
|
when 'not_started'
|
||||||
|
render json: { error: 'Match can\'t stop in this state' }, status: :unprocessable_content \
|
||||||
|
unless @match.in_progress?
|
||||||
else
|
else
|
||||||
render json: { error: 'Invalid target state' }, status: :unprocessable_content
|
render json: { error: 'Invalid target state' }, status: :unprocessable_content
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -233,6 +233,21 @@ RSpec.describe MatchesController, type: :controller do
|
||||||
expect(body[:state]).to eq(valid_update[:state])
|
expect(body[:state]).to eq(valid_update[:state])
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it 'stops an in-progress match and resets its scores' do
|
||||||
|
@match.update!(state: :in_progress)
|
||||||
|
@match.match_scores.each do |match_score|
|
||||||
|
match_score.update!(points: 7, hidden_points: 5)
|
||||||
|
end
|
||||||
|
|
||||||
|
expect(TournamentLiveBroadcast).to receive(:call).with(@match.tournament.id)
|
||||||
|
put :update, params: { id: @match.to_param, state: 'not_started' }
|
||||||
|
|
||||||
|
expect(response).to be_successful
|
||||||
|
expect(@match.reload.state).to eq('not_started')
|
||||||
|
expect(@match.match_scores.reload.map(&:points)).to all(eq(0))
|
||||||
|
expect(@match.match_scores.reload.map(&:hidden_points)).to all(eq(0))
|
||||||
|
end
|
||||||
|
|
||||||
context 'on a running playoff match' do
|
context 'on a running playoff match' do
|
||||||
let(:finished) do
|
let(:finished) do
|
||||||
{
|
{
|
||||||
|
|
@ -385,6 +400,14 @@ RSpec.describe MatchesController, type: :controller do
|
||||||
expect(response).to have_http_status(:unprocessable_content)
|
expect(response).to have_http_status(:unprocessable_content)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context 'with stop params on a not-started match' do
|
||||||
|
it 'renders an unprocessable entity response' do
|
||||||
|
put :update, params: { id: @match.to_param, state: 'not_started' }
|
||||||
|
expect(response).to have_http_status(:unprocessable_content)
|
||||||
|
expect(deserialize_response(response)[:error]).to eq("Match can't stop in this state")
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'as another user' do
|
context 'as another user' do
|
||||||
|
|
@ -401,6 +424,34 @@ RSpec.describe MatchesController, type: :controller do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context 'on a running group match' do
|
||||||
|
let(:tournament) { create(:group_stage_tournament, group_count: 1, match_factory: :filled_group_match) }
|
||||||
|
let(:group) { tournament.stages.find_by(level: -1).groups.first }
|
||||||
|
let(:group_match) { group.matches.first }
|
||||||
|
|
||||||
|
before do
|
||||||
|
apply_authentication_headers_for tournament.owner
|
||||||
|
group_match.update!(state: :in_progress)
|
||||||
|
group_match.match_scores.order(:id).each_with_index do |match_score, index|
|
||||||
|
match_score.update!(points: index.zero? ? 9 : 2, hidden_points: index.zero? ? 4 : 1)
|
||||||
|
end
|
||||||
|
UpdateGroupsGroupScoresAndSave.call(group: group)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'stops the match, clears scores, and recalculates group scores' do
|
||||||
|
put :update, params: { id: group_match.to_param, state: 'not_started' }
|
||||||
|
|
||||||
|
expect(response).to be_successful
|
||||||
|
expect(group_match.reload.state).to eq('not_started')
|
||||||
|
expect(group_match.match_scores.reload.map(&:points)).to all(eq(0))
|
||||||
|
expect(group_match.match_scores.reload.map(&:hidden_points)).to all(eq(0))
|
||||||
|
involved_group_scores = group.group_scores.reload.select { |score| group_match.teams.include?(score.team) }
|
||||||
|
expect(involved_group_scores.map(&:group_points)).to all(eq(0))
|
||||||
|
expect(involved_group_scores.map(&:scored_points)).to all(eq(0))
|
||||||
|
expect(involved_group_scores.map(&:received_points)).to all(eq(0))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
context 'on a playoff match that isn\'t ready yet' do
|
context 'on a playoff match that isn\'t ready yet' do
|
||||||
let(:invalid_update) do
|
let(:invalid_update) do
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -270,11 +270,14 @@ RSpec.describe 'Backend API surface HTTP E2E' do
|
||||||
started_match = anonymous_client.get("/matches/#{match.fetch(:id)}")
|
started_match = anonymous_client.get("/matches/#{match.fetch(:id)}")
|
||||||
score_to_update = started_match.fetch(:json).fetch(:match_scores).first
|
score_to_update = started_match.fetch(:json).fetch(:match_scores).first
|
||||||
score_show = anonymous_client.get("/match_scores/#{score_to_update.fetch(:id)}")
|
score_show = anonymous_client.get("/match_scores/#{score_to_update.fetch(:id)}")
|
||||||
score_update = owner_client.patch("/match_scores/#{score_to_update.fetch(:id)}", body: { points: 7 })
|
score_update = owner_client.patch("/match_scores/#{score_to_update.fetch(:id)}", body: { points: 7, hidden_points: 11 })
|
||||||
forbidden_score_update = other_client.patch("/match_scores/#{score_to_update.fetch(:id)}", body: { points: 3 })
|
forbidden_score_update = other_client.patch("/match_scores/#{score_to_update.fetch(:id)}", body: { points: 3 })
|
||||||
unauthenticated_score_update = anonymous_client.patch("/match_scores/#{score_to_update.fetch(:id)}", body: { points: 3 })
|
unauthenticated_score_update = anonymous_client.patch("/match_scores/#{score_to_update.fetch(:id)}", body: { points: 3 })
|
||||||
bet_while_running = owner_client.post("/matches/#{match.fetch(:id)}/bets", body: { team: team.fetch(:id) })
|
bet_while_running = owner_client.post("/matches/#{match.fetch(:id)}/bets", body: { team: team.fetch(:id) })
|
||||||
restart_started_match = owner_client.patch("/matches/#{match.fetch(:id)}", body: { state: 'in_progress' })
|
restart_started_match = owner_client.patch("/matches/#{match.fetch(:id)}", body: { state: 'in_progress' })
|
||||||
|
stop_started_match = owner_client.patch("/matches/#{match.fetch(:id)}", body: { state: 'not_started' })
|
||||||
|
stopped_match = anonymous_client.get("/matches/#{match.fetch(:id)}")
|
||||||
|
restop_match = owner_client.patch("/matches/#{match.fetch(:id)}", body: { state: 'not_started' })
|
||||||
playoff_stats = anonymous_client.get("/tournaments/#{playoff_only.fetch(:id)}/statistics")
|
playoff_stats = anonymous_client.get("/tournaments/#{playoff_only.fetch(:id)}/statistics")
|
||||||
tied_playoff_match = playoff_only.fetch(:stages)
|
tied_playoff_match = playoff_only.fetch(:stages)
|
||||||
.flat_map { |stage| stage.fetch(:matches) }
|
.flat_map { |stage| stage.fetch(:matches) }
|
||||||
|
|
@ -349,12 +352,21 @@ RSpec.describe 'Backend API surface HTTP E2E' do
|
||||||
expect(score_show[:status]).to eq(200)
|
expect(score_show[:status]).to eq(200)
|
||||||
expect(score_update[:status]).to eq(200)
|
expect(score_update[:status]).to eq(200)
|
||||||
expect(score_update.dig(:json, :points)).to eq(7)
|
expect(score_update.dig(:json, :points)).to eq(7)
|
||||||
|
expect(score_update.dig(:json, :hidden_points)).to eq(11)
|
||||||
expect(forbidden_score_update[:status]).to eq(403)
|
expect(forbidden_score_update[:status]).to eq(403)
|
||||||
expect(unauthenticated_score_update[:status]).to eq(401)
|
expect(unauthenticated_score_update[:status]).to eq(401)
|
||||||
expect(bet_while_running[:status]).to eq(422)
|
expect(bet_while_running[:status]).to eq(422)
|
||||||
expect(bet_while_running.dig(:json, :error)).to eq('Betting is not allowed while match is in_progress')
|
expect(bet_while_running.dig(:json, :error)).to eq('Betting is not allowed while match is in_progress')
|
||||||
expect(restart_started_match[:status]).to eq(422)
|
expect(restart_started_match[:status]).to eq(422)
|
||||||
expect(restart_started_match.dig(:json, :error)).to eq("Match can't start in this state")
|
expect(restart_started_match.dig(:json, :error)).to eq("Match can't start in this state")
|
||||||
|
expect(stop_started_match[:status]).to eq(200)
|
||||||
|
expect(stop_started_match.dig(:json, :state)).to eq('not_started')
|
||||||
|
expect(stopped_match[:status]).to eq(200)
|
||||||
|
expect(stopped_match.dig(:json, :state)).to eq('not_started')
|
||||||
|
expect(stopped_match.dig(:json, :match_scores).map { |score| score.fetch(:points) }).to all(eq(0))
|
||||||
|
expect(stopped_match.dig(:json, :match_scores).map { |score| score.fetch(:hidden_points) }).to all(eq(0))
|
||||||
|
expect(restop_match[:status]).to eq(422)
|
||||||
|
expect(restop_match.dig(:json, :error)).to eq("Match can't stop in this state")
|
||||||
|
|
||||||
expect(playoff_stats[:status]).to eq(501)
|
expect(playoff_stats[:status]).to eq(501)
|
||||||
expect(playoff_stats[:json]).to eq({})
|
expect(playoff_stats[:json]).to eq({})
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue