Skip to content

Commit

Permalink
Allow a checker to be retried in case it fails
Browse files Browse the repository at this point in the history
  • Loading branch information
leochab committed Jun 26, 2024
1 parent 01d93b4 commit 68c91c3
Show file tree
Hide file tree
Showing 12 changed files with 143 additions and 20 deletions.
3 changes: 2 additions & 1 deletion .rubocop.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ AllCops:
#

Layout/DotPosition:
EnforcedStyle: trailing
Enabled: false
StyleGuide: https://relaxed.ruby.style/#layoutdotposition

Layout/FirstHashElementIndentation:
EnforcedStyle: consistent
Expand Down
18 changes: 15 additions & 3 deletions lib/checker_jobs/checks/base.rb
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
CheckerJobs::Checks::Base = Struct.new(:klass, :name, :options, :block) do
def perform
def perform(iteration = 0)
result = CheckerJobs.configuration.around_check.call do
klass.new.instance_exec(&block)
end

result.tap { |res| handle_result(res) }
result.tap { |res| handle_result(res, iteration) }
end

private
Expand All @@ -14,7 +14,19 @@ def notify(count:, entries: nil)
notifier_class.new(self, count, entries).notify
end

def handle_result(_result)
def handle_result(_result, _retry_count)
raise NotImplementedError
end

def handle_retry(count, iteration, entries = nil)
retry?(iteration) ? perform(iteration + 1) : notify(count: count, entries: entries)
end

def retry?(iteration)
iteration < max_retry
end

def max_retry
@_max_retry ||= options.fetch(:retry, 0).clamp(0, 3)
end
end
4 changes: 2 additions & 2 deletions lib/checker_jobs/checks/ensure_fewer.rb
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
class CheckerJobs::Checks::EnsureFewer < CheckerJobs::Checks::Base
private

def handle_result(result)
def handle_result(result, iteration)
case result
when Numeric
notify(count: result) if result > options.fetch(:than)
handle_retry(result, iteration) if result > options.fetch(:than)
else
raise ArgumentError, "Unsupported result: '#{result.class.name}' for 'ensure_less'"
end
Expand Down
4 changes: 2 additions & 2 deletions lib/checker_jobs/checks/ensure_more.rb
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
class CheckerJobs::Checks::EnsureMore < CheckerJobs::Checks::Base
private

def handle_result(result)
def handle_result(result, iteration)
case result
when Numeric
notify(count: result) if result < options.fetch(:than)
handle_retry(result, iteration) if result < options.fetch(:than)
else
raise ArgumentError, "Unsupported result: '#{result.class.name}' for 'ensure_more'"
end
Expand Down
8 changes: 4 additions & 4 deletions lib/checker_jobs/checks/ensure_no.rb
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
class CheckerJobs::Checks::EnsureNo < CheckerJobs::Checks::Base
private

def handle_result(result)
def handle_result(result, iteration)
case result
when Numeric
notify(count: result) unless result.zero?
handle_retry(result, iteration) unless result.zero?
when Enumerable
notify(count: result.size, entries: result) unless result.empty?
handle_retry(result.size, iteration, result) unless result.empty?
when TrueClass, FalseClass
notify(count: 1) if result
handle_retry(1, iteration) if result
else
raise ArgumentError, "Unsupported result: '#{result.class.name}' for 'ensure_no'"
end
Expand Down
6 changes: 3 additions & 3 deletions lib/checker_jobs/notifiers/email.rb
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,9 @@ def valid?
end

def mailer_options
@_mailer_options ||= @defaults.
merge(email_options).
merge(@check.klass.notifier_options)
@_mailer_options ||= @defaults
.merge(email_options)
.merge(@check.klass.notifier_options)
end

def email_options
Expand Down
2 changes: 1 addition & 1 deletion spec/checker_jobs/checks/base_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@
it "keeps handling the result outside the around_check delegation" do
allow(instance).to receive(:handle_result).and_call_original
perform
expect(instance).to have_received(:handle_result).with(0)
expect(instance).to have_received(:handle_result).with(0, 0)
end
end
end
Expand Down
26 changes: 25 additions & 1 deletion spec/checker_jobs/checks/ensure_fewer_spec.rb
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
RSpec.describe CheckerJobs::Checks::EnsureFewer, :bugsnag, :configuration do
include BugsnagHelpers

let(:instance) { described_class.new(checker_klass, "ensure_name", { than: 3 }, block) }
let(:instance) { described_class.new(checker_klass, "ensure_name", options, block) }
let(:checker_klass) do
Class.new do
include CheckerJobs::Base
notify :bugsnag
end
end

let(:options) { { than: 3 } }

describe "#perform" do
subject(:perform) { instance.perform }

Expand All @@ -29,5 +31,27 @@

include_examples "sends_a_bugsnag_notification"
end

context "when retry option is used" do
let(:options) { { than: 3, retry: 1 } }

context "when block's result is lower than the threshold" do
let(:block) { Proc.new { 2 } }

include_examples "does_not_send_a_bugsnag_notification"
end

context "when block's result is greater than the threshold" do
let(:block) { Proc.new { 4 } }

it "sends 2 sidekiq jobs" do
allow(instance).to receive(:perform).and_call_original
perform
expect(instance).to have_received(:perform).twice
end

include_examples "sends_a_bugsnag_notification"
end
end
end
end
26 changes: 25 additions & 1 deletion spec/checker_jobs/checks/ensure_more_spec.rb
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
RSpec.describe CheckerJobs::Checks::EnsureMore, :bugsnag, :configuration do
include BugsnagHelpers

let(:instance) { described_class.new(checker_klass, "ensure_name", { than: 3 }, block) }
let(:instance) { described_class.new(checker_klass, "ensure_name", options, block) }
let(:checker_klass) do
Class.new do
include CheckerJobs::Base
notify :bugsnag
end
end

let(:options) { { than: 3 } }

describe "#perform" do
subject(:perform) { instance.perform }

Expand All @@ -29,5 +31,27 @@

include_examples "does_not_send_a_bugsnag_notification"
end

context "when retry option is used" do
let(:options) { { than: 3, retry: 1 } }

context "when block's result is greater than the threshold" do
let(:block) { Proc.new { 4 } }

include_examples "does_not_send_a_bugsnag_notification"
end

context "when block's result is lower than the threshold" do
let(:block) { Proc.new { 2 } }

it "sends 2 sidekiq jobs" do
allow(instance).to receive(:perform).and_call_original
perform
expect(instance).to have_received(:perform).twice
end

include_examples "sends_a_bugsnag_notification"
end
end
end
end
47 changes: 46 additions & 1 deletion spec/checker_jobs/checks/ensure_no_spec.rb
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
require "support/checkers"

RSpec.describe CheckerJobs::Checks::EnsureNo, :bugsnag, :configuration do
include BugsnagHelpers

let(:instance) { described_class.new(checker_klass, "ensure_name", {}, block) }
let(:instance) { described_class.new(checker_klass, "ensure_name", options, block) }
let(:checker_klass) do
Class.new do
include CheckerJobs::Base
notify :bugsnag
end
end
let(:options) { {} }

describe "#perform" do
subject(:perform) { instance.perform }
Expand Down Expand Up @@ -47,5 +50,47 @@

include_examples "does_not_send_a_bugsnag_notification"
end

context "when retry option is used" do
context "when set to 1 time" do
let(:options) { { retry: 1 } }

context "when block's result is 0" do
let(:block) { Proc.new { 0 } }

include_examples "does_not_send_a_bugsnag_notification"
end

context "when block's result is > 0" do
include_context "when SidekiqChecker is available"
let(:block) { Proc.new { 2 } }

it "sends 2 sidekiq jobs" do
allow(instance).to receive(:perform).and_call_original
perform
expect(instance).to have_received(:perform).twice
end

include_examples "sends_a_bugsnag_notification"
end
end

context "when set to more than the maximum allowed" do
let(:options) { { retry: 8 } }

context "when block's result is > 0" do
include_context "when SidekiqChecker is available"
let(:block) { Proc.new { 2 } }

it "sends 1 more sidekiq jobs than the maximum number of retries" do
allow(instance).to receive(:perform).and_call_original
perform
expect(instance).to have_received(:perform).exactly(4).times
end

include_examples "sends_a_bugsnag_notification"
end
end
end
end
end
2 changes: 1 addition & 1 deletion spec/checker_jobs/notifiers/bugsnag_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
expect(::Bugsnag).to have_received(:deliver_notification).once
end

describe "notify's resulting payload" do
describe "notifies resulting payload" do
subject(:notify_report) do
report = nil

Expand Down
17 changes: 17 additions & 0 deletions spec/support/bugsnag_helpers.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
module BugsnagHelpers
shared_examples "sends_a_bugsnag_notification" do
it do
allow(::Bugsnag).to receive(:deliver_notification)
subject
expect(::Bugsnag).to have_received(:deliver_notification).once
end
end

shared_examples "does_not_send_a_bugsnag_notification" do
it do
allow(::Bugsnag).to receive(:deliver_notification)
subject
expect(::Bugsnag).not_to have_received(:deliver_notification)
end
end
end

0 comments on commit 68c91c3

Please sign in to comment.