karafka-testing 2.5.4 → 2.5.5

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a0bac7810f07fb9324256c1cdb77d41a703a9b4d9333de1f191d74502fc93948
4
- data.tar.gz: 1d4a0400c7e7784f377eae9a2bf94ff4046a8c21214844019a2300c71b1c4e9c
3
+ metadata.gz: addea79d51dc5c967ee59a86e455b701b76961fbe5262d59af5749c57c9a0099
4
+ data.tar.gz: f1844e8a511ee458365b39ba36f4f200e6338c03742ba3350f7f66c085bf2433
5
5
  SHA512:
6
- metadata.gz: 27c816ab440addf35afde363c2ad742b02089ea2b1b863e33087d80d2284bac83b6cbe1bc62fee3eebfff7bd50126a80e8f10fe9c9c9b1df9c8396b00cb7af15
7
- data.tar.gz: 31ef6e3808dc2e2c5263c981974c0b360749c02cf8ac8d3a9dee9339f6dd909b2bdd871e9bcc33ffcbcc987ba240e005828e556fae46e78563cc970278e20e14
6
+ metadata.gz: cc98c964bbbea8e5e31e0ba65726b130e81e00bfe392fd6b75a30be534c7f923d4da00f5d96c4688cf04b3faaa1af4e7c4048da71205d345f94a9ceb90a46fb3
7
+ data.tar.gz: a63c804f23232ce992a9f9e013ea47560ed39317e47d1bf4d929a0b30390e78a29047fb2261e19ee040d37f42bc7486e90cf1476cedbfae88286ba27d3f37eca
@@ -17,12 +17,11 @@ jobs:
17
17
  specs:
18
18
  timeout-minutes: 30
19
19
  runs-on: ubuntu-latest
20
- needs: diffend
21
20
  strategy:
22
21
  fail-fast: false
23
22
  matrix:
24
23
  ruby:
25
- - '3.5.0-preview1'
24
+ - '4.0.0-preview2'
26
25
  - '3.4'
27
26
  - '3.3'
28
27
  - '3.2'
@@ -30,7 +29,7 @@ jobs:
30
29
  - ruby: '3.3'
31
30
  coverage: 'true'
32
31
  steps:
33
- - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
32
+ - uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
34
33
  with:
35
34
  fetch-depth: 0
36
35
 
@@ -38,7 +37,7 @@ jobs:
38
37
  run: "[ -e $APT_DEPS ] || sudo apt-get install -y --no-install-recommends $APT_DEPS"
39
38
 
40
39
  - name: Set up Ruby
41
- uses: ruby/setup-ruby@0481980f17b760ef6bca5e8c55809102a0af1e5a # v1.263.0
40
+ uses: ruby/setup-ruby@8aeb6ff8030dd539317f8e1769a044873b56ea71 # v1.268.0
42
41
  with:
43
42
  ruby-version: ${{matrix.ruby}}
44
43
  bundler: 'latest'
@@ -53,29 +52,10 @@ jobs:
53
52
  run: |
54
53
  bundle install --jobs 4 --retry 3
55
54
 
56
- diffend:
57
- timeout-minutes: 5
58
- runs-on: ubuntu-latest
59
- strategy:
60
- fail-fast: false
61
- steps:
62
- - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
63
- with:
64
- fetch-depth: 0
65
-
66
- - name: Set up Ruby
67
- uses: ruby/setup-ruby@0481980f17b760ef6bca5e8c55809102a0af1e5a # v1.263.0
68
- with:
69
- ruby-version: 3.3
70
-
71
- - name: Install latest bundler
72
- run: gem install bundler --no-document
73
-
74
- - name: Install Diffend plugin
75
- run: bundle plugin install diffend
76
-
77
- - name: Bundle Secure
78
- run: bundle secure
55
+ - name: Run all tests
56
+ env:
57
+ GITHUB_COVERAGE: ${{matrix.coverage}}
58
+ run: bundle exec rspec
79
59
 
80
60
  coditsu:
81
61
  timeout-minutes: 5
@@ -83,7 +63,7 @@ jobs:
83
63
  strategy:
84
64
  fail-fast: false
85
65
  steps:
86
- - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
66
+ - uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
87
67
  with:
88
68
  fetch-depth: 0
89
69
 
@@ -104,14 +84,26 @@ jobs:
104
84
  - name: Run Coditsu
105
85
  run: ./coditsu_script.sh
106
86
 
87
+ yard-lint:
88
+ timeout-minutes: 5
89
+ runs-on: ubuntu-latest
90
+ steps:
91
+ - uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
92
+ - name: Set up Ruby
93
+ uses: ruby/setup-ruby@8aeb6ff8030dd539317f8e1769a044873b56ea71 # v1.268.0
94
+ with:
95
+ ruby-version: '3.4.7'
96
+ bundler-cache: true
97
+ - run: bundle exec yard-lint lib/
98
+
107
99
  ci-success:
108
100
  name: CI Success
109
101
  runs-on: ubuntu-latest
110
102
  if: always()
111
103
  needs:
112
- - diffend
113
104
  - coditsu
114
105
  - specs
106
+ - yard-lint
115
107
  steps:
116
108
  - name: Check all jobs passed
117
109
  if: |
@@ -19,12 +19,12 @@ jobs:
19
19
  id-token: write
20
20
 
21
21
  steps:
22
- - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
22
+ - uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
23
23
  with:
24
24
  fetch-depth: 0
25
25
 
26
26
  - name: Set up Ruby
27
- uses: ruby/setup-ruby@0481980f17b760ef6bca5e8c55809102a0af1e5a # v1.263.0
27
+ uses: ruby/setup-ruby@8aeb6ff8030dd539317f8e1769a044873b56ea71 # v1.268.0
28
28
  with:
29
29
  bundler-cache: false
30
30
 
@@ -32,4 +32,4 @@ jobs:
32
32
  run: |
33
33
  bundle install --jobs 4 --retry 3
34
34
 
35
- - uses: rubygems/release-gem@a25424ba2ba8b387abc8ef40807c2c85b96cbe32 # v1.1.1
35
+ - uses: rubygems/release-gem@1c162a739e8b4cb21a676e97b087e8268d8fc40b # v1.1.2
@@ -13,7 +13,7 @@ jobs:
13
13
  if: github.repository_owner == 'karafka'
14
14
  steps:
15
15
  - name: Trigger wiki refresh
16
- uses: peter-evans/repository-dispatch@ff45666b9427631e3450c54a1bcbee4d9ff4d7c0 # v3.0.0
16
+ uses: peter-evans/repository-dispatch@28959ce8df70de7be546dd1250a005dd32156697 # v4.0.1
17
17
  with:
18
18
  token: ${{ secrets.WIKI_REPO_TOKEN }}
19
19
  repository: karafka/wiki
@@ -7,7 +7,7 @@ jobs:
7
7
  verify_action_pins:
8
8
  runs-on: ubuntu-latest
9
9
  steps:
10
- - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
10
+ - uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
11
11
  - name: Check SHA pins
12
12
  run: |
13
13
  if grep -E -r "uses: .*/.*@(v[0-9]+|main|master)($|[[:space:]]|$)" --include="*.yml" --include="*.yaml" .github/workflows/ | grep -v "#"; then
data/.rspec CHANGED
@@ -1 +1,2 @@
1
1
  --require spec_helper
2
+ --format documentation
data/.ruby-version CHANGED
@@ -1 +1 @@
1
- 3.4.6
1
+ 3.4.7
data/.yard-lint.yml ADDED
@@ -0,0 +1,174 @@
1
+ # YARD-Lint Configuration
2
+ # See https://github.com/mensfeld/yard-lint for documentation
3
+
4
+ # Global settings for all validators
5
+ AllValidators:
6
+ # YARD command-line options (applied to all validators by default)
7
+ YardOptions:
8
+ - --private
9
+ - --protected
10
+
11
+ # Global file exclusion patterns
12
+ Exclude:
13
+ - '\.git'
14
+ - 'vendor/**/*'
15
+ - 'node_modules/**/*'
16
+ - 'spec/**/*'
17
+ - 'test/**/*'
18
+
19
+ # Exit code behavior (error, warning, convention, never)
20
+ FailOnSeverity: convention
21
+
22
+ # Minimum documentation coverage percentage (0-100)
23
+ # Fails if coverage is below this threshold
24
+ MinCoverage: 99.0
25
+
26
+ # Diff mode settings
27
+ DiffMode:
28
+ # Default base ref for --diff (auto-detects main/master if not specified)
29
+ DefaultBaseRef: ~
30
+
31
+ # Documentation validators
32
+ Documentation/UndocumentedObjects:
33
+ Description: 'Checks for classes, modules, and methods without documentation.'
34
+ Enabled: true
35
+ Severity: error
36
+ ExcludedMethods:
37
+ - 'initialize/0' # Exclude parameter-less initialize
38
+ - '/^_/' # Exclude private methods (by convention)
39
+
40
+ Documentation/UndocumentedMethodArguments:
41
+ Description: 'Checks for method parameters without @param tags.'
42
+ Enabled: true
43
+ Severity: error
44
+
45
+ Documentation/UndocumentedBooleanMethods:
46
+ Description: 'Checks that question mark methods document their boolean return.'
47
+ Enabled: true
48
+ Severity: error
49
+
50
+ Documentation/UndocumentedOptions:
51
+ Description: 'Detects methods with options hash parameters but no @option tags.'
52
+ Enabled: true
53
+ Severity: error
54
+
55
+ Documentation/MarkdownSyntax:
56
+ Description: 'Detects common markdown syntax errors in documentation.'
57
+ Enabled: true
58
+ Severity: error
59
+
60
+ # Tags validators
61
+ Tags/Order:
62
+ Description: 'Enforces consistent ordering of YARD tags.'
63
+ Enabled: true
64
+ Severity: error
65
+ EnforcedOrder:
66
+ - param
67
+ - option
68
+ - return
69
+ - raise
70
+ - example
71
+
72
+ Tags/InvalidTypes:
73
+ Description: 'Validates type definitions in @param, @return, @option tags.'
74
+ Enabled: true
75
+ Severity: error
76
+ ValidatedTags:
77
+ - param
78
+ - option
79
+ - return
80
+
81
+ Tags/TypeSyntax:
82
+ Description: 'Validates YARD type syntax using YARD parser.'
83
+ Enabled: true
84
+ Severity: error
85
+ ValidatedTags:
86
+ - param
87
+ - option
88
+ - return
89
+ - yieldreturn
90
+
91
+ Tags/MeaninglessTag:
92
+ Description: 'Detects @param/@option tags on classes, modules, or constants.'
93
+ Enabled: true
94
+ Severity: error
95
+ CheckedTags:
96
+ - param
97
+ - option
98
+ InvalidObjectTypes:
99
+ - class
100
+ - module
101
+ - constant
102
+
103
+ Tags/CollectionType:
104
+ Description: 'Validates Hash collection syntax consistency.'
105
+ Enabled: true
106
+ Severity: error
107
+ EnforcedStyle: long # 'long' for Hash{K => V} (YARD standard), 'short' for {K => V}
108
+ ValidatedTags:
109
+ - param
110
+ - option
111
+ - return
112
+ - yieldreturn
113
+
114
+ Tags/TagTypePosition:
115
+ Description: 'Validates type annotation position in tags.'
116
+ Enabled: true
117
+ Severity: error
118
+ CheckedTags:
119
+ - param
120
+ - option
121
+ # EnforcedStyle: 'type_after_name' (YARD standard: @param name [Type])
122
+ # or 'type_first' (@param [Type] name)
123
+ EnforcedStyle: type_after_name
124
+
125
+ Tags/ApiTags:
126
+ Description: 'Enforces @api tags on public objects.'
127
+ Enabled: false # Opt-in validator
128
+ Severity: error
129
+ AllowedApis:
130
+ - public
131
+ - private
132
+ - internal
133
+
134
+ Tags/OptionTags:
135
+ Description: 'Requires @option tags for methods with options parameters.'
136
+ Enabled: true
137
+ Severity: error
138
+
139
+ # Warnings validators - catches YARD parser errors
140
+ Warnings/UnknownTag:
141
+ Description: 'Detects unknown YARD tags.'
142
+ Enabled: true
143
+ Severity: error
144
+
145
+ Warnings/UnknownDirective:
146
+ Description: 'Detects unknown YARD directives.'
147
+ Enabled: true
148
+ Severity: error
149
+
150
+ Warnings/InvalidTagFormat:
151
+ Description: 'Detects malformed tag syntax.'
152
+ Enabled: true
153
+ Severity: error
154
+
155
+ Warnings/InvalidDirectiveFormat:
156
+ Description: 'Detects malformed directive syntax.'
157
+ Enabled: true
158
+ Severity: error
159
+
160
+ Warnings/DuplicatedParameterName:
161
+ Description: 'Detects duplicate @param tags.'
162
+ Enabled: true
163
+ Severity: error
164
+
165
+ Warnings/UnknownParameterName:
166
+ Description: 'Detects @param tags for non-existent parameters.'
167
+ Enabled: true
168
+ Severity: error
169
+
170
+ # Semantic validators
171
+ Semantic/AbstractMethods:
172
+ Description: 'Ensures @abstract methods do not have real implementations.'
173
+ Enabled: true
174
+ Severity: error
data/CHANGELOG.md CHANGED
@@ -1,8 +1,12 @@
1
1
  # Karafka Testing Changelog
2
2
 
3
+ ## 2.5.5 (2025-11-28)
4
+ - [Feature] Add `produce_to` method for explicit consumer targeting in multi-consumer scenarios.
5
+ - [Fix] Fix consumer mapping collision when multiple consumer groups listen to the same topic.
6
+
3
7
  ## 2.5.4 (2025-10-03)
4
8
  - [Fix] Topic present in second dynamically built subscription group not available for dispatch (#291).
5
- - [Change] Remove Ruby `3.1` according to the EOL schedule.
9
+ - **[EOL]** Remove Ruby `3.1` according to the EOL schedule.
6
10
 
7
11
  ## 2.5.3 (2025-08-12)
8
12
  - [Fix] Allow testing consumers with Pro expansions (i.e. Piping).
@@ -19,7 +23,7 @@
19
23
  - [Maintenance] Release matching Karafka `2.5.0` release.
20
24
 
21
25
  ## 2.4.7 (2025-04-01)
22
- - **[Breaking]** Drop Ruby `3.0` support according to the EOL schedule.
26
+ - **[EOL]** Drop Ruby `3.0` support according to the EOL schedule.
23
27
  - **[Fix]** Check not only that `Mocha` is loaded but also that its stubs are used.
24
28
 
25
29
  ## 2.4.6 (2024-07-31)
@@ -44,7 +48,7 @@
44
48
  - [Fix] Fix instance variable in minitest helper (tldn0718)
45
49
 
46
50
  ## 2.4.0 (2024-04-26)
47
- - **[Breaking]** Drop Ruby `2.7` support.
51
+ - **[EOL]** Drop Ruby `2.7` support.
48
52
  - [Refactor] Extract common components for Minitest and RSpec.
49
53
  - [Fix] Support again `require: false` on gem loading.
50
54
  - [Fix] Fix a case where multiplexed SG would fail with `TopicInManyConsumerGroupsError`
@@ -153,7 +157,7 @@
153
157
  - Change the API to be more comprehensive
154
158
  - Update to work with Karafka 2.0
155
159
  - Support for Ruby 3.1
156
- - Drop support for ruby 2.6
160
+ - **[EOL]** Drop support for ruby 2.6
157
161
 
158
162
  ## 1.4.*
159
163
 
data/Gemfile CHANGED
@@ -2,6 +2,12 @@
2
2
 
3
3
  source 'https://rubygems.org'
4
4
 
5
- plugin 'diffend'
6
-
7
5
  gemspec
6
+
7
+ group :test do
8
+ gem 'ostruct'
9
+ gem 'rspec'
10
+ gem 'simplecov'
11
+ gem 'warning'
12
+ gem 'yard-lint'
13
+ end
data/Gemfile.lock CHANGED
@@ -1,46 +1,73 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- karafka-testing (2.5.4)
4
+ karafka-testing (2.5.5)
5
5
  karafka (>= 2.5.0, < 2.6.0)
6
6
  waterdrop (>= 2.8.0)
7
7
 
8
8
  GEM
9
9
  remote: https://rubygems.org/
10
10
  specs:
11
- base64 (0.2.0)
12
- ffi (1.17.0)
13
- ffi (1.17.0-x86_64-linux-gnu)
14
- json (2.13.2)
15
- karafka (2.5.0)
11
+ base64 (0.3.0)
12
+ diff-lcs (1.6.2)
13
+ docile (1.4.1)
14
+ ffi (1.17.2)
15
+ ffi (1.17.2-x86_64-linux-gnu)
16
+ json (2.15.1)
17
+ karafka (2.5.1)
16
18
  base64 (~> 0.2)
17
- karafka-core (>= 2.5.2, < 2.6.0)
18
- karafka-rdkafka (>= 0.19.5)
19
- waterdrop (>= 2.8.3, < 3.0.0)
19
+ karafka-core (>= 2.5.6, < 2.6.0)
20
+ karafka-rdkafka (>= 0.22.0)
21
+ waterdrop (>= 2.8.9, < 3.0.0)
20
22
  zeitwerk (~> 2.3)
21
- karafka-core (2.5.6)
23
+ karafka-core (2.5.7)
22
24
  karafka-rdkafka (>= 0.20.0)
23
25
  logger (>= 1.6.0)
24
- karafka-rdkafka (0.21.0)
26
+ karafka-rdkafka (0.22.2)
25
27
  ffi (~> 1.15)
26
28
  json (> 2.0)
27
29
  logger
28
30
  mini_portile2 (~> 2.6)
29
31
  rake (> 12)
30
- karafka-rdkafka (0.21.0-x86_64-linux-gnu)
32
+ karafka-rdkafka (0.22.2-x86_64-linux-gnu)
31
33
  ffi (~> 1.15)
32
34
  json (> 2.0)
33
35
  logger
34
36
  mini_portile2 (~> 2.6)
35
37
  rake (> 12)
36
38
  logger (1.7.0)
37
- mini_portile2 (2.8.7)
38
- rake (13.2.1)
39
- waterdrop (2.8.3)
39
+ mini_portile2 (2.8.9)
40
+ ostruct (0.6.3)
41
+ rake (13.3.0)
42
+ rspec (3.13.2)
43
+ rspec-core (~> 3.13.0)
44
+ rspec-expectations (~> 3.13.0)
45
+ rspec-mocks (~> 3.13.0)
46
+ rspec-core (3.13.6)
47
+ rspec-support (~> 3.13.0)
48
+ rspec-expectations (3.13.5)
49
+ diff-lcs (>= 1.2.0, < 2.0)
50
+ rspec-support (~> 3.13.0)
51
+ rspec-mocks (3.13.7)
52
+ diff-lcs (>= 1.2.0, < 2.0)
53
+ rspec-support (~> 3.13.0)
54
+ rspec-support (3.13.6)
55
+ simplecov (0.22.0)
56
+ docile (~> 1.1)
57
+ simplecov-html (~> 0.11)
58
+ simplecov_json_formatter (~> 0.1)
59
+ simplecov-html (0.13.2)
60
+ simplecov_json_formatter (0.1.4)
61
+ warning (1.5.0)
62
+ waterdrop (2.8.12)
40
63
  karafka-core (>= 2.4.9, < 3.0.0)
41
- karafka-rdkafka (>= 0.19.1)
64
+ karafka-rdkafka (>= 0.20.0)
42
65
  zeitwerk (~> 2.3)
43
- zeitwerk (2.6.16)
66
+ yard (0.9.37)
67
+ yard-lint (1.2.3)
68
+ yard (~> 0.9)
69
+ zeitwerk (~> 2.6)
70
+ zeitwerk (2.7.3)
44
71
 
45
72
  PLATFORMS
46
73
  ruby
@@ -48,6 +75,11 @@ PLATFORMS
48
75
 
49
76
  DEPENDENCIES
50
77
  karafka-testing!
78
+ ostruct
79
+ rspec
80
+ simplecov
81
+ warning
82
+ yard-lint
51
83
 
52
84
  BUNDLED WITH
53
85
  2.6.3
@@ -92,7 +92,7 @@ module Karafka
92
92
  consumer_obj = if defined?(@consumer)
93
93
  @consumer
94
94
  else
95
- @_karafka_consumer_mappings&.dig(message[:topic])
95
+ _karafka_find_consumer_for_message(message)
96
96
  end
97
97
  # Consumer needs to be defined in order to pass messages to it
98
98
  return unless defined?(consumer_obj)
@@ -103,6 +103,9 @@ module Karafka
103
103
  # We target to the consumer only messages that were produced to it, since specs may also
104
104
  # produce other messages targeting other topics
105
105
  return unless message[:topic] == consumer_obj.topic.name
106
+ # If consumer_group is explicitly specified, verify it matches
107
+ return if message[:consumer_group] &&
108
+ message[:consumer_group].to_s != consumer_obj.topic.consumer_group.name
106
109
 
107
110
  # Build message metadata and copy any metadata that would come from the message
108
111
  metadata = _karafka_message_metadata_defaults(consumer_obj)
@@ -144,7 +147,8 @@ module Karafka
144
147
  elsif defined?(@consumer)
145
148
  @consumer.topic.name
146
149
  else
147
- @_karafka_consumer_mappings&.keys&.last
150
+ last_consumer = @_karafka_consumer_mappings&.values&.last
151
+ last_consumer&.topic&.name
148
152
  end
149
153
  Karafka.producer.produce_sync(
150
154
  {
@@ -165,8 +169,52 @@ module Karafka
165
169
  @_karafka_consumer_messages
166
170
  end
167
171
 
172
+ # Produces message to a specific consumer instance
173
+ # Use when testing multiple consumers for the same topic
174
+ #
175
+ # @param consumer_instance [Object] the consumer to produce to
176
+ # @param payload [String] message content (usually serialized JSON) to deliver to the
177
+ # consumer
178
+ # @param metadata [Hash] any metadata to dispatch alongside the payload
179
+ #
180
+ # @example Produce to specific consumer when multiple exist for same topic
181
+ # consumer1 = @karafka.consumer_for(:events, :analytics_group)
182
+ # consumer2 = @karafka.consumer_for(:events, :notifications_group)
183
+ # @karafka.produce_to(consumer1, { 'event' => 'click' }.to_json)
184
+ def _karafka_produce_to(consumer_instance, payload, metadata = {})
185
+ _karafka_produce(
186
+ payload,
187
+ metadata.merge(
188
+ topic: consumer_instance.topic.name,
189
+ consumer_group: consumer_instance.topic.consumer_group.name
190
+ )
191
+ )
192
+ end
193
+
168
194
  private
169
195
 
196
+ # Finds a consumer for the given message with backward-compatible fallback
197
+ # @param message [Hash] the message being routed
198
+ # @return [Object, nil] the consumer instance or nil
199
+ def _karafka_find_consumer_for_message(message)
200
+ return nil unless @_karafka_consumer_mappings
201
+
202
+ topic_name = message[:topic]
203
+ consumer_group = message[:consumer_group]
204
+
205
+ if consumer_group
206
+ # Explicit consumer group - find by composite key pattern
207
+ @_karafka_consumer_mappings.values.find do |c|
208
+ c.topic.name == topic_name && c.topic.consumer_group.name == consumer_group.to_s
209
+ end
210
+ else
211
+ # No consumer group specified - find all consumers for this topic
212
+ matching = @_karafka_consumer_mappings.values.select { |c| c.topic.name == topic_name }
213
+ # If exactly one consumer matches, use it (backward compatible)
214
+ matching.size == 1 ? matching.first : nil
215
+ end
216
+ end
217
+
170
218
  # @param consumer_obj [Karafka::BaseConsumer] consumer reference
171
219
  # @return [Hash] message default options
172
220
  def _karafka_message_metadata_defaults(consumer_obj)
@@ -200,10 +248,10 @@ module Karafka
200
248
  @consumer.coordinator = coordinators.find_or_create(topic.name, 0)
201
249
  @consumer.coordinator.seek_offset = 0
202
250
  # Indicate usage as for tests no direct enqueuing happens
203
- @consumer.instance_variable_set('@used', true)
251
+ @consumer.instance_variable_set(:@used, true)
204
252
  expansions = processing_cfg.expansions_selector.find(topic)
205
253
  expansions.each { |expansion| @consumer.singleton_class.include(expansion) }
206
- @_karafka_consumer_mappings[topic.name] = @consumer
254
+ @_karafka_consumer_mappings[topic.id] = @consumer
207
255
  @consumer
208
256
  end
209
257
  end
@@ -11,14 +11,19 @@ module Karafka
11
11
  @minitest_example = minitest_example
12
12
  end
13
13
 
14
- # @param args Anything that the `#_karafka_consumer_for` accepts
15
- def consumer_for(*args)
16
- @minitest_example._karafka_consumer_for(*args)
14
+ # Forwards all arguments to `#_karafka_consumer_for`
15
+ def consumer_for(*)
16
+ @minitest_example._karafka_consumer_for(*)
17
17
  end
18
18
 
19
- # @param args Anything that `#_karafka_produce` accepts
20
- def produce(*args)
21
- @minitest_example._karafka_produce(*args)
19
+ # Forwards all arguments to `#_karafka_produce`
20
+ def produce(*)
21
+ @minitest_example._karafka_produce(*)
22
+ end
23
+
24
+ # Forwards all arguments to `#_karafka_produce_to`
25
+ def produce_to(*)
26
+ @minitest_example._karafka_produce_to(*)
22
27
  end
23
28
 
24
29
  # @return [Array<Hash>] messages produced via `Karafka#producer`
@@ -42,8 +42,8 @@ module Karafka
42
42
  base.before(:context) do
43
43
  Karafka::Testing.ensure_karafka_initialized!
44
44
  @_karafka_shared_producer_client = WaterDrop::Clients::Dummy.new(-1)
45
- Karafka.producer.instance_variable_set(:'@client', @_karafka_shared_producer_client)
46
- Karafka.producer.instance_variable_set(:'@pid', ::Process.pid)
45
+ Karafka.producer.instance_variable_set(:@client, @_karafka_shared_producer_client)
46
+ Karafka.producer.instance_variable_set(:@pid, ::Process.pid)
47
47
  end
48
48
 
49
49
  base.prepend_before do
@@ -107,7 +107,7 @@ module Karafka
107
107
  consumer_obj = if defined?(consumer)
108
108
  consumer
109
109
  else
110
- @_karafka_consumer_mappings&.dig(message[:topic])
110
+ _karafka_find_consumer_for_message(message)
111
111
  end
112
112
  # Consumer needs to be defined in order to pass messages to it
113
113
  return unless consumer_obj
@@ -118,6 +118,9 @@ module Karafka
118
118
  # We target to the consumer only messages that were produced to it, since specs may also
119
119
  # produce other messages targeting other topics
120
120
  return unless message[:topic] == consumer_obj.topic.name
121
+ # If consumer_group is explicitly specified, verify it matches
122
+ return if message[:consumer_group] &&
123
+ message[:consumer_group].to_s != consumer_obj.topic.consumer_group.name
121
124
 
122
125
  # Build message metadata and copy any metadata that would come from the message
123
126
  metadata = _karafka_message_metadata_defaults(consumer_obj)
@@ -160,7 +163,8 @@ module Karafka
160
163
  elsif defined?(consumer)
161
164
  consumer.topic.name
162
165
  else
163
- @_karafka_consumer_mappings&.keys&.last
166
+ last_consumer = @_karafka_consumer_mappings&.values&.last
167
+ last_consumer&.topic&.name
164
168
  end
165
169
  Karafka.producer.produce_sync(
166
170
  {
@@ -175,8 +179,55 @@ module Karafka
175
179
  _karafka_producer_client.messages
176
180
  end
177
181
 
182
+ # Produces message to a specific consumer instance
183
+ # Use when testing multiple consumers for the same topic
184
+ #
185
+ # @param consumer_instance [Object] the consumer to produce to
186
+ # @param payload [String] message content (usually serialized JSON) to deliver to the
187
+ # consumer
188
+ # @param metadata [Hash] any metadata to dispatch alongside the payload
189
+ #
190
+ # @example Produce to specific consumer when multiple exist for same topic
191
+ # let(:consumer1) { karafka.consumer_for(:events, :analytics_group) }
192
+ # let(:consumer2) { karafka.consumer_for(:events, :notifications_group) }
193
+ #
194
+ # before do
195
+ # karafka.produce_to(consumer1, { 'event' => 'click' }.to_json)
196
+ # end
197
+ def _karafka_produce_to(consumer_instance, payload, metadata = {})
198
+ _karafka_produce(
199
+ payload,
200
+ metadata.merge(
201
+ topic: consumer_instance.topic.name,
202
+ consumer_group: consumer_instance.topic.consumer_group.name
203
+ )
204
+ )
205
+ end
206
+
178
207
  private
179
208
 
209
+ # Finds a consumer for the given message with backward-compatible fallback
210
+ # @param message [Hash] the message being routed
211
+ # @return [Object, nil] the consumer instance or nil
212
+ def _karafka_find_consumer_for_message(message)
213
+ return nil unless @_karafka_consumer_mappings
214
+
215
+ topic_name = message[:topic]
216
+ consumer_group = message[:consumer_group]
217
+
218
+ if consumer_group
219
+ # Explicit consumer group - find by composite key pattern
220
+ @_karafka_consumer_mappings.values.find do |c|
221
+ c.topic.name == topic_name && c.topic.consumer_group.name == consumer_group.to_s
222
+ end
223
+ else
224
+ # No consumer group specified - find all consumers for this topic
225
+ matching = @_karafka_consumer_mappings.values.select { |c| c.topic.name == topic_name }
226
+ # If exactly one consumer matches, use it (backward compatible)
227
+ matching.size == 1 ? matching.first : nil
228
+ end
229
+ end
230
+
180
231
  # @param consumer_obj [Karafka::BaseConsumer] consumer reference
181
232
  # @return [Hash] message default options
182
233
  def _karafka_message_metadata_defaults(consumer_obj)
@@ -211,11 +262,11 @@ module Karafka
211
262
  consumer.coordinator = coordinators.find_or_create(topic.name, 0)
212
263
  consumer.coordinator.seek_offset = 0
213
264
  # Indicate usage as for tests no direct enqueuing happens
214
- consumer.instance_variable_set('@used', true)
265
+ consumer.instance_variable_set(:@used, true)
215
266
  expansions = processing_cfg.expansions_selector.find(topic)
216
267
  expansions.each { |expansion| consumer.singleton_class.include(expansion) }
217
268
 
218
- @_karafka_consumer_mappings[topic.name] = consumer
269
+ @_karafka_consumer_mappings[topic.id] = consumer
219
270
  consumer
220
271
  end
221
272
  end
@@ -11,14 +11,19 @@ module Karafka
11
11
  @rspec_example = rspec_example
12
12
  end
13
13
 
14
- # @param args Anything that the `#_karafka_consumer_for` accepts
15
- def consumer_for(*args)
16
- @rspec_example._karafka_consumer_for(*args)
14
+ # Forwards all arguments to `#_karafka_consumer_for`
15
+ def consumer_for(*)
16
+ @rspec_example._karafka_consumer_for(*)
17
17
  end
18
18
 
19
- # @param args Anything that `#_karafka_produce` accepts
20
- def produce(*args)
21
- @rspec_example._karafka_produce(*args)
19
+ # Forwards all arguments to `#_karafka_produce`
20
+ def produce(*)
21
+ @rspec_example._karafka_produce(*)
22
+ end
23
+
24
+ # Forwards all arguments to `#_karafka_produce_to`
25
+ def produce_to(*)
26
+ @rspec_example._karafka_produce_to(*)
22
27
  end
23
28
 
24
29
  # @return [Array<Hash>] messages produced via `Karafka#producer`
@@ -4,6 +4,6 @@
4
4
  module Karafka
5
5
  module Testing
6
6
  # Current version of gem. It should match Karafka framework version
7
- VERSION = '2.5.4'
7
+ VERSION = '2.5.5'
8
8
  end
9
9
  end
data/renovate.json CHANGED
@@ -3,16 +3,9 @@
3
3
  "extends": [
4
4
  "config:recommended"
5
5
  ],
6
+ "minimumReleaseAge": "7 days",
6
7
  "github-actions": {
7
8
  "enabled": true,
8
9
  "pinDigests": true
9
- },
10
- "packageRules": [
11
- {
12
- "matchManagers": [
13
- "github-actions"
14
- ],
15
- "minimumReleaseAge": "7 days"
16
- }
17
- ]
10
+ }
18
11
  }
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: karafka-testing
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.5.4
4
+ version: 2.5.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Maciej Mensfeld
@@ -51,7 +51,6 @@ extensions: []
51
51
  extra_rdoc_files: []
52
52
  files:
53
53
  - ".coditsu/ci.yml"
54
- - ".diffend.yml"
55
54
  - ".github/CODEOWNERS"
56
55
  - ".github/FUNDING.yml"
57
56
  - ".github/ISSUE_TEMPLATE/bug_report.md"
@@ -64,6 +63,7 @@ files:
64
63
  - ".rspec"
65
64
  - ".ruby-gemset"
66
65
  - ".ruby-version"
66
+ - ".yard-lint.yml"
67
67
  - 2.0-Upgrade.md
68
68
  - CHANGELOG.md
69
69
  - Gemfile
data/.diffend.yml DELETED
@@ -1,3 +0,0 @@
1
- project_id: 'd19af75c-1296-45bd-950f-3072b0134497'
2
- shareable_id: '0bddcda8-7b70-4bf4-aa80-8bbc3b16106a'
3
- shareable_key: '6d808641-cde2-49c6-9fc3-fbc2fc86a733'