sequra-style 1.18.0 → 1.19.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +7 -0
- data/Gemfile.lock +1 -1
- data/lib/rubocop/cop/sequra/no_sidekiq_perform_stubs.rb +44 -7
- data/lib/sequra/style/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 47e2880848be7928ef68435e320055b4d2003f4b321d172094fa77f123f5139e
|
|
4
|
+
data.tar.gz: d8ef008a1b7ba0477c45a19d52eb965d86a994fe5c7c203c67ecc2941f2ac798
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 19d7b3760cf692ed282a02b99416f5d3e6f9fc288a8f5ecf8086e40459215f8665c27fc90f336183e42dcc37823668364a9d8503fa8a462b3afc5ad0d68885cb
|
|
7
|
+
data.tar.gz: 17bd16a9f9499060f4ee8e986c0f7a12e32a5a1f5d28ba494b9b5ca2d6ac8e78dda03254ea83d75c8073bc7823c60a738f171dd492e76910269fd41f1f5574da
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [1.19.0](https://github.com/sequra/sequra-style/compare/v1.18.0...v1.19.0) (2026-07-29)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Features
|
|
7
|
+
|
|
8
|
+
* [COR-1992] Flag mailer .later stubs in NoSidekiqPerformStubs ([#81](https://github.com/sequra/sequra-style/issues/81)) ([b9ea46e](https://github.com/sequra/sequra-style/commit/b9ea46e97649b40aae30bfa4f6cbbc51fe0c72f0))
|
|
9
|
+
|
|
3
10
|
## [1.18.0](https://github.com/sequra/sequra-style/compare/v1.17.0...v1.18.0) (2026-07-28)
|
|
4
11
|
|
|
5
12
|
|
data/Gemfile.lock
CHANGED
|
@@ -2,7 +2,8 @@ module RuboCop
|
|
|
2
2
|
module Cop
|
|
3
3
|
module Sequra
|
|
4
4
|
# Detects RSpec stubs and spy assertions on Sidekiq enqueue methods
|
|
5
|
-
# (`perform_async`, `perform_at`, `perform_in`)
|
|
5
|
+
# (`perform_async`, `perform_at`, `perform_in`) and on the mailer
|
|
6
|
+
# enqueue wrapper (`SomeMailer.later`).
|
|
6
7
|
#
|
|
7
8
|
# Stubbing these methods bypasses `Sidekiq.strict_args!` validation,
|
|
8
9
|
# which lets symbol-keyed hashes (and other non-JSON-native types)
|
|
@@ -12,12 +13,19 @@ module RuboCop
|
|
|
12
13
|
# rescue-swallowers can mask the failure. This pattern caused a
|
|
13
14
|
# silent push-notification drop in COR-1923.
|
|
14
15
|
#
|
|
16
|
+
# `SomeMailer.later` (seQura's `ApplicationMailer.later`) is the
|
|
17
|
+
# standard mailer enqueue path and funnels through
|
|
18
|
+
# `ApplicationMailerJob.perform_async`, so stubbing it bypasses
|
|
19
|
+
# `strict_args!` exactly like stubbing `perform_async` directly.
|
|
20
|
+
# `.later` is only flagged when the stubbed subject is a `*Mailer`
|
|
21
|
+
# constant, so unrelated `.later` methods are left alone.
|
|
22
|
+
#
|
|
15
23
|
# Use `have_enqueued_sidekiq_job` (or `change(SomeJob.jobs, :size)`)
|
|
16
24
|
# instead. Both go through Sidekiq's client middleware chain, so
|
|
17
25
|
# `strict_args!` validates the arguments as it would in production.
|
|
18
26
|
#
|
|
19
27
|
# `.and_call_original` is exempt: the stub spies on the call but
|
|
20
|
-
# then runs the real
|
|
28
|
+
# then runs the real method, which still exercises the
|
|
21
29
|
# serialization contract.
|
|
22
30
|
#
|
|
23
31
|
# @example
|
|
@@ -33,6 +41,9 @@ module RuboCop
|
|
|
33
41
|
# # bad - spy verification on a stubbed enqueue
|
|
34
42
|
# expect(SomeJob).to have_received(:perform_async).with(id)
|
|
35
43
|
#
|
|
44
|
+
# # bad - mailer enqueue wrapper bypasses strict_args! too
|
|
45
|
+
# allow(WelcomeMailer).to receive(:later)
|
|
46
|
+
#
|
|
36
47
|
# # good - exercises strict_args! through Sidekiq's middleware
|
|
37
48
|
# expect(SomeJob).to have_enqueued_sidekiq_job(id, name: "value")
|
|
38
49
|
#
|
|
@@ -43,17 +54,23 @@ module RuboCop
|
|
|
43
54
|
# expect(SomeJob).to receive(:perform_async).and_call_original
|
|
44
55
|
#
|
|
45
56
|
class NoSidekiqPerformStubs < Base
|
|
46
|
-
MSG = "Avoid stubbing
|
|
47
|
-
"Stubs bypass `Sidekiq.strict_args!` validation and can hide
|
|
48
|
-
"(see COR-1923). Use `have_enqueued_sidekiq_job` or
|
|
49
|
-
"Use `.and_call_original` only as an escape hatch.".freeze
|
|
57
|
+
MSG = "Avoid stubbing job/mailer enqueue methods (`perform_async`/`perform_at`/`perform_in`, " \
|
|
58
|
+
"or `Mailer.later`). Stubs bypass `Sidekiq.strict_args!` validation and can hide " \
|
|
59
|
+
"symbol-keyed hash bugs (see COR-1923). Use `have_enqueued_sidekiq_job` or " \
|
|
60
|
+
"`change(Job.jobs, :size)` instead. Use `.and_call_original` only as an escape hatch.".freeze
|
|
61
|
+
|
|
62
|
+
SUBJECT_WRAPPERS = [:allow, :expect, :allow_any_instance_of, :expect_any_instance_of].freeze
|
|
50
63
|
|
|
51
64
|
def_node_matcher :perform_method_stub?, <<~PATTERN
|
|
52
65
|
(send nil? {:receive :have_received} (sym {:perform_async :perform_at :perform_in}))
|
|
53
66
|
PATTERN
|
|
54
67
|
|
|
68
|
+
def_node_matcher :later_method_stub?, <<~PATTERN
|
|
69
|
+
(send nil? {:receive :have_received} (sym :later))
|
|
70
|
+
PATTERN
|
|
71
|
+
|
|
55
72
|
def on_send(node)
|
|
56
|
-
return unless perform_method_stub?(node)
|
|
73
|
+
return unless perform_method_stub?(node) || mailer_later_stub?(node)
|
|
57
74
|
return if chained_with_call_original?(node)
|
|
58
75
|
|
|
59
76
|
add_offense(node)
|
|
@@ -61,6 +78,26 @@ module RuboCop
|
|
|
61
78
|
|
|
62
79
|
private
|
|
63
80
|
|
|
81
|
+
def mailer_later_stub?(node)
|
|
82
|
+
return false unless later_method_stub?(node)
|
|
83
|
+
|
|
84
|
+
subject = stubbed_subject(node)
|
|
85
|
+
subject&.const_type? && subject.const_name.to_s.end_with?("Mailer")
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def stubbed_subject(node)
|
|
89
|
+
current = node
|
|
90
|
+
current = current.parent while current.parent&.send_type? && current.parent.receiver == current
|
|
91
|
+
|
|
92
|
+
matcher = current.parent
|
|
93
|
+
return unless matcher&.send_type?
|
|
94
|
+
|
|
95
|
+
wrapper = matcher.receiver
|
|
96
|
+
return unless wrapper&.send_type? && SUBJECT_WRAPPERS.include?(wrapper.method_name)
|
|
97
|
+
|
|
98
|
+
wrapper.first_argument
|
|
99
|
+
end
|
|
100
|
+
|
|
64
101
|
def chained_with_call_original?(node)
|
|
65
102
|
current = node
|
|
66
103
|
loop do
|
data/lib/sequra/style/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: sequra-style
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.
|
|
4
|
+
version: 1.19.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Sequra engineering
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-07-
|
|
11
|
+
date: 2026-07-30 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: rubocop
|