diff --git a/app/services/interactors/set_effort_status.rb b/app/services/interactors/set_effort_status.rb index ef477a992..f75415a81 100644 --- a/app/services/interactors/set_effort_status.rb +++ b/app/services/interactors/set_effort_status.rb @@ -3,17 +3,17 @@ class SetEffortStatus # This class expects to be given an effort loaded with split_times: :splits, or in the alternative, it expects # :ordered_split_times and :lap_splits to be provided in tne options hash. - def self.perform(effort, options = {}) - new(effort, options).perform + def self.perform(effort, **) + new(effort, **).perform end - def initialize(effort, options = {}) - ArgsValidator.validate(subject: effort, subject_class: Effort, params: options, - exclusive: [:ordered_split_times, :lap_splits, :times_container], class: self) + def initialize(effort, ordered_split_times: nil, lap_splits: nil, times_container: nil) @effort = effort - @ordered_split_times = options[:ordered_split_times] || effort.ordered_split_times.reject(&:destroyed?) - @lap_splits = options[:lap_splits] || effort.lap_splits - @times_container = options[:times_container] || SegmentTimesContainer.new(calc_model: :stats) + validate_setup + + @ordered_split_times = ordered_split_times || effort.ordered_split_times.reject(&:destroyed?) + @lap_splits = lap_splits || effort.lap_splits + @times_container = times_container || SegmentTimesContainer.new(calc_model: :stats) end def perform @@ -40,7 +40,10 @@ def set_split_time_status(split_time) end def set_effort_status - worst_split_time_status = ordered_split_times.map(&:data_status_numeric).push(Effort.data_statuses[:good]).compact.min + worst_split_time_status = ordered_split_times.map(&:data_status_numeric) + .push(Effort.data_statuses[:good]) + .compact + .min effort_status = times_without_start_time? ? ::Effort.data_statuses[:bad] : ::Effort.data_statuses[:good] effort.data_status = [worst_split_time_status, effort_status].min end @@ -105,5 +108,10 @@ def changed_split_times def unconfirmed_split_times @unconfirmed_split_times ||= ordered_split_times.reject(&:confirmed?) end + + def validate_setup + raise ArgumentError, "set_effort_status must include effort" unless effort + raise ArgumentError, "effort must be an Effort" unless effort.is_a?(Effort) + end end end diff --git a/app/services/interactors/set_effort_stop.rb b/app/services/interactors/set_effort_stop.rb index 9ff90af6b..8e0da4424 100644 --- a/app/services/interactors/set_effort_stop.rb +++ b/app/services/interactors/set_effort_stop.rb @@ -1,14 +1,13 @@ module Interactors class SetEffortStop - def self.perform(effort, options = {}) - new(effort, options).perform + def self.perform(effort, **) + new(effort, **).perform end - def initialize(effort, options = {}) - ArgsValidator.validate(subject: effort, subject_class: Effort, params: options, exclusive: [:stop_status, :split_time_id]) + def initialize(effort, stop_status: nil, split_time_id: nil) @effort = effort - @stop_status = options[:stop_status].nil? ? true : options[:stop_status] - @split_time_id = options[:split_time_id] + @stop_status = stop_status.nil? || stop_status + @split_time_id = split_time_id validate_setup end @@ -39,9 +38,11 @@ def resources end def validate_setup - if split_time_id && !found_split_time - raise ArgumentError, "split_time_id #{split_time_id} does not exist for #{effort}" - end + raise ArgumentError, "set_effort_stop must include effort" unless effort + raise ArgumentError, "effort must be an Effort" unless effort.is_a?(Effort) + return unless split_time_id && !found_split_time + + raise ArgumentError, "split_time_id #{split_time_id} does not exist for #{effort}" end end end diff --git a/app/services/interactors/shift_event_start_time.rb b/app/services/interactors/shift_event_start_time.rb index d84318081..36908ad01 100644 --- a/app/services/interactors/shift_event_start_time.rb +++ b/app/services/interactors/shift_event_start_time.rb @@ -3,26 +3,23 @@ class ShiftEventStartTime include Interactors::Errors include TimeFormats - def self.perform!(event, args) - new(event, args).perform! + def self.perform!(event, new_start_time:) + new(event, new_start_time: new_start_time).perform! end - def initialize(event, args) - ArgsValidator.validate(subject: event, - subject_class: Event, - params: args, - required: [:new_start_time], - exclusive: [:new_start_time], - class: self.class) + def initialize(event, new_start_time:) @event = event - @new_start_time = args[:new_start_time].in_time_zone(event.home_time_zone) + @non_localized_new_start_time = new_start_time + validate_setup + + @new_start_time = new_start_time.in_time_zone(event.home_time_zone) @old_start_time = event.scheduled_start_time_local @current_user = User.current @errors = [] end def perform! - unless errors.present? || shift_seconds == 0 + unless errors.present? || shift_seconds.zero? ActiveRecord::Base.transaction do update_event update_efforts @@ -35,6 +32,8 @@ def perform! private + attr_reader :event, :non_localized_new_start_time, :new_start_time, :old_start_time, :current_user, :errors + def update_event event.update!(scheduled_start_time: new_start_time) rescue ActiveRecord::ActiveRecordError => e @@ -53,8 +52,6 @@ def update_split_times errors << active_record_error(e) end - attr_reader :event, :new_start_time, :old_start_time, :current_user, :errors - def effort_query EffortQuery.shift_event_scheduled_times(event, shift_seconds) end @@ -73,14 +70,24 @@ def shift_direction def response_message if errors.present? - "The start time for #{event.name} could not be shifted from #{flexible_format(old_start_time, new_start_time)} to #{flexible_format(new_start_time, old_start_time)}. " - elsif shift_seconds == 0 - "The new start time for #{event.name} was #{flexible_format(new_start_time, old_start_time)}, unchanged from the old start time. " + "The start time for #{event.name} could not be shifted " \ + "from #{flexible_format(old_start_time, new_start_time)} " \ + "to #{flexible_format(new_start_time, old_start_time)}. " + elsif shift_seconds.zero? + "The new start time for #{event.name} was #{flexible_format(new_start_time, old_start_time)}, " \ + "unchanged from the old start time. " else - "The start time for #{event.name} was shifted #{shift_direction} " + - "from #{flexible_format(old_start_time, new_start_time)} to #{flexible_format(new_start_time, old_start_time)}. " + + "The start time for #{event.name} was shifted #{shift_direction} " \ + "from #{flexible_format(old_start_time, new_start_time)} " \ + "to #{flexible_format(new_start_time, old_start_time)}. " \ "All related scheduled start times and split times were shifted #{shift_direction} by the same amount." end end + + def validate_setup + raise ArgumentError, "shift_event_start_time must include event" unless event + raise ArgumentError, "event must be an Event" unless event.is_a?(Event) + raise ArgumentError, "shift_event_start_time must include new_start_time" unless non_localized_new_start_time + end end end diff --git a/app/services/interactors/start_efforts.rb b/app/services/interactors/start_efforts.rb index 7093dad73..3a0819afc 100644 --- a/app/services/interactors/start_efforts.rb +++ b/app/services/interactors/start_efforts.rb @@ -3,24 +3,22 @@ class StartEfforts include Interactors::Errors include ActionView::Helpers::TextHelper - def self.perform!(args) - new(args).perform! + def self.perform!(efforts:, start_time: nil) + new(efforts: efforts, start_time: start_time).perform! end - def initialize(args) - ArgsValidator.validate(params: args, - required: [:efforts], - exclusive: [:efforts, :start_time], - class: self.class) - @efforts = args[:efforts] - @start_time = args[:start_time] + def initialize(efforts:, start_time: nil) + raise ArgumentError, "start_efforts must include efforts" unless efforts + + @efforts = efforts + @start_time = start_time @errors = [] @saved_split_times = [] validate_setup end def perform! - unless errors.present? + if errors.blank? SplitTime.transaction do efforts.each(&method(:start_effort)) raise ActiveRecord::Rollback if errors.present? @@ -69,8 +67,20 @@ def time_zone end def response_message - started_time = converted_start_time ? I18n.l(converted_start_time, format: :datetime_input) : "the scheduled start #{pluralize(saved_split_times.size, 'time')}" - errors.present? ? "No efforts were started" : "Started #{pluralize(saved_split_times.size, 'effort')} at #{started_time}" + started_time = if converted_start_time + I18n.l(converted_start_time, + format: :datetime_input) + else + "the scheduled start #{pluralize( + saved_split_times.size, 'time' + )}" + end + if errors.present? + "No efforts were started" + else + "Started #{pluralize(saved_split_times.size, + 'effort')} at #{started_time}" + end end def validate_setup @@ -82,8 +92,8 @@ def validate_setup errors << multiple_event_groups_error(event_group_ids) if event_group_ids.uniq.many? errors << invalid_start_time_error(start_time) if invalid_start_time? errors << invalid_start_time_error(start_time || "nil") unless converted_start_time || - efforts.all?(&:scheduled_start_time?) || - efforts.all?(&:event) + efforts.all?(&:scheduled_start_time?) || + efforts.all?(&:event) end def event_group_ids diff --git a/app/services/interactors/submit_raw_time_rows.rb b/app/services/interactors/submit_raw_time_rows.rb index 9aa854dec..7ea9a36e9 100644 --- a/app/services/interactors/submit_raw_time_rows.rb +++ b/app/services/interactors/submit_raw_time_rows.rb @@ -2,25 +2,27 @@ module Interactors class SubmitRawTimeRows include Interactors::Errors - def self.perform!(args) - new(args).perform! + def self.perform!(raw_time_rows:, event_group:, force_submit:, mark_as_reviewed:, current_user_id: nil) + new( + raw_time_rows: raw_time_rows, + event_group: event_group, + force_submit: force_submit, + mark_as_reviewed: mark_as_reviewed, + current_user_id: current_user_id + ).perform! end - def initialize(args) - ArgsValidator.validate(params: args, - required: [:raw_time_rows, :event_group, :force_submit, :mark_as_reviewed], - exclusive: [:raw_time_rows, :event_group, :force_submit, :mark_as_reviewed, - :current_user_id], - class: self.class) - @raw_time_rows = args[:raw_time_rows] - @event_group = args[:event_group] - @force_submit = args[:force_submit] - @mark_as_reviewed = args[:mark_as_reviewed] - @current_user_id = args[:current_user_id] + def initialize(raw_time_rows:, event_group:, force_submit:, mark_as_reviewed:, current_user_id: nil) + @raw_time_rows = raw_time_rows + @event_group = event_group + @force_submit = force_submit + @mark_as_reviewed = mark_as_reviewed + @current_user_id = current_user_id @times_container = SegmentTimesContainer.new(calc_model: :stats) @problem_rows = [] @upserted_split_times = [] @errors = [] + validate_setup end def perform! @@ -99,5 +101,12 @@ def bib_numbers def resources { problem_rows: problem_rows, upserted_split_times: upserted_split_times } end + + def validate_setup + raise ArgumentError, "submit_raw_time_rows must include raw_time_rows" unless raw_time_rows + raise ArgumentError, "submit_raw_time_rows must include event_group" unless event_group + raise ArgumentError, "submit_raw_time_rows must include force_submit" if force_submit.nil? + raise ArgumentError, "submit_raw_time_rows must include mark_as_reviewed" if mark_as_reviewed.nil? + end end end diff --git a/spec/services/interactors/set_effort_status_spec.rb b/spec/services/interactors/set_effort_status_spec.rb index 2f518a92a..38492cd2f 100644 --- a/spec/services/interactors/set_effort_status_spec.rb +++ b/spec/services/interactors/set_effort_status_spec.rb @@ -1,7 +1,8 @@ require "rails_helper" RSpec.describe Interactors::SetEffortStatus do - subject { Interactors::SetEffortStatus.new(effort, times_container: times_container) } + subject { described_class.new(effort, times_container: times_container) } + let(:effort) { efforts(:hardrock_2014_finished_first) } let(:subject_split_times) { effort.ordered_split_times } let(:times_container) { SegmentTimesContainer.new(calc_model: :terrain) } @@ -19,7 +20,7 @@ let(:effort) { nil } it "raises an ArgumentError" do - expect { subject }.to raise_error(/must include a subject/) + expect { subject }.to raise_error(ArgumentError, /must include effort/) end end end @@ -30,7 +31,7 @@ subject_split_times.each { |st| st.update(data_status: nil) } end - context "for an effort that has not yet started" do + context "when the effort has not yet started" do let(:effort) { efforts(:hardrock_2014_not_started) } it "sets effort data_status to good and does not attempt to change split_times" do @@ -73,7 +74,7 @@ it 'sets data_status of all split_times correctly and sets effort to "questionable"' do subject.perform - expect(subject_split_times.map(&:data_status)).to eq(%w[good questionable] + ["good"] * 10) + expect(subject_split_times.map(&:data_status)).to eq(%w[good questionable] + (["good"] * 10)) expect(effort.data_status).to eq("questionable") end end @@ -82,12 +83,14 @@ let(:split_time_1) { subject_split_times.second } let(:split_time_2) { subject_split_times.third } - before { split_time_1.update(absolute_time: split_time_1.absolute_time - 4.hours) } - before { split_time_2.update(absolute_time: split_time_2.absolute_time - 3.hours) } + before do + split_time_1.update(absolute_time: split_time_1.absolute_time - 4.hours) + split_time_2.update(absolute_time: split_time_2.absolute_time - 3.hours) + end it 'sets data_status of all split_times correctly and sets effort to "bad"' do subject.perform - expect(subject_split_times.map(&:data_status)).to eq(%w[good bad questionable] + ["good"] * 9) + expect(subject_split_times.map(&:data_status)).to eq(%w[good bad questionable] + (["good"] * 9)) expect(effort.data_status).to eq("bad") end end @@ -97,15 +100,17 @@ it 'sets data_status of all subsequent split_times to "questionable"' do subject.perform - expect(subject_split_times.map(&:data_status)).to eq(%w[good good good] + ["questionable"] * 9) + expect(subject_split_times.map(&:data_status)).to eq(%w[good good good] + (["questionable"] * 9)) expect(effort.data_status).to eq("questionable") end end context "when all split_times are confirmed" do let(:split_time) { subject_split_times.third } - before { split_time.update(absolute_time: split_time.absolute_time - 4.hours) } # Bad time - before { subject_split_times.each { |st| st.data_status = :confirmed } } + before do + split_time.update(absolute_time: split_time.absolute_time - 4.hours) # Bad time + subject_split_times.each { |st| st.data_status = :confirmed } + end it 'sets data_status of the effort to "good"' do subject.perform @@ -120,7 +125,7 @@ split_time.update(absolute_time: split_time.absolute_time - 4.hours) # Bad time effort.update(data_status: :good) - pre_set_statuses = %w[bad questionable good questionable good bad] + ["good"] * 6 + pre_set_statuses = %w[bad questionable good questionable good bad] + (["good"] * 6) subject_split_times.zip(pre_set_statuses).each do |st, status| st.update(data_status: status) end @@ -128,12 +133,12 @@ it "sets data_status of the split_times and effort correctly" do subject.perform - expect(subject_split_times.map(&:data_status)).to eq(%w[good good bad] + ["good"] * 9) + expect(subject_split_times.map(&:data_status)).to eq(%w[good good bad] + (["good"] * 9)) expect(effort.data_status).to eq("bad") end end - context "for a multi-lap event" do + context "when the event is multi-lap" do let(:effort) { efforts(:rufa_2017_24h_finished_last) } context "when all times are good" do @@ -150,7 +155,7 @@ it "works as expected" do subject.perform - expect(subject_split_times.map(&:data_status)).to eq(["good"] * 4 + ["bad"] + ["good"]) + expect(subject_split_times.map(&:data_status)).to eq((["good"] * 4) + ["bad"] + ["good"]) expect(effort.data_status).to eq("bad") end end diff --git a/spec/services/interactors/set_effort_stop_spec.rb b/spec/services/interactors/set_effort_stop_spec.rb index 1c9a59a07..76442ac29 100644 --- a/spec/services/interactors/set_effort_stop_spec.rb +++ b/spec/services/interactors/set_effort_stop_spec.rb @@ -3,11 +3,12 @@ RSpec.describe Interactors::SetEffortStop do include BitkeyDefinitions - subject { Interactors::SetEffortStop.new(effort, stop_status: stop_status, split_time_id: split_time_id) } + subject { described_class.new(effort, stop_status: stop_status, split_time_id: split_time_id) } + let(:stop_status) { nil } let(:split_time_id) { nil } - let(:split_time_1) { build_stubbed(:split_time, effort: effort, lap: 1, split: split_1, bitkey: in_bitkey, absolute_time: start_time + 0, stopped_here: false) } + let(:split_time_1) { build_stubbed(:split_time, effort: effort, lap: 1, split: split_1, bitkey: in_bitkey, absolute_time: start_time, stopped_here: false) } let(:split_time_2) { build_stubbed(:split_time, effort: effort, lap: 1, split: split_2, bitkey: in_bitkey, absolute_time: start_time + 10_000, stopped_here: false) } let(:split_time_3) { build_stubbed(:split_time, effort: effort, lap: 1, split: split_2, bitkey: out_bitkey, absolute_time: start_time + 11_000, stopped_here: false) } let(:split_time_4) { build_stubbed(:split_time, effort: effort, lap: 1, split: split_3, bitkey: in_bitkey, absolute_time: start_time + 20_000, stopped_here: false) } @@ -37,7 +38,7 @@ let(:effort) { nil } it "raises an error" do - expect { subject }.to raise_error(/arguments must include a subject/) + expect { subject }.to raise_error(ArgumentError, /must include effort/) end end @@ -110,7 +111,7 @@ end end - context "for a multi-lap event where stop_status is true" do + context "when the event is multi-lap and stop_status is true" do let(:event) { build_stubbed(:event, course: course, laps_required: 2) } let(:split_time_4) { build_stubbed(:split_time, effort: effort, lap: 1, split: split_3, bitkey: in_bitkey, absolute_time: start_time + 20_000, stopped_here: true) } let(:split_times) { [split_time_1, split_time_2, split_time_3, split_time_4, split_time_5, split_time_6, split_time_7] } diff --git a/spec/services/interactors/shift_event_start_time_spec.rb b/spec/services/interactors/shift_event_start_time_spec.rb new file mode 100644 index 000000000..e9c3caa26 --- /dev/null +++ b/spec/services/interactors/shift_event_start_time_spec.rb @@ -0,0 +1,97 @@ +require "rails_helper" + +RSpec.describe Interactors::ShiftEventStartTime do + subject { described_class.new(event, new_start_time: new_start_time) } + + let(:event) { events(:hardrock_2014) } + let(:effort) { efforts(:hardrock_2014_finished_first) } + + describe "#initialize" do + let(:new_start_time) { event.scheduled_start_time_local + 1.hour } + + context "when valid arguments are provided" do + it "initializes without error" do + expect { subject }.not_to raise_error + end + end + + context "when event is nil" do + it "raises an ArgumentError" do + expect { described_class.new(nil, new_start_time: new_start_time) }.to raise_error(ArgumentError, /must include event/) + end + end + + context "when event is not an Event" do + it "raises an ArgumentError" do + expect { described_class.new("not an event", new_start_time: new_start_time) }.to raise_error(ArgumentError, /must be an Event/) + end + end + + context "when new_start_time is nil" do + it "raises an ArgumentError" do + expect { described_class.new(event, new_start_time: nil) }.to raise_error(ArgumentError, /must include new_start_time/) + end + end + end + + describe "#perform!" do + let(:response) { subject.perform! } + let(:split_time) { effort.ordered_split_times.first } + + context "when the start time is shifted forward" do + let(:new_start_time) { event.scheduled_start_time_local + 1.hour } + + it "shifts the event, effort, and split_time times forward" do + original_event_time = event.scheduled_start_time + original_split_time = split_time.absolute_time + + response + + expect(event.reload.scheduled_start_time).to eq(original_event_time + 1.hour) + expect(split_time.reload.absolute_time).to eq(original_split_time + 1.hour) + end + + it "returns a successful response" do + expect(response).to be_successful + expect(response.message).to include("shifted") + end + end + + context "when the start time is shifted backward" do + let(:new_start_time) { event.scheduled_start_time_local - 1.hour } + + it "shifts the event and split_time times backward" do + original_event_time = event.scheduled_start_time + original_split_time = split_time.absolute_time + + response + + expect(event.reload.scheduled_start_time).to eq(original_event_time - 1.hour) + expect(split_time.reload.absolute_time).to eq(original_split_time - 1.hour) + end + + it "returns a successful response" do + expect(response).to be_successful + end + end + + context "when the new start time equals the current start time" do + let(:new_start_time) { event.scheduled_start_time_local } + + it "does not change any times" do + original_event_time = event.scheduled_start_time + original_split_time = split_time.absolute_time + + response + + expect(event.reload.scheduled_start_time).to eq(original_event_time) + expect(split_time.reload.absolute_time).to eq(original_split_time) + end + + it "returns a successful response with unchanged message" do + expect(response).to be_successful + expect(response.message).to include("unchanged") + end + end + end +end diff --git a/spec/services/interactors/submit_raw_time_rows_spec.rb b/spec/services/interactors/submit_raw_time_rows_spec.rb new file mode 100644 index 000000000..876eaf7c0 --- /dev/null +++ b/spec/services/interactors/submit_raw_time_rows_spec.rb @@ -0,0 +1,190 @@ +require "rails_helper" + +RSpec.describe Interactors::SubmitRawTimeRows do + include BitkeyDefinitions + + let(:event_group) { event_groups(:hardrock_2014) } + let(:event) { events(:hardrock_2014) } + let(:effort) { efforts(:hardrock_2014_not_started) } + let(:split_1) { splits(:hardrock_cw_start) } + let(:upsert_response) { Interactors::Response.new([], "", { upserted_split_times: [] }) } + + before do + allow(EnrichRawTimeRow).to receive(:perform) + allow(Interactors::UpsertSplitTimesFromRawTimeRow).to receive(:perform!).and_return(upsert_response) + end + + describe "#initialize" do + context "when all required arguments are provided" do + it "initializes without error" do + expect do + described_class.new(raw_time_rows: [], event_group: event_group, force_submit: false, mark_as_reviewed: false) + end.not_to raise_error + end + end + + context "when raw_time_rows is missing" do + it "raises an ArgumentError" do + expect do + described_class.new(raw_time_rows: nil, event_group: event_group, force_submit: false, mark_as_reviewed: false) + end.to raise_error(ArgumentError, /must include raw_time_rows/) + end + end + + context "when event_group is missing" do + it "raises an ArgumentError" do + expect do + described_class.new(raw_time_rows: [], event_group: nil, force_submit: false, mark_as_reviewed: false) + end.to raise_error(ArgumentError, /must include event_group/) + end + end + + context "when force_submit is nil" do + it "raises an ArgumentError" do + expect do + described_class.new(raw_time_rows: [], event_group: event_group, force_submit: nil, mark_as_reviewed: false) + end.to raise_error(ArgumentError, /must include force_submit/) + end + end + + context "when mark_as_reviewed is nil" do + it "raises an ArgumentError" do + expect do + described_class.new(raw_time_rows: [], event_group: event_group, force_submit: false, mark_as_reviewed: nil) + end.to raise_error(ArgumentError, /must include mark_as_reviewed/) + end + end + end + + describe "#perform!" do + let(:raw_time) do + build(:raw_time, event_group: event_group, bib_number: effort.bib_number.to_s, + split_name: split_1.base_name, bitkey: in_bitkey) + end + let(:raw_time_row) { RawTimeRow.new([raw_time], nil, nil, nil, []) } + let(:force_submit) { true } + let(:mark_as_reviewed) { false } + let(:current_user_id) { nil } + + let(:response) do + described_class.perform!( + raw_time_rows: [raw_time_row], + event_group: event_group, + force_submit: force_submit, + mark_as_reviewed: mark_as_reviewed, + current_user_id: current_user_id, + ) + end + + context "when raw_times are valid" do + it "saves raw_times to the database" do + expect { response }.to change(RawTime, :count).by(1) + end + + it "sets event_group_id on saved raw_times" do + response + expect(raw_time.reload.event_group_id).to eq(event_group.id) + end + + it "looks up the effort by bib_number" do + response + expect(raw_time_row.effort).to eq(effort) + end + + it "returns a successful response with no problem rows" do + expect(response).to be_successful + expect(response.resources[:problem_rows]).to be_empty + end + end + + context "when mark_as_reviewed is true" do + let(:mark_as_reviewed) { true } + let(:current_user_id) { 1 } + + it "sets reviewed_by and reviewed_at on saved raw_times" do + response + raw_time.reload + expect(raw_time.reviewed_by).to eq(current_user_id) + expect(raw_time.reviewed_at).to be_present + end + end + + context "when the raw_time is not clean and force_submit is false" do + let(:force_submit) { false } + + before do + allow(EnrichRawTimeRow).to receive(:perform) do |args| + args[:raw_time_row].raw_times.each { |rt| rt.data_status = :bad } + end + end + + it "does not save the raw_time" do + expect { response }.not_to change(RawTime, :count) + end + + it "includes the row in problem_rows" do + expect(response.resources[:problem_rows]).to include(raw_time_row) + end + end + + context "when force_submit is true and the raw_time is not clean" do + let(:force_submit) { true } + + before do + allow(EnrichRawTimeRow).to receive(:perform) do |args| + args[:raw_time_row].raw_times.each { |rt| rt.data_status = :bad } + end + end + + it "saves the raw_time anyway" do + expect { response }.to change(RawTime, :count).by(1) + end + + it "returns no problem rows" do + expect(response.resources[:problem_rows]).to be_empty + end + end + + context "when the bib_number does not match any effort" do + let(:raw_time) do + build(:raw_time, event_group: event_group, bib_number: "99999", + split_name: split_1.base_name, bitkey: in_bitkey) + end + + before do + allow(VerifyRawTimeRow).to receive(:perform) do |rtr, **_| + rtr.errors << { title: "missing effort" } + end + end + + it "does not save the raw_time" do + expect { response }.not_to change(RawTime, :count) + end + + it "includes the row in problem_rows" do + expect(response.resources[:problem_rows]).to include(raw_time_row) + end + end + + context "when event_group permits notifications" do + before do + allow(event_group).to receive(:permit_notifications?).and_return(true) + allow(BulkProgressNotifier).to receive(:notify) + end + + it "sends notifications" do + response + expect(BulkProgressNotifier).to have_received(:notify) + end + end + + context "when event_group does not permit notifications" do + before { allow(BulkProgressNotifier).to receive(:notify) } + + it "does not send notifications" do + response + expect(BulkProgressNotifier).not_to have_received(:notify) + end + end + end +end