From cdfa93366431b181512b06932b4157c15e17ef37 Mon Sep 17 00:00:00 2001 From: moveson Date: Mon, 24 Aug 2026 14:34:26 -0600 Subject: [PATCH 1/2] Add failing specs for event course-organization consistency Covers the cross-organization course validation, the factory default, the persisted-change rejection, and the nil-course guard from #2234. Co-Authored-By: Claude Fable 5 --- spec/models/event_spec.rb | 42 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/spec/models/event_spec.rb b/spec/models/event_spec.rb index bc263e271..98b097a68 100644 --- a/spec/models/event_spec.rb +++ b/spec/models/event_spec.rb @@ -126,6 +126,48 @@ end end + describe "course organization consistency" do + it "is invalid when the course belongs to a different organization than the event group" do + event = build(:event, :with_short_name, course: courses(:d30_12m_course), event_group: event_groups(:hardrock_2016)) + expect(event).not_to be_valid + expect(event.errors[:course_id]).to include("must belong to the same organization as the event group") + end + + it "is valid when the course belongs to the event group's organization" do + event = build(:event, :with_short_name, course: courses(:hardrock_ccw), event_group: event_groups(:hardrock_2016)) + expect(event).to be_valid + end + + it "gives factory-built events a course in the event group's organization" do + event = build(:event) + expect(event.course.organization).to eq(event.event_group.organization) + end + + context "when changing a persisted event's course to another organization's course" do + it "is invalid and does not persist the change" do + event = events(:hardrock_2016) + original_course = event.course + + response = event.update(course_id: courses(:d30_12m_course).id) + + expect(response).to eq(false) + expect(event.errors[:course_id]).to include("must belong to the same organization as the event group") + expect(event.reload.course).to eq(original_course) + end + end + end + + describe "clearing the course on a persisted event" do + it "is invalid rather than raising from the course-change conforming" do + event = events(:hardrock_2016) + event.course_id = nil + + expect { event.valid? }.not_to raise_error + expect(event).not_to be_valid + expect(event.errors[:course]).to include("must exist") + end + end + describe "methods that produce lap_splits and time_points" do let(:event) { build_stubbed(:event, laps_required: laps_required) } let(:laps_required) { 2 } From c76cc146d85a63ba2441a6353e507f7504a74084 Mon Sep 17 00:00:00 2001 From: moveson Date: Mon, 24 Aug 2026 14:53:48 -0600 Subject: [PATCH 2/2] Validate that an event's course belongs to the event group's organization Courses were originally organization-less, and relic events pointing at another organization's course still exist on staging (#2235). Such events break the event edit form, whose course selector offers only the event group organization's courses and silently blanks course_id. Validate the consistency so no path (UI, API, imports, duplication) can create new relics. Also bail out of conform_changed_course when the course is blank, so a nil course_id yields the belongs_to presence validation's 422 instead of an ArgumentError 500 (the guard from #2234). The event factory now builds its course in the event group's organization, and specs that explicitly paired independently-built courses and event groups thread a single organization through. Resolves #2235 Co-Authored-By: Claude Fable 5 --- app/models/event.rb | 10 +++++ .../api/v1/aid_stations_controller_spec.rb | 24 ++++++------ .../api/v1/events_controller_spec.rb | 2 +- .../api/v1/splits_controller_spec.rb | 38 ++++++++++--------- spec/factories/event.rb | 2 +- spec/lib/etl/importer_spec.rb | 6 +-- spec/lib/etl/loaders/upsert_strategy_spec.rb | 2 +- spec/models/aid_station_spec.rb | 4 +- spec/models/split_spec.rb | 4 +- .../interactors/change_event_course_spec.rb | 6 +-- .../destroy_effort_split_times_spec.rb | 26 +++++++------ .../match_raw_times_to_split_times_spec.rb | 2 +- .../match_time_records_to_split_times_spec.rb | 2 +- ...sert_split_times_from_raw_time_row_spec.rb | 2 +- 14 files changed, 73 insertions(+), 57 deletions(-) diff --git a/app/models/event.rb b/app/models/event.rb index 94d15af81..fd7bde617 100644 --- a/app/models/event.rb +++ b/app/models/event.rb @@ -35,6 +35,7 @@ class Event < ApplicationRecord validates :scheduled_start_time, :laps_required, presence: true validates :short_name, uniqueness: { case_sensitive: false, scope: :event_group_id } validate :course_is_consistent + validate :course_matches_event_group_organization before_validation :add_default_results_template before_validation :conform_changed_course @@ -95,6 +96,13 @@ def course_is_consistent errors.add(:course_id, "does not reconcile with one or more splits") end + def course_matches_event_group_organization + return unless course && event_group&.organization_id + return if course.organization_id == event_group.organization_id + + errors.add(:course_id, "must belong to the same organization as the event group") + end + def to_s slug end @@ -164,6 +172,8 @@ def add_default_results_template def conform_changed_course return unless persisted? && course_id_changed? + # Let the belongs_to presence validation reject a blank course_id + return if course.nil? response = Interactors::ChangeEventCourse.perform!(event: self, new_course: course) response.errors.each { |error| errors.add(:base, error[:title]) } diff --git a/spec/controllers/api/v1/aid_stations_controller_spec.rb b/spec/controllers/api/v1/aid_stations_controller_spec.rb index 8b859371b..69db1c040 100644 --- a/spec/controllers/api/v1/aid_stations_controller_spec.rb +++ b/spec/controllers/api/v1/aid_stations_controller_spec.rb @@ -3,7 +3,7 @@ RSpec.describe Api::V1::AidStationsController do let(:course) { create(:course) } let(:split) { create(:split, course: course) } - let(:event) { create(:event, course: course) } + let(:event) { create(:event, course: course, event_group: create(:event_group, organization: course.organization)) } let(:aid_station) { create(:aid_station, split: split, event: event) } let(:type) { "aid_stations" } @@ -12,7 +12,7 @@ via_login_and_jwt do context "when an existing aid_station.id is provided" do - let(:params) { {id: aid_station} } + let(:params) { { id: aid_station } } it "returns a successful 200 response" do make_request @@ -21,18 +21,18 @@ it "returns data of a single aid_station" do make_request - parsed_response = JSON.parse(response.body) + parsed_response = response.parsed_body expect(parsed_response["data"]["id"].to_i).to eq(aid_station.id) expect(response.body).to be_jsonapi_response_for(type) end end - context "if the aid_station does not exist" do - let(:params) { {id: 0} } + context "when the aid_station does not exist" do + let(:params) { { id: 0 } } it "returns an error" do make_request - parsed_response = JSON.parse(response.body) + parsed_response = response.parsed_body expect(parsed_response["errors"]).to include(/not found/) expect(response.status).to eq(404) end @@ -45,25 +45,25 @@ via_login_and_jwt do context "when provided data is valid" do - let(:params) { {data: {type: type, attributes: {split_id: split.id, event_id: event.id}}} } + let(:params) { { data: { type: type, attributes: { split_id: split.id, event_id: event.id } } } } it "returns a successful json response" do make_request expect(response.body).to be_jsonapi_response_for(type) - parsed_response = JSON.parse(response.body) + parsed_response = response.parsed_body expect(parsed_response["data"]["id"]).not_to be_nil expect(response.status).to eq(201) end it "creates a aid_station record" do - expect { make_request }.to change { AidStation.count }.by(1) + expect { make_request }.to change(AidStation, :count).by(1) end end end end describe "#destroy" do - subject(:make_request) { delete :destroy, params: {id: aid_station_id} } + subject(:make_request) { delete :destroy, params: { id: aid_station_id } } via_login_and_jwt do context "when the record exists" do @@ -76,7 +76,7 @@ end it "destroys the aid_station record" do - expect { make_request }.to change { AidStation.count }.by(-1) + expect { make_request }.to change(AidStation, :count).by(-1) end end @@ -85,7 +85,7 @@ it "returns an error if the aid_station does not exist" do make_request - parsed_response = JSON.parse(response.body) + parsed_response = response.parsed_body expect(parsed_response["errors"]).to include(/not found/) expect(response.status).to eq(404) end diff --git a/spec/controllers/api/v1/events_controller_spec.rb b/spec/controllers/api/v1/events_controller_spec.rb index c5c15cf22..f4327de0b 100644 --- a/spec/controllers/api/v1/events_controller_spec.rb +++ b/spec/controllers/api/v1/events_controller_spec.rb @@ -3,7 +3,7 @@ RSpec.describe Api::V1::EventsController do let(:type) { "events" } let(:event) { create(:event, course: course, event_group: event_group) } - let(:course) { create(:course) } + let(:course) { create(:course, organization: event_group.organization) } let(:event_group) { create(:event_group) } let(:parsed_response) { response.parsed_body } diff --git a/spec/controllers/api/v1/splits_controller_spec.rb b/spec/controllers/api/v1/splits_controller_spec.rb index 77d43db18..3cc472cf0 100644 --- a/spec/controllers/api/v1/splits_controller_spec.rb +++ b/spec/controllers/api/v1/splits_controller_spec.rb @@ -10,7 +10,7 @@ via_login_and_jwt do context "when an existing split.id is provided" do - let(:params) { {id: split} } + let(:params) { { id: split } } it "returns a successful 200 response" do make_request @@ -19,18 +19,18 @@ it "returns data of a single split" do make_request - parsed_response = JSON.parse(response.body) + parsed_response = response.parsed_body expect(parsed_response["data"]["id"].to_i).to eq(split.id) expect(response.body).to be_jsonapi_response_for(type) end end - context "if the split does not exist" do - let(:params) { {id: 0} } + context "when the split does not exist" do + let(:params) { { id: 0 } } it "returns an error" do make_request - parsed_response = JSON.parse(response.body) + parsed_response = response.parsed_body expect(parsed_response["errors"]).to include(/not found/) expect(response.status).to eq(404) end @@ -40,25 +40,26 @@ describe "#create" do subject(:make_request) { post :create, params: params } - let(:params) { {data: {type: "splits", attributes: attributes}} } + + let(:params) { { data: { type: "splits", attributes: attributes } } } via_login_and_jwt do context "when provided data is valid" do let(:attributes) do - {base_name: "Test Split", course_id: course.id, distance_from_start: 100, - kind: "intermediate", sub_split_bitkey: 1} + { base_name: "Test Split", course_id: course.id, distance_from_start: 100, + kind: "intermediate", sub_split_bitkey: 1 } end it "returns a successful json response" do make_request expect(response.body).to be_jsonapi_response_for(type) - parsed_response = JSON.parse(response.body) + parsed_response = response.parsed_body expect(parsed_response["data"]["id"]).not_to be_nil expect(response.status).to eq(201) end it "creates a split record" do - expect { make_request }.to change { Split.count }.by(1) + expect { make_request }.to change(Split, :count).by(1) end end end @@ -66,8 +67,9 @@ describe "#update" do subject(:make_request) { put :update, params: params } - let(:params) { {id: split_id, data: {type: type, attributes: attributes}} } - let(:attributes) { {base_name: "Updated Split Name", latitude: 40, longitude: -105, elevation: 2000} } + + let(:params) { { id: split_id, data: { type: type, attributes: attributes } } } + let(:attributes) { { base_name: "Updated Split Name", latitude: 40, longitude: -105, elevation: 2000 } } via_login_and_jwt do context "when the split exists" do @@ -93,7 +95,7 @@ it "returns an error if the split does not exist" do make_request - parsed_response = JSON.parse(response.body) + parsed_response = response.parsed_body expect(parsed_response["errors"]).to include(/not found/) expect(response.status).to eq(404) end @@ -102,7 +104,7 @@ end describe "#destroy" do - subject(:make_request) { delete :destroy, params: {id: split_id} } + subject(:make_request) { delete :destroy, params: { id: split_id } } via_login_and_jwt do context "when the record exists" do @@ -115,7 +117,7 @@ end it "destroys the split record" do - expect { make_request }.to change { Split.count }.by(-1) + expect { make_request }.to change(Split, :count).by(-1) end end @@ -123,11 +125,11 @@ let(:split_id) { split.id } it "returns an error message" do - event = create(:event, course: course) + event = create(:event, course: course, event_group: create(:event_group, organization: course.organization)) effort = create(:effort, event: event) create(:split_time, split: split, effort: effort) make_request - parsed_response = JSON.parse(response.body) + parsed_response = response.parsed_body expect(parsed_response["errors"].first["detail"]["messages"]).to include(/Split has 1 associated split times/) expect(response.status).to eq(422) end @@ -138,7 +140,7 @@ it "returns an error if the split does not exist" do make_request - parsed_response = JSON.parse(response.body) + parsed_response = response.parsed_body expect(parsed_response["errors"]).to include(/not found/) expect(response.status).to eq(404) end diff --git a/spec/factories/event.rb b/spec/factories/event.rb index a151de5a9..855fc7da2 100644 --- a/spec/factories/event.rb +++ b/spec/factories/event.rb @@ -2,8 +2,8 @@ factory :event do scheduled_start_time { FFaker::Time.datetime } laps_required { 1 } - course event_group + course { association :course, organization: event_group&.organization } trait :with_short_name do short_name { "#{rand(25..2000)}#{%w[-mile -kilo k M].sample}" } diff --git a/spec/lib/etl/importer_spec.rb b/spec/lib/etl/importer_spec.rb index 44d1629db..76093a879 100644 --- a/spec/lib/etl/importer_spec.rb +++ b/spec/lib/etl/importer_spec.rb @@ -23,7 +23,7 @@ let(:source_data) { file_fixture("test_splits.csv") } let(:data_format) { :csv_splits } let(:options) { { parent: event, current_user_id: 1 } } - let(:event) { create(:event, course: course) } + let(:event) { create(:event, course: course, event_group: create(:event_group, organization: course.organization)) } let(:course) { create(:course) } it "creates all new splits within the given course" do @@ -40,7 +40,7 @@ let(:source_data) { file_fixture("test_splits.csv") } let(:data_format) { :csv_splits } let(:options) { { parent: event, current_user_id: 1 } } - let(:event) { create(:event, course: course) } + let(:event) { create(:event, course: course, event_group: create(:event_group, organization: course.organization)) } let(:course) { create(:course) } before do create(:split, :start, course: course) @@ -61,7 +61,7 @@ let(:source_data) { file_fixture("test_splits_minimal.csv") } let(:data_format) { :csv_splits } let(:options) { { parent: event, current_user_id: 1 } } - let(:event) { create(:event, course: course) } + let(:event) { create(:event, course: course, event_group: create(:event_group, organization: course.organization)) } let(:course) { create(:course) } before do create(:split, :start, base_name: "Start", course: course) diff --git a/spec/lib/etl/loaders/upsert_strategy_spec.rb b/spec/lib/etl/loaders/upsert_strategy_spec.rb index 0c98627c8..bcba665b0 100644 --- a/spec/lib/etl/loaders/upsert_strategy_spec.rb +++ b/spec/lib/etl/loaders/upsert_strategy_spec.rb @@ -4,7 +4,7 @@ subject { described_class.new(proto_records, options) } let(:course) { create(:course, id: 10) } - let(:event) { create(:event, id: 1, course: course) } + let(:event) { create(:event, id: 1, course: course, event_group: create(:event_group, organization: course.organization)) } let(:options) { { parent: course, event: event, unique_key: [:course_id, :distance_from_start], current_user_id: 111 } } let(:valid_proto_records) do [ProtoRecord.new({ record_type: :split, kind: 0, sub_split_bitmap: 1, base_name: "Start", distance_from_start: 0, course_id: 10 }), diff --git a/spec/models/aid_station_spec.rb b/spec/models/aid_station_spec.rb index 7786c9eab..cf020e546 100644 --- a/spec/models/aid_station_spec.rb +++ b/spec/models/aid_station_spec.rb @@ -51,10 +51,10 @@ let(:event_1) { create(:event, :with_short_name, course: course_1, event_group: event_group) } let(:event_2) { create(:event, :with_short_name, course: course_2, event_group: event_group) } let(:event_group) { create(:event_group, home_time_zone: "Arizona") } - let(:course_1) { create(:course) } + let(:course_1) { create(:course, organization: event_group.organization) } let(:course_1_split_1) { create(:split, :start, course: course_1, base_name: "Start", latitude: 40, longitude: -105) } let(:course_1_split_2) { create(:split, :finish, course: course_1, base_name: "Finish", latitude: 42, longitude: -107) } - let(:course_2) { create(:course) } + let(:course_2) { create(:course, organization: event_group.organization) } let(:course_2_split_1) { create(:split, :start, course: course_2, base_name: "Start", latitude: 40, longitude: -105) } before do event_1.splits << course_1_split_1 diff --git a/spec/models/split_spec.rb b/spec/models/split_spec.rb index a52f2825f..9fd211e6f 100644 --- a/spec/models/split_spec.rb +++ b/spec/models/split_spec.rb @@ -189,10 +189,10 @@ let(:event_1) { create(:event, :with_short_name, course: course_1, event_group: event_group) } let(:event_2) { create(:event, :with_short_name, course: course_2, event_group: event_group) } let(:event_group) { create(:event_group) } - let(:course_1) { create(:course) } + let(:course_1) { create(:course, organization: event_group.organization) } let(:course_1_split_1) { create(:split, :start, course: course_1, base_name: "Start", latitude: 40, longitude: -105) } let(:course_1_split_2) { create(:split, :finish, course: course_1, base_name: "Finish", latitude: 42, longitude: -107) } - let(:course_2) { create(:course) } + let(:course_2) { create(:course, organization: event_group.organization) } let(:course_2_split_1) { create(:split, :start, course: course_2, base_name: "Start", latitude: 40, longitude: -105) } let(:course_2_split_2) { create(:split, :finish, course: course_2, base_name: "Finish", latitude: 42, longitude: -107) } before do diff --git a/spec/services/interactors/change_event_course_spec.rb b/spec/services/interactors/change_event_course_spec.rb index f729f5803..a70cc258b 100644 --- a/spec/services/interactors/change_event_course_spec.rb +++ b/spec/services/interactors/change_event_course_spec.rb @@ -29,7 +29,7 @@ end describe "#perform!" do - let(:event) { create(:event, course: old_course) } + let(:event) { create(:event, course: old_course, event_group: create(:event_group, organization: old_course.organization)) } let!(:old_course) { create(:course, splits: old_splits) } let(:old_split_1) { create(:split, :start, base_name: "Start") } let(:old_split_2) { create(:split, distance_from_start: 1000, base_name: "Aid 1") } @@ -38,7 +38,7 @@ let!(:efforts) { create_list(:effort, 2, event: event) } context "when the new course has splits with the same distances as the old" do - let(:new_course) { create(:course, splits: new_splits) } + let(:new_course) { create(:course, splits: new_splits, organization: old_course.organization) } let(:new_split_1) { create(:split, :start, base_name: "Start") } let(:new_split_2) { create(:split, base_name: old_course.ordered_splits.second.base_name) } let(:new_split_3) { create(:split, base_name: old_course.ordered_splits.third.base_name) } @@ -57,7 +57,7 @@ response = subject.perform! expect(event.course_id).to eq(new_course.id) expect(response).to be_successful - expect(response.message).to match(/was changed from/) + expect(response.message).to include("was changed from") end it "changes the split_ids of event split_times to the corresponding split_ids of the new course" do diff --git a/spec/services/interactors/destroy_effort_split_times_spec.rb b/spec/services/interactors/destroy_effort_split_times_spec.rb index 5423d36a6..0daaa887d 100644 --- a/spec/services/interactors/destroy_effort_split_times_spec.rb +++ b/spec/services/interactors/destroy_effort_split_times_spec.rb @@ -3,24 +3,27 @@ RSpec.describe Interactors::DestroyEffortSplitTimes do include BitkeyDefinitions - subject { Interactors::DestroyEffortSplitTimes.new(effort, split_time_ids) } - let!(:split_time_1) { create(:split_time, effort: effort, lap: 1, split: split_1, bitkey: in_bitkey, time_from_start: 0, stopped_here: false) } - let!(:split_time_2) { create(:split_time, effort: effort, lap: 1, split: split_2, bitkey: in_bitkey, time_from_start: 10_000, stopped_here: false) } + subject { described_class.new(effort, split_time_ids) } + + before do + create(:split_time, effort: effort, lap: 1, split: split_1, bitkey: in_bitkey, time_from_start: 0, stopped_here: false) + create(:split_time, effort: effort, lap: 1, split: split_2, bitkey: in_bitkey, time_from_start: 10_000, stopped_here: false) + effort.reload + end + let!(:split_time_3) { create(:split_time, effort: effort, lap: 1, split: split_2, bitkey: out_bitkey, time_from_start: 11_000, stopped_here: false) } let!(:split_time_4) { create(:split_time, effort: effort, lap: 1, split: split_3, bitkey: in_bitkey, time_from_start: 20_000, stopped_here: false) } let!(:split_time_5) { create(:split_time, effort: effort, lap: 1, split: split_3, bitkey: out_bitkey, time_from_start: 21_000, stopped_here: true) } let(:split_time_ids) { split_times.map { |st| st.id.to_s } } let(:effort) { create(:effort, event: event) } - let(:event) { create(:event, course: course) } + let(:event) { create(:event, course: course, event_group: create(:event_group, organization: course.organization)) } let(:course) { create(:course) } let(:split_1) { create(:split, :start, course: course) } let(:split_2) { create(:split, course: course) } let(:split_3) { create(:split, course: course) } let(:split_4) { create(:split, :finish, course: course) } - before { effort.reload } - describe "#initialize" do context "when effort and split_time_ids arguments are provided" do let(:split_times) { [split_time_4, split_time_5] } @@ -31,7 +34,8 @@ end context "when no effort is provided" do - subject { Interactors::DestroyEffortSplitTimes.new(nil, split_time_ids) } + subject { described_class.new(nil, split_time_ids) } + let(:split_times) { [split_time_4, split_time_5] } it "raises an error" do @@ -40,7 +44,7 @@ end context "when no split_time_ids argument is provided" do - subject { Interactors::DestroyEffortSplitTimes.new(effort, nil) } + subject { described_class.new(effort, nil) } it "raises an error" do expect { subject }.to raise_error(/split_time_ids argument was not provided/) @@ -68,7 +72,7 @@ expect(effort.split_times.size).to eq(3) split_time_3.reload expect(split_time_3.stopped_here).to eq(true) - expect(response.resources).to match_array([split_time_3, split_time_4, split_time_5]) + expect(response.resources).to contain_exactly(split_time_3, split_time_4, split_time_5) end end @@ -82,7 +86,7 @@ expect(effort.split_times.size).to eq(3) split_time_5.reload expect(split_time_5.stopped_here).to eq(true) - expect(response.resources).to match_array([split_time_3, split_time_4]) + expect(response.resources).to contain_exactly(split_time_3, split_time_4) end end @@ -94,7 +98,7 @@ response = subject.perform! effort.reload expect(effort.split_times.size).to eq(5) - expect(response.resources).to match_array([]) + expect(response.resources).to be_empty end end end diff --git a/spec/services/interactors/match_raw_times_to_split_times_spec.rb b/spec/services/interactors/match_raw_times_to_split_times_spec.rb index 56c77e505..d3a952d47 100644 --- a/spec/services/interactors/match_raw_times_to_split_times_spec.rb +++ b/spec/services/interactors/match_raw_times_to_split_times_spec.rb @@ -13,7 +13,7 @@ let(:effort) { create(:effort, :with_bib_number, event: event) } let(:event) { create(:event, course: course, event_group: event_group, scheduled_start_time_local: "2018-02-10 06:00:00") } - let(:course) { create(:course) } + let(:course) { create(:course, organization: event_group.organization) } let(:event_group) { create(:event_group, available_live: true) } let(:split_1) { create(:split, :start, course: course) } let(:split_2) { create(:split, course: course) } diff --git a/spec/services/interactors/match_time_records_to_split_times_spec.rb b/spec/services/interactors/match_time_records_to_split_times_spec.rb index 76b3901a1..214f36349 100644 --- a/spec/services/interactors/match_time_records_to_split_times_spec.rb +++ b/spec/services/interactors/match_time_records_to_split_times_spec.rb @@ -129,7 +129,7 @@ end context "when the candidate pool contains a matching split_time belonging to an effort in a different event_group" do - let(:other_event_group) { create(:event_group) } + let(:other_event_group) { create(:event_group, organization: event.event_group.organization) } let(:other_event) do create(:event, event_group: other_event_group, course: event.course, scheduled_start_time: event.scheduled_start_time) diff --git a/spec/services/interactors/upsert_split_times_from_raw_time_row_spec.rb b/spec/services/interactors/upsert_split_times_from_raw_time_row_spec.rb index d08e4dfb8..16751cd0c 100644 --- a/spec/services/interactors/upsert_split_times_from_raw_time_row_spec.rb +++ b/spec/services/interactors/upsert_split_times_from_raw_time_row_spec.rb @@ -16,7 +16,7 @@ let(:effort) { create(:effort, :with_bib_number, event: event) } let(:event) { create(:event, course: course, event_group: event_group, scheduled_start_time_local: "2018-02-10 06:00:00") } - let(:course) { create(:course) } + let(:course) { create(:course, organization: event_group.organization) } let(:split_1) { create(:split, :start, course: course) } let(:split_2) { create(:split, course: course) } let(:split_3) { create(:split, course: course) }