From c2c6303a211cdd9f943459ed421441935294a402 Mon Sep 17 00:00:00 2001 From: Mark Burns Date: Wed, 3 Jul 2024 17:43:09 +0100 Subject: [PATCH] improve test coverage and fix rubocop violations --- lib/interactify/async/job_maker.rb | 3 +- lib/interactify/async/jobable.rb | 2 + lib/interactify/async/null_job.rb | 23 --- lib/interactify/configure.rb | 2 + lib/interactify/contracts/breaches.rb | 2 + lib/interactify/dependency_inference.rb | 10 +- lib/interactify/hooks.rb | 2 + .../all_the_things_integration_spec.rb | 4 +- spec/integration/each_integration_spec.rb | 4 +- spec/lib/interactify/async/job_maker_spec.rb | 8 +- spec/lib/interactify/wiring_spec.rb | 139 +++++++++++++++++- spec/support/spec_support.rb | 2 +- 12 files changed, 160 insertions(+), 41 deletions(-) delete mode 100644 lib/interactify/async/null_job.rb diff --git a/lib/interactify/async/job_maker.rb b/lib/interactify/async/job_maker.rb index 87fff56..f3e679c 100644 --- a/lib/interactify/async/job_maker.rb +++ b/lib/interactify/async/job_maker.rb @@ -1,7 +1,6 @@ # frozen_string_literal: true require "interactify/async/job_klass" -require "interactify/async/null_job" module Interactify module Async @@ -23,7 +22,7 @@ def job_klass private def define_job_klass - return NullJob if Interactify.sidekiq_missing? + return if Interactify.sidekiq_missing? this = self diff --git a/lib/interactify/async/jobable.rb b/lib/interactify/async/jobable.rb index 8375b8d..d09dea0 100644 --- a/lib/interactify/async/jobable.rb +++ b/lib/interactify/async/jobable.rb @@ -15,6 +15,8 @@ module Jobable next if Interactify.sidekiq_missing? def base.inherited(klass) + return if Interactify.sidekiq_missing? + super_klass = klass.superclass super_job = super_klass::Job # really spiffing diff --git a/lib/interactify/async/null_job.rb b/lib/interactify/async/null_job.rb deleted file mode 100644 index a3e788f..0000000 --- a/lib/interactify/async/null_job.rb +++ /dev/null @@ -1,23 +0,0 @@ -# frozen_string_literal: true - -module Interactify - module Async - class NullJob - def method_missing(...) - self - end - - def self.method_missing(...) - self - end - - def respond_to_missing?(...) - true - end - - def self.respond_to_missing?(...) - true - end - end - end -end diff --git a/lib/interactify/configure.rb b/lib/interactify/configure.rb index 1196416..ffea00e 100644 --- a/lib/interactify/configure.rb +++ b/lib/interactify/configure.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Interactify module Configure def validate_app(ignore: []) diff --git a/lib/interactify/contracts/breaches.rb b/lib/interactify/contracts/breaches.rb index bf395e2..1ff5828 100644 --- a/lib/interactify/contracts/breaches.rb +++ b/lib/interactify/contracts/breaches.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Interactify module Breaches def self.handle_with_failure(context, breaches) diff --git a/lib/interactify/dependency_inference.rb b/lib/interactify/dependency_inference.rb index 662733e..b6fd324 100644 --- a/lib/interactify/dependency_inference.rb +++ b/lib/interactify/dependency_inference.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Interactify class << self delegate :on_definition_error, :trigger_definition_error, to: :configuration @@ -10,10 +12,6 @@ def railties_missing! @railties_missing = true end - def railties - railties? - end - def railties? !railties_missing? end @@ -26,10 +24,6 @@ def sidekiq_missing! @sidekiq_missing = true end - def sidekiq - sidekiq? - end - def sidekiq? !sidekiq_missing? end diff --git a/lib/interactify/hooks.rb b/lib/interactify/hooks.rb index 5b52883..b77b282 100644 --- a/lib/interactify/hooks.rb +++ b/lib/interactify/hooks.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Interactify module Hooks def reset diff --git a/spec/integration/all_the_things_integration_spec.rb b/spec/integration/all_the_things_integration_spec.rb index 277f3b5..e971202 100644 --- a/spec/integration/all_the_things_integration_spec.rb +++ b/spec/integration/all_the_things_integration_spec.rb @@ -16,7 +16,9 @@ before do load_interactify_fixtures("each") load_interactify_fixtures("if/") - load "./spec/fixtures/integration_app/app/interactors/all_the_things.rb" + silence_warnings do + load "./spec/fixtures/integration_app/app/interactors/all_the_things.rb" + end end context "without an optional thing" do diff --git a/spec/integration/each_integration_spec.rb b/spec/integration/each_integration_spec.rb index bcb37e2..3bcb639 100644 --- a/spec/integration/each_integration_spec.rb +++ b/spec/integration/each_integration_spec.rb @@ -4,7 +4,9 @@ before do files = Dir.glob("./spec/fixtures/integration_app/app/interactors/each/**/*.rb") files.each do |file| - require file + silence_warnings do + load file + end end end diff --git a/spec/lib/interactify/async/job_maker_spec.rb b/spec/lib/interactify/async/job_maker_spec.rb index 0015a68..339f7a9 100644 --- a/spec/lib/interactify/async/job_maker_spec.rb +++ b/spec/lib/interactify/async/job_maker_spec.rb @@ -31,7 +31,7 @@ describe "concerning JobClass" do describe "#job_klass" do - if Interactify.sidekiq + if Interactify.sidekiq? it "returns a job class" do job_klass = subject.job_klass @@ -43,16 +43,16 @@ expect(job_klass.included_modules).to include(Sidekiq::Job) end else - it "returns a null job_klass" do + it "returns nil" do job_klass = subject.job_klass - expect(job_klass).to eq(Interactify::Async::NullJob) + expect(job_klass).to eq(nil) end end end end - if Interactify.sidekiq + if Interactify.sidekiq? describe "concerning JobClass" do describe "#job_klass" do it "job class includes JOBABLE_OPTS constant" do diff --git a/spec/lib/interactify/wiring_spec.rb b/spec/lib/interactify/wiring_spec.rb index 2cdb248..d069808 100644 --- a/spec/lib/interactify/wiring_spec.rb +++ b/spec/lib/interactify/wiring_spec.rb @@ -12,7 +12,7 @@ end before do - Dir.glob("#{root}**/*.rb").each { |f| require(f) } + Dir.glob("#{root}**/*.rb").each { |f| silence_warnings { require(f) } } end def f(path) @@ -82,4 +82,141 @@ def f(path) end end end + + describe "#ignore_klass?" do + self::DummyInteractor = Class.new do + include Interactify + end + + let(:klass) { self.class::DummyInteractor } + let(:wiring) { described_class.new(root:, ignore:) } + let(:result) { wiring.send(:ignore_klass?, wiring.ignore, klass) } + + def self.it_ignores + it "ignores" do + expect(result).to be true + end + end + + context "with an array of classes" do + let(:ignore) { [klass] } + + it_ignores + end + + context "with a regexp" do + let(:ignore) { [/Dummy/] } + + it_ignores + end + + context "with a string" do + let(:ignore) { ["DummyInteractor"] } + + it_ignores + end + + context "proc condition" do + let(:ignore) { [->(k) { k.to_s =~ /DummyInteractor/ }] } + + it_ignores + end + + context "empty ignore" do + let(:ignore) { [] } + + it "does not ignore class" do + expect(result).to be false + end + end + end + context "with errors" do + let(:organizer1) { double("Organizer1", klass: "SomeOrganizer1") } + let(:organizer2) { double("Organizer2", klass: "SomeOrganizer2") } + + let(:interactor1) { double("Interactor1", klass: "SomeInteractor1") } + let(:interactor2) { double("Interactor2", klass: "SomeInteractor2") } + let(:interactor3) { double("Interactor3", klass: "SomeInteractor3") } + + describe "#format_error" do + let(:missing_keys) { %i[foo bar] } + let(:interactor) { double("Interactor", klass: "SomeInteractor") } + let(:organizer) { double("Organizer", klass: "SomeOrganizer") } + let(:formatted_errors) { [] } + + it "formats the error message correctly" do + subject.send(:format_error, missing_keys, interactor, organizer, formatted_errors) + expect(formatted_errors.first).to include("Missing keys: :foo, :bar") + expect(formatted_errors.first).to include("expected in: SomeInteractor") + expect(formatted_errors.first).to include("called by: SomeOrganizer") + end + end + + describe "#format_errors" do + it "formats and combines all errors" do + all_errors = { + organizer1 => double( + "ErrorContext", + missing_keys: { + interactor1 => %i[key1 key2] + } + ), + organizer2 => double( + "ErrorContext", + missing_keys: { + interactor2 => [:key3] + } + ) + } + + expect(subject).to receive(:format_error).twice.and_call_original + formatted_error_string = subject.format_errors(all_errors) + expect(formatted_error_string).to include("Missing keys: :key1, :key2") + expect(formatted_error_string).to include("Missing keys: :key3") + expect(formatted_error_string).to match(/expected in:.*\n\s+called by:/) + end + end + + describe "#each_error" do + it "yields each error unless the class is ignored" do + all_errors = { + organizer1 => double( + "ErrorContext", + missing_keys: { + interactor1 => [:key1], + interactor2 => [:key2] + } + ), + organizer2 => double( + "ErrorContext", + missing_keys: { + interactor3 => [:key3] + } + ) + } + + allow(subject).to receive(:ignore_klass?).and_return(false) + + expect { |b| subject.each_error(all_errors, &b) } + .to yield_successive_args( + [[:key1], anything, anything], + [[:key2], anything, anything], + [[:key3], anything, anything] + ) + end + + it "does not yield errors for ignored classes" do + all_errors = { + organizer1 => double( + "ErrorContext", + missing_keys: { + interactor1 => [:key1] + } + ) + } + allow(subject).to receive(:ignore_klass?).and_return(true) + expect { |b| subject.each_error(all_errors, &b) }.not_to yield_control + end + end + end end diff --git a/spec/support/spec_support.rb b/spec/support/spec_support.rb index 3060172..2461788 100644 --- a/spec/support/spec_support.rb +++ b/spec/support/spec_support.rb @@ -8,7 +8,7 @@ def load_interactify_fixtures(sub_directory) files = Dir.glob("./spec/fixtures/integration_app/app/interactors/#{sub_directory}/**/*.rb") files.each do |file| - load file + silence_warnings { load file } end end end