From b89775896a3cd8e5b89e48f60f99b793f9deb256 Mon Sep 17 00:00:00 2001 From: Morgan Roderick Date: Fri, 30 Oct 2026 07:25:39 +0100 Subject: [PATCH 1/2] fix: fall back to TBA when a physical workshop has no host The calendar serializer raised NoMethodError for a physical workshop without a host. The admin destroy_host action can produce one: it updates WorkshopSponsor, so the Workshop validation from #3059 does not run. Guard the host name and address and pin the behaviour with a spec. --- app/serializers/workshop_calendar.rb | 4 ++-- spec/serializers/workshop_calendar_spec.rb | 11 +++++++++++ 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/app/serializers/workshop_calendar.rb b/app/serializers/workshop_calendar.rb index 196a9e68f..955a21c06 100644 --- a/app/serializers/workshop_calendar.rb +++ b/app/serializers/workshop_calendar.rb @@ -25,7 +25,7 @@ def calendar def setup_event calendar.event do |e| - configure(e, host_name: workshop.host.name) + configure(e, host_name: workshop.host&.name || 'TBA') e.location = address(workshop) e.description = I18n.t('workshop.calendar.description', invitation_link: invitation_url) @@ -55,7 +55,7 @@ def configure(event, host_name:) end def address(workshop) - AddressPresenter.new(workshop.host.address).to_s + workshop.host&.address ? AddressPresenter.new(workshop.host.address).to_s : 'TBA' end def start_and_end_time(event) diff --git a/spec/serializers/workshop_calendar_spec.rb b/spec/serializers/workshop_calendar_spec.rb index 05f85844f..be3e1e100 100644 --- a/spec/serializers/workshop_calendar_spec.rb +++ b/spec/serializers/workshop_calendar_spec.rb @@ -53,6 +53,17 @@ end end + context 'with a physical workshop that has no host' do + let(:workshop) { Fabricate(:workshop_no_sponsor, virtual: false) } + + it 'falls back to TBA for host name and address' do + event = calendar.events.first + + expect(event.summary.to_s).to eq('codebar @ TBA') + expect(event.location.to_s).to eq('TBA') + end + end + context 'with a virtual workshop' do let(:workshop) { Fabricate(:virtual_workshop) } From c53cd51568c049b1b31b49399fc0621f699ac133 Mon Sep 17 00:00:00 2001 From: Morgan Roderick Date: Fri, 30 Oct 2026 08:19:48 +0100 Subject: [PATCH 2/2] test: pin TBA fallbacks and hostless calendar output Covers the review finding on PR #3064: the address fallback also applies when a host exists but its address record is gone, a state the nil-host example never reached. Asserts the iCalendar output carries the fallback end to end, and the organizer and description for the hostless case. All three examples fail against the pre-guard serializer. --- spec/serializers/workshop_calendar_spec.rb | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/spec/serializers/workshop_calendar_spec.rb b/spec/serializers/workshop_calendar_spec.rb index be3e1e100..6ac84a34c 100644 --- a/spec/serializers/workshop_calendar_spec.rb +++ b/spec/serializers/workshop_calendar_spec.rb @@ -61,6 +61,28 @@ expect(event.summary.to_s).to eq('codebar @ TBA') expect(event.location.to_s).to eq('TBA') + expect(event.organizer.to_s).to eq(workshop.chapter.email) + expect(event.description).to include(invitation_url) + end + + it 'serialises the fallback into the iCalendar output' do + expect(described_class.new(workshop, invitation_url).ical).to include('TBA') + end + end + + context 'with a host whose address record has been removed' do + let(:workshop) do + Fabricate(:workshop).tap do |w| + w.host.address.destroy + w.reload + end + end + + it 'keeps the host name and falls back to TBA for the address' do + event = calendar.events.first + + expect(event.summary.to_s).to eq("codebar @ #{workshop.host.name}") + expect(event.location.to_s).to eq('TBA') end end