diff --git a/app/controllers/matches_controller.rb b/app/controllers/matches_controller.rb index d15e841..c65b521 100644 --- a/app/controllers/matches_controller.rb +++ b/app/controllers/matches_controller.rb @@ -1,8 +1,50 @@ # frozen_string_literal: true class MatchesController < ApplicationController + before_action :set_match, only: %i[show update] + before_action :validate_params, only: %i[update] + before_action -> { require_owner! @match.owner }, only: %i[update] + # GET /matches/1 def show - render json: Match.find(params[:id]), include: ['match_scores.points', 'match_scores.team'] + render json: @match, include: ['match_scores.points', 'match_scores.team'] + end + + # 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) + render json: @match + else + render json: @match.errors, status: :unprocessable_entity + end + end + + private + + def validate_params + case match_params['state'] + when 'in_progress' + render json: { error: 'Match can\'t start in this state' }, status: :unprocessable_entity \ + unless @match.not_started? + when 'finished' + render json: { error: 'Match can\'t finish in this state' }, status: :unprocessable_entity \ + unless @match.in_progress? + else + render json: { error: 'Invalid target state' }, status: :unprocessable_entity + end + end + + def set_match + @match = Match.find(params[:id]) + end + + def match_params + params.slice(:state).permit! end end diff --git a/app/models/group.rb b/app/models/group.rb index 1b99378..b9bac6a 100644 --- a/app/models/group.rb +++ b/app/models/group.rb @@ -5,6 +5,8 @@ class Group < ApplicationRecord has_many :matches, dependent: :destroy has_many :group_scores, dependent: :destroy + delegate :owner, to: :stage + def teams matches.map(&:teams).flatten.uniq end diff --git a/app/models/match.rb b/app/models/match.rb index 6c3eee8..8ab9756 100644 --- a/app/models/match.rb +++ b/app/models/match.rb @@ -15,6 +15,10 @@ class Match < ApplicationRecord match_scores.map(&:team).flatten.uniq end + def owner + stage ? stage.owner : group.owner + end + private def stage_xor_group diff --git a/app/models/stage.rb b/app/models/stage.rb index 48ad20d..e1aa980 100644 --- a/app/models/stage.rb +++ b/app/models/stage.rb @@ -5,6 +5,8 @@ class Stage < ApplicationRecord has_many :matches, dependent: :destroy has_many :groups, dependent: :destroy + delegate :owner, to: :tournament + def teams return matches.map(&:teams).flatten.uniq unless matches.size.zero? diff --git a/config/routes.rb b/config/routes.rb index bf135cb..16b79b6 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -6,7 +6,7 @@ Rails.application.routes.draw do sessions: 'overrides/sessions' } - resources :matches, only: %i[show] + resources :matches, only: %i[show update] resources :teams, only: %i[show update] resources :tournaments resources :match_scores, only: %i[show update] diff --git a/spec/controllers/matches_controller_spec.rb b/spec/controllers/matches_controller_spec.rb index 4426afa..10b0a30 100644 --- a/spec/controllers/matches_controller_spec.rb +++ b/spec/controllers/matches_controller_spec.rb @@ -4,7 +4,7 @@ require 'rails_helper' RSpec.describe MatchesController, type: :controller do before do - @match = create(:match) + @match = create(:match, state: :not_started) @match.match_scores = create_pair(:match_score) end @@ -22,4 +22,60 @@ RSpec.describe MatchesController, type: :controller do expect(body[:match_scores].map { |ms| ms[:id] }).to eq(@match.match_scores.map(&:id)) end end + + describe 'POST #update' do + let(:valid_update) do + { + state: 'in_progress' + } + end + + let(:invalid_update) do + { + state: 'team1_won' + } + end + + context 'as owner' do + before(:each) do + apply_authentication_headers_for @match.owner + end + + context 'with valid params' do + it 'updates the match' do + put :update, params: { id: @match.to_param }.merge(valid_update) + @match.reload + expect(response).to be_successful + expect(@match.state).to eq(valid_update[:state]) + end + + it 'renders a response with the updated match' do + put :update, params: { id: @match.to_param }.merge(valid_update) + expect(response).to be_successful + body = deserialize_response response + expect(body[:state]).to eq(valid_update[:state]) + end + end + + context 'with invalid params' do + it 'renders an unprocessable entity response' do + put :update, params: { id: @match.to_param }.merge(invalid_update) + expect(response).to have_http_status(:unprocessable_entity) + end + end + end + + context 'as another user' do + context 'with valid params' do + before(:each) do + apply_authentication_headers_for create(:user) + end + + it 'renders a forbidden error response' do + put :update, params: { id: @match.to_param }.merge(valid_update) + expect(response).to have_http_status(:forbidden) + end + end + end + end end