Skip to content

Commit 442bc62

Browse files
odlpJonRowe
authored andcommitted
Verify ActiveJob job signature matches arguments
Guards against false-positives where both the implementation and have_enqueued_job or have_been_enqueued matchers are passed the wrong arguments which don't match the job's signature. Prior to this change it was possible for the implmentation and spec to pass whilst expecting the wrong number of arguments, or mixing up the positional/keyword arguments. This resulted in a passing test and the implementation failing at runtime.
1 parent 2787525 commit 442bc62

File tree

3 files changed

+63
-0
lines changed

3 files changed

+63
-0
lines changed

lib/rspec/rails/matchers/active_job.rb

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,7 @@ def check(jobs)
103103
@matching_jobs, @unmatching_jobs = jobs.partition do |job|
104104
if job_match?(job) && arguments_match?(job) && queue_match?(job) && at_match?(job)
105105
args = deserialize_arguments(job)
106+
verify_arguments_match_signature!(job, args)
106107
@block.call(*args)
107108
true
108109
else
@@ -152,6 +153,18 @@ def arguments_match?(job)
152153
end
153154
end
154155

156+
def verify_arguments_match_signature!(job, args)
157+
job_method = job.fetch(:job).public_instance_method(:perform)
158+
verify_signature!(job_method, args)
159+
end
160+
161+
def verify_signature!(job_method, args)
162+
signature = Support::MethodSignature.new(job_method)
163+
verifier = Support::StrictSignatureVerifier.new(signature, args)
164+
165+
raise ArgumentError, verifier.error_message unless verifier.valid?
166+
end
167+
155168
def queue_match?(job)
156169
return true unless @queue
157170

lib/rspec/rails/matchers/have_enqueued_mail.rb

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,10 @@ def arguments_match?(job)
8989
super(job)
9090
end
9191

92+
def verify_arguments_match_signature!(_job, _args)
93+
# Not implemented for mailers
94+
end
95+
9296
def base_mailer_args
9397
[mailer_class_name, @method_name.to_s, MAILER_JOB_METHOD]
9498
end

spec/rspec/rails/matchers/active_job_spec.rb

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,20 @@ def self.name; "LoggingJob"; end
6464
end
6565
end
6666

67+
let(:two_args_job) do
68+
Class.new(ActiveJob::Base) do
69+
def perform(one, two); end
70+
def self.name; "TwoArgsJob"; end
71+
end
72+
end
73+
74+
let(:keyword_args_job) do
75+
Class.new(ActiveJob::Base) do
76+
def perform(one:, two:); end
77+
def self.name; "KeywordArgsJob"; end
78+
end
79+
end
80+
6781
before do
6882
ActiveJob::Base.queue_adapter = :test
6983
end
@@ -324,6 +338,22 @@ def perform; raise StandardError; end
324338
}.to have_enqueued_job.with(42, "David")
325339
end
326340

341+
it "fails if the arguments do not match the job's signature" do
342+
expect {
343+
expect {
344+
two_args_job.perform_later(1)
345+
}.to have_enqueued_job.with(1)
346+
}.to raise_error(ArgumentError, /Wrong number of arguments/)
347+
end
348+
349+
it "fails if the job's signature/arguments are mismatched keyword/positional arguments" do
350+
expect {
351+
expect {
352+
keyword_args_job.perform_later(1, 2)
353+
}.to have_enqueued_job.with(1, 2)
354+
}.to raise_error(ArgumentError, /Missing required keyword arguments/)
355+
end
356+
327357
it "passes with provided arguments containing global id object" do
328358
global_id_object = GlobalIdModel.new("42")
329359

@@ -457,6 +487,22 @@ def perform; raise StandardError; end
457487
}.to raise_error(/expected to enqueue exactly 1 jobs, but enqueued 0/)
458488
end
459489

490+
it "fails if the arguments do not match the job's signature" do
491+
two_args_job.perform_later(1)
492+
493+
expect {
494+
expect(two_args_job).to have_been_enqueued.with(1)
495+
}.to raise_error(ArgumentError, /Wrong number of arguments/)
496+
end
497+
498+
it "fails if the job's signature/arguments are mismatched keyword/positional arguments" do
499+
keyword_args_job.perform_later(1, 2)
500+
501+
expect {
502+
expect(keyword_args_job).to have_been_enqueued.with(1, 2)
503+
}.to raise_error(ArgumentError, /Missing required keyword arguments/)
504+
end
505+
460506
it "fails when negated and several jobs enqueued" do
461507
heavy_lifting_job.perform_later
462508
heavy_lifting_job.perform_later

0 commit comments

Comments
 (0)