Fix final match completion after group stage

This commit is contained in:
Daniel Schädler 2026-04-12 18:07:08 +02:00
parent 1c275f49f7
commit 6d21dad5d7
5 changed files with 30 additions and 32 deletions

View File

@ -70,7 +70,9 @@ class PlayoffStageService
# @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 }
next_stage = current_stage.tournament.stages
.select { |stage| stage.matches.any? && stage.level < current_stage.level }
.max_by(&:level)
# return if next stage does not exist (there are no matches after the finale)
return [] if next_stage.nil?

View File

@ -7,7 +7,6 @@ require 'time'
module TurniereE2E
class ScenarioRunner
DEFAULT_PASSWORD = 'password123'.freeze
FINAL_FINISH_ERROR = 'Moving Team one stage down failed'
attr_reader :client
@ -65,12 +64,7 @@ module TurniereE2E
return result(session: session, tournament: tournament, checkpoints: checkpoints, stop_at: stop_at) if stop_at == :before_final_finish_attempt
final_match = final_match_of!(tournament)
finish_attempt = play_match!(
final_match,
winner_name: preferred_playoff_winner_name(final_match),
expected_finish_status: 422,
expected_finish_error: FINAL_FINISH_ERROR
)
finish_attempt = play_match!(final_match, winner_name: preferred_playoff_winner_name(final_match))
tournament = fetch_tournament!(tournament.fetch(:id))
checkpoints[:final_finish_attempted] = {
finish_attempt: finish_attempt,
@ -103,12 +97,7 @@ module TurniereE2E
return result(session: session, tournament: tournament, checkpoints: checkpoints, stop_at: stop_at) if stop_at == :before_final_finish_attempt
final_match = final_match_of!(tournament)
finish_attempt = play_match!(
final_match,
winner_name: preferred_playoff_winner_name(final_match),
expected_finish_status: 422,
expected_finish_error: FINAL_FINISH_ERROR
)
finish_attempt = play_match!(final_match, winner_name: preferred_playoff_winner_name(final_match))
tournament = fetch_tournament!(tournament.fetch(:id))
checkpoints[:final_finish_attempted] = {
finish_attempt: finish_attempt,

View File

@ -21,7 +21,7 @@ RSpec.describe 'Tournament lifecycle HTTP E2E' do
)
end
it 'drives the main 4x4 group-stage use case over HTTP and preserves the current final-finish behavior' do
it 'drives the main 4x4 group-stage use case over HTTP through a finished final' do
result = runner.run_group_stage_main_usecase
expect(result.dig(:checkpoints, :created, :playoff_teams_amount)).to eq(8)
@ -35,11 +35,12 @@ RSpec.describe 'Tournament lifecycle HTTP E2E' do
expect(playoff_entry_stage[:match_states].count).to eq(4)
final_attempt = result.dig(:checkpoints, :final_finish_attempted, :finish_attempt)
expect(final_attempt[:status]).to eq(422)
expect(final_attempt.dig(:body, :error)).to eq('Moving Team one stage down failed')
expect(final_attempt[:status]).to eq(200)
expect(result.dig(:checkpoints, :final_finish_attempted, :tournament, :stage_summaries)
.find { |stage| stage[:level] == 0 }[:match_states]).to eq(['finished'])
end
it 'drives the 3-group intermediate-round path over HTTP and preserves the current advancing behavior' do
it 'drives the 3-group intermediate-round path over HTTP through a finished final' do
result = runner.run_group_stage_intermediate_round
created = result.fetch(:checkpoints).fetch(:created)
@ -55,8 +56,9 @@ RSpec.describe 'Tournament lifecycle HTTP E2E' do
expect(playoff_entry_stage[:match_states].count('not_started')).to eq(1)
final_attempt = result.dig(:checkpoints, :final_finish_attempted, :finish_attempt)
expect(final_attempt[:status]).to eq(422)
expect(final_attempt.dig(:body, :error)).to eq('Moving Team one stage down failed')
expect(final_attempt[:status]).to eq(200)
expect(result.dig(:checkpoints, :final_finish_attempted, :tournament, :stage_summaries)
.find { |stage| stage[:level] == 0 }[:match_states]).to eq(['finished'])
end
{

View File

@ -20,18 +20,6 @@ The reusable scenario script supports this through:
Frontend tests can use the same script against a live backend as long as those credentials point to a confirmed account on that backend.
## Group-stage tournaments cannot finish the playoff final through the normal match endpoint
Observed and covered by `spec/e2e/http/tournament_lifecycle_spec.rb` and `script/e2e_scenarios.rb`:
- in tournaments that started with a group stage, the playoff final can be started and scored
- but `PATCH /matches/:id` with `state=finished` on that final returns:
- status `422`
- error `"Moving Team one stage down failed"`
- the final remains `in_progress`
This appears to happen because playoff stages count down to level `0`, while the old group stage still exists at level `-1`. The final-finish flow looks for a "stage below" using `current_stage.level - 1`, which resolves to the group stage for finals in group-stage tournaments. The E2E suite preserves this current behavior instead of changing backend code.
## Group stage to playoff conversion with 3 groups and `playoff_teams_amount = 4`
Observed and covered by `spec/e2e/http/tournament_lifecycle_spec.rb` and `script/e2e_scenarios.rb`:

View File

@ -212,5 +212,22 @@ RSpec.describe PlayoffStageService do
expect(@test.state).to eq('not_started')
end
end
context 'when the current match is the final of a tournament with a group stage' do
before do
@tournament = create(:group_stage_tournament, stage_count: 1)
@final_match = @tournament.stages.reject { |stage| stage.level == -1 }.max_by(&:level).matches.first
@final_match.state = :finished
@final_match.match_scores.each_with_index do |match_score, index|
match_score.points = index
match_score.save!
end
@final_match.save!
end
it 'does not treat the group stage as a lower playoff stage' do
expect(PlayoffStageService.populate_match_below(@final_match)).to eq([])
end
end
end
end