From 0c8eef2a3667655dcdc0945479abf0d49cfca784 Mon Sep 17 00:00:00 2001 From: Morgan Roderick Date: Sat, 8 Aug 2026 17:40:19 +0200 Subject: [PATCH] fix: events coach/student 500 when member already has an invitation `Invitation.create_or_find_by` in `find_invitation_and_redirect_to_event` returns an unsaved record (id/token nil) when the member already has an invitation for the event+role with attending nil, because the uniqueness validation fails but create_or_find_by only rescues RecordNotUnique. The subsequent `redirect_to event_invitation_path` then raises a UrlGenerationError for a missing token. Fall back to the existing persisted invitation when create_or_find_by returns an unpersisted record. Same fix as #2791 applied to the events path. Fixes #2794 --- app/controllers/events_controller.rb | 5 +- spec/controllers/events_controller_spec.rb | 72 ++++++++++++++++++++++ 2 files changed, 75 insertions(+), 2 deletions(-) diff --git a/app/controllers/events_controller.rb b/app/controllers/events_controller.rb index 6506354de..543c0d7c7 100644 --- a/app/controllers/events_controller.rb +++ b/app/controllers/events_controller.rb @@ -63,8 +63,9 @@ def latest_model_updated def find_invitation_and_redirect_to_event(role) set_event - @invitation = Invitation.create_or_find_by(event: @event, member: current_user, role: role) - redirect_to event_invitation_path(@event, @invitation) + invitation = Invitation.create_or_find_by(event: @event, member: current_user, role: role) + invitation = Invitation.find_by(event: @event, member: current_user, role: role) unless invitation.persisted? + redirect_to event_invitation_path(@event, invitation) end def set_event diff --git a/spec/controllers/events_controller_spec.rb b/spec/controllers/events_controller_spec.rb index 0438ad0db..29f639fb1 100644 --- a/spec/controllers/events_controller_spec.rb +++ b/spec/controllers/events_controller_spec.rb @@ -15,6 +15,78 @@ end end + describe 'GET #student' do + let(:member) { Fabricate(:member) } + let(:event) { Fabricate(:event) } + + before { login(member) } + + context 'when the member already has an invitation for the event and role with attending nil' do + let!(:invitation) do + Fabricate(:invitation, event: event, member: member, role: 'Student', attending: nil) + end + + it 'redirects to the existing invitation page' do + get :student, params: { event_id: event.slug } + + expect(response).to redirect_to(event_invitation_path(event, invitation)) + end + + it 'does not create a new invitation' do + expect do + get :student, params: { event_id: event.slug } + end.not_to change(Invitation, :count) + end + end + + context 'when the member does not have an invitation for the event and role' do + it 'creates a new invitation and redirects' do + expect do + get :student, params: { event_id: event.slug } + end.to change(Invitation, :count).by(1) + + invitation = Invitation.last + expect(response).to redirect_to(event_invitation_path(event, invitation)) + end + end + end + + describe 'GET #coach' do + let(:member) { Fabricate(:member) } + let(:event) { Fabricate(:event) } + + before { login(member) } + + context 'when the member already has a coach invitation for the event with attending nil' do + let!(:invitation) do + Fabricate(:coach_invitation, event: event, member: member, attending: nil) + end + + it 'redirects to the existing invitation page' do + get :coach, params: { event_id: event.slug } + + expect(response).to redirect_to(event_invitation_path(event, invitation)) + end + + it 'does not create a new invitation' do + expect do + get :coach, params: { event_id: event.slug } + end.not_to change(Invitation, :count) + end + end + + context 'when the member does not have a coach invitation for the event' do + it 'creates a new coach invitation and redirects' do + expect do + get :coach, params: { event_id: event.slug } + end.to change(Invitation, :count).by(1) + + invitation = Invitation.last + expect(response).to redirect_to(event_invitation_path(event, invitation)) + end + end + end + describe '#past' do before { Fabricate(:event, date_and_time: 2.weeks.ago) }