Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Respect verify partial doubles config when verifying job/mailer arguments #2808

Merged
merged 4 commits into from
Nov 8, 2024
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions lib/rspec/rails/matchers/active_job.rb
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,8 @@ def arguments_match?(job)
end

def detect_args_signature_mismatch(jobs)
return if skip_signature_verification?

jobs.each do |job|
args = deserialize_arguments(job)

Expand All @@ -189,6 +191,11 @@ def detect_args_signature_mismatch(jobs)
nil
end

def skip_signature_verification?
!RSpec::Mocks.configuration.verify_partial_doubles? ||
RSpec::Mocks.configuration.temporarily_suppress_partial_double_verification
end

def check_args_signature_mismatch(job_class, job_method, args)
signature = Support::MethodSignature.new(job_class.public_instance_method(job_method))
verifier = Support::StrictSignatureVerifier.new(signature, args)
Expand Down
1 change: 1 addition & 0 deletions lib/rspec/rails/matchers/have_enqueued_mail.rb
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ def arguments_match?(job)

def detect_args_signature_mismatch(jobs)
return if @method_name.nil?
return if skip_signature_verification?

mailer_class = mailer_class_name.constantize

Expand Down
88 changes: 66 additions & 22 deletions spec/rspec/rails/matchers/active_job_spec.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
require "rspec/rails/feature_check"
require "support/temporary_assignment"

if RSpec::Rails::FeatureCheck.has_active_job?
require "rspec/rails/matchers/active_job"
Expand Down Expand Up @@ -34,6 +35,7 @@ def self.find(_id)

RSpec.describe "ActiveJob matchers", skip: !RSpec::Rails::FeatureCheck.has_active_job? do
include ActiveSupport::Testing::TimeHelpers
include TemporaryAssignment

around do |example|
original_logger = ActiveJob::Base.logger
Expand Down Expand Up @@ -372,20 +374,40 @@ def perform; raise StandardError; end
}.to have_enqueued_job.with(42, "David")
end

it "fails if the arguments do not match the job's signature" do
expect {
describe "verifying the arguments passed match the job's signature" do
it "fails if there is an arity mismatch" do
expect {
two_args_job.perform_later(1)
}.to have_enqueued_job.with(1)
}.to fail_with(/Incorrect arguments passed to TwoArgsJob: Wrong number of arguments/)
end
expect {
two_args_job.perform_later(1)
}.to have_enqueued_job.with(1)
}.to fail_with(/Incorrect arguments passed to TwoArgsJob: Wrong number of arguments/)
end

it "fails if the job's signature/arguments are mismatched keyword/positional arguments" do
expect {
it "fails if there is a keyword/positional arguments mismatch" do
expect {
keyword_args_job.perform_later(1, 2)
}.to have_enqueued_job.with(1, 2)
}.to fail_with(/Incorrect arguments passed to KeywordArgsJob: Missing required keyword arguments/)
expect {
keyword_args_job.perform_later(1, 2)
}.to have_enqueued_job.with(1, 2)
}.to fail_with(/Incorrect arguments passed to KeywordArgsJob: Missing required keyword arguments/)
end

context "with partial double verification disabled" do
it "skips signature checks" do
with_temporary_assignment(RSpec::Mocks.configuration, :verify_partial_doubles, false) {
expect { two_args_job.perform_later(1) }.to have_enqueued_job.with(1)
}
end
end

context "when partial double verification is temporarily suspended" do
it "skips signature checks" do
without_partial_double_verification {
expect {
two_args_job.perform_later(1)
}.to have_enqueued_job.with(1)
}
end
end
end

it "passes with provided arguments containing global id object" do
Expand Down Expand Up @@ -521,20 +543,42 @@ def perform; raise StandardError; end
}.to fail_with(/expected to enqueue exactly 1 jobs, but enqueued 0/)
end

it "fails if the arguments do not match the job's signature" do
two_args_job.perform_later(1)
describe "verifying the arguments passed match the job's signature" do
it "fails if there is an arity mismatch" do
two_args_job.perform_later(1)

expect {
expect(two_args_job).to have_been_enqueued.with(1)
}.to fail_with(/Incorrect arguments passed to TwoArgsJob: Wrong number of arguments/)
end
expect {
expect(two_args_job).to have_been_enqueued.with(1)
}.to fail_with(/Incorrect arguments passed to TwoArgsJob: Wrong number of arguments/)
end

it "fails if the job's signature/arguments are mismatched keyword/positional arguments" do
keyword_args_job.perform_later(1, 2)
it "fails if there is a keyword/positional arguments mismatch" do
keyword_args_job.perform_later(1, 2)

expect {
expect(keyword_args_job).to have_been_enqueued.with(1, 2)
}.to fail_with(/Incorrect arguments passed to KeywordArgsJob: Missing required keyword arguments/)
expect {
expect(keyword_args_job).to have_been_enqueued.with(1, 2)
}.to fail_with(/Incorrect arguments passed to KeywordArgsJob: Missing required keyword arguments/)
end

context "with partial double verification disabled" do
it "skips signature checks" do
keyword_args_job.perform_later(1, 2)

with_temporary_assignment(RSpec::Mocks.configuration, :verify_partial_doubles, false) {
expect(keyword_args_job).to have_been_enqueued.with(1, 2)
}
end
end

context "when partial double verification is temporarily suspended" do
it "skips signature checks" do
keyword_args_job.perform_later(1, 2)

without_partial_double_verification {
expect(keyword_args_job).to have_been_enqueued.with(1, 2)
}
end
end
end

it "fails when negated and several jobs enqueued" do
Expand Down
35 changes: 30 additions & 5 deletions spec/rspec/rails/matchers/have_enqueued_mail_spec.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
require "rspec/rails/feature_check"
require "support/temporary_assignment"

if RSpec::Rails::FeatureCheck.has_active_job?
require "action_mailer"
Expand Down Expand Up @@ -49,6 +50,8 @@ def test_email; end
end

RSpec.describe "HaveEnqueuedMail matchers", skip: !RSpec::Rails::FeatureCheck.has_active_job? do
include TemporaryAssignment

before do
ActiveJob::Base.queue_adapter = :test
end
Expand Down Expand Up @@ -251,12 +254,34 @@ def test_email; end
}.not_to have_enqueued_mail(TestMailer, :email_with_args).with(3, 4)
end

it "fails if the arguments do not match the mailer method's signature" do
expect {
describe "verifying the arguments passed match the mailer's signature" do
it "fails if there is a mismatch" do
expect {
TestMailer.email_with_args(1).deliver_later
}.to have_enqueued_mail(TestMailer, :email_with_args).with(1)
}.to fail_with(/Incorrect arguments passed to TestMailer: Wrong number of arguments/)
expect {
TestMailer.email_with_args(1).deliver_later
}.to have_enqueued_mail(TestMailer, :email_with_args).with(1)
}.to fail_with(/Incorrect arguments passed to TestMailer: Wrong number of arguments/)
end

context "with partial double verification disabled" do
it "skips signature checks" do
with_temporary_assignment(RSpec::Mocks.configuration, :verify_partial_doubles, false) {
expect {
TestMailer.email_with_args(1).deliver_later
}.to have_enqueued_mail(TestMailer, :email_with_args).with(1)
}
end
end

context "when partial double verification is temporarily suspended" do
it "skips signature checks" do
without_partial_double_verification {
expect {
TestMailer.email_with_args(1).deliver_later
}.to have_enqueued_mail(TestMailer, :email_with_args).with(1)
}
end
end
end

it "generates a failure message" do
Expand Down
15 changes: 15 additions & 0 deletions spec/support/temporary_assignment.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# frozen_string_literal: true

module TemporaryAssignment
def with_temporary_assignment(assignee, attribute, temporary_value)
predicate = "#{attribute}?"
attribute_reader = assignee.respond_to?(predicate) ? predicate : attribute
attribute_writer = "#{attribute}="

original_value = assignee.public_send(attribute_reader)
assignee.public_send(attribute_writer, temporary_value)
yield
ensure
assignee.public_send(attribute_writer, original_value)
end
end