karafka-testing 2.5.5 → 2.5.6

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.
data/Gemfile.lint.lock ADDED
@@ -0,0 +1,108 @@
1
+ GEM
2
+ remote: https://rubygems.org/
3
+ specs:
4
+ ast (2.4.3)
5
+ json (2.18.0)
6
+ language_server-protocol (3.17.0.5)
7
+ lint_roller (1.1.0)
8
+ parallel (1.27.0)
9
+ parser (3.3.10.1)
10
+ ast (~> 2.4.1)
11
+ racc
12
+ prism (1.8.0)
13
+ racc (1.8.1)
14
+ rainbow (3.1.1)
15
+ regexp_parser (2.11.3)
16
+ rubocop (1.82.1)
17
+ json (~> 2.3)
18
+ language_server-protocol (~> 3.17.0.2)
19
+ lint_roller (~> 1.1.0)
20
+ parallel (~> 1.10)
21
+ parser (>= 3.3.0.2)
22
+ rainbow (>= 2.2.2, < 4.0)
23
+ regexp_parser (>= 2.9.3, < 3.0)
24
+ rubocop-ast (>= 1.48.0, < 2.0)
25
+ ruby-progressbar (~> 1.7)
26
+ unicode-display_width (>= 2.4.0, < 4.0)
27
+ rubocop-ast (1.49.0)
28
+ parser (>= 3.3.7.2)
29
+ prism (~> 1.7)
30
+ rubocop-minitest (0.39.1)
31
+ lint_roller (~> 1.1)
32
+ rubocop (>= 1.75.0, < 2.0)
33
+ rubocop-ast (>= 1.38.0, < 2.0)
34
+ rubocop-performance (1.26.1)
35
+ lint_roller (~> 1.1)
36
+ rubocop (>= 1.75.0, < 2.0)
37
+ rubocop-ast (>= 1.47.1, < 2.0)
38
+ rubocop-thread_safety (0.7.3)
39
+ lint_roller (~> 1.1)
40
+ rubocop (~> 1.72, >= 1.72.1)
41
+ rubocop-ast (>= 1.44.0, < 2.0)
42
+ ruby-progressbar (1.13.0)
43
+ standard (1.53.0)
44
+ language_server-protocol (~> 3.17.0.2)
45
+ lint_roller (~> 1.0)
46
+ rubocop (~> 1.82.0)
47
+ standard-custom (~> 1.0.0)
48
+ standard-performance (~> 1.8)
49
+ standard-custom (1.0.2)
50
+ lint_roller (~> 1.0)
51
+ rubocop (~> 1.50)
52
+ standard-minitest (1.0.0)
53
+ lint_roller (~> 1.0)
54
+ rubocop-minitest
55
+ standard-performance (1.9.0)
56
+ lint_roller (~> 1.1)
57
+ rubocop-performance (~> 1.26.0)
58
+ unicode-display_width (3.2.0)
59
+ unicode-emoji (~> 4.1)
60
+ unicode-emoji (4.2.0)
61
+ yard (0.9.38)
62
+ yard-lint (1.4.0)
63
+ yard (~> 0.9)
64
+ zeitwerk (~> 2.6)
65
+ zeitwerk (2.7.4)
66
+
67
+ PLATFORMS
68
+ ruby
69
+ x86_64-linux
70
+
71
+ DEPENDENCIES
72
+ rubocop-minitest
73
+ rubocop-performance
74
+ rubocop-thread_safety
75
+ standard
76
+ standard-minitest
77
+ standard-performance
78
+ yard-lint
79
+
80
+ CHECKSUMS
81
+ ast (2.4.3) sha256=954615157c1d6a382bc27d690d973195e79db7f55e9765ac7c481c60bdb4d383
82
+ json (2.18.0) sha256=b10506aee4183f5cf49e0efc48073d7b75843ce3782c68dbeb763351c08fd505
83
+ language_server-protocol (3.17.0.5) sha256=fd1e39a51a28bf3eec959379985a72e296e9f9acfce46f6a79d31ca8760803cc
84
+ lint_roller (1.1.0) sha256=2c0c845b632a7d172cb849cc90c1bce937a28c5c8ccccb50dfd46a485003cc87
85
+ parallel (1.27.0) sha256=4ac151e1806b755fb4e2dc2332cbf0e54f2e24ba821ff2d3dcf86bf6dc4ae130
86
+ parser (3.3.10.1) sha256=06f6a725d2cd91e5e7f2b7c32ba143631e1f7c8ae2fb918fc4cebec187e6a688
87
+ prism (1.8.0) sha256=84453a16ef5530ea62c5f03ec16b52a459575ad4e7b9c2b360fd8ce2c39c1254
88
+ racc (1.8.1) sha256=4a7f6929691dbec8b5209a0b373bc2614882b55fc5d2e447a21aaa691303d62f
89
+ rainbow (3.1.1) sha256=039491aa3a89f42efa1d6dec2fc4e62ede96eb6acd95e52f1ad581182b79bc6a
90
+ regexp_parser (2.11.3) sha256=ca13f381a173b7a93450e53459075c9b76a10433caadcb2f1180f2c741fc55a4
91
+ rubocop (1.82.1) sha256=09f1a6a654a960eda767aebea33e47603080f8e9c9a3f019bf9b94c9cab5e273
92
+ rubocop-ast (1.49.0) sha256=49c3676d3123a0923d333e20c6c2dbaaae2d2287b475273fddee0c61da9f71fd
93
+ rubocop-minitest (0.39.1) sha256=998398d6da4026d297f0f9bf709a1eac5f2b6947c24431f94af08138510cf7ed
94
+ rubocop-performance (1.26.1) sha256=cd19b936ff196df85829d264b522fd4f98b6c89ad271fa52744a8c11b8f71834
95
+ rubocop-thread_safety (0.7.3) sha256=067cdd52fbf5deffc18995437e45b5194236eaff4f71de3375a1f6052e48f431
96
+ ruby-progressbar (1.13.0) sha256=80fc9c47a9b640d6834e0dc7b3c94c9df37f08cb072b7761e4a71e22cff29b33
97
+ standard (1.53.0) sha256=f3c9493385db7079d0abce6f7582f553122156997b81258cd361d3480eeacf9c
98
+ standard-custom (1.0.2) sha256=424adc84179a074f1a2a309bb9cf7cd6bfdb2b6541f20c6bf9436c0ba22a652b
99
+ standard-minitest (1.0.0) sha256=450caa86a64a6e6f6f186cc88601dbb49b6cfaa3b0dce77a73b50ba8cdc15b2a
100
+ standard-performance (1.9.0) sha256=49483d31be448292951d80e5e67cdcb576c2502103c7b40aec6f1b6e9c88e3f2
101
+ unicode-display_width (3.2.0) sha256=0cdd96b5681a5949cdbc2c55e7b420facae74c4aaf9a9815eee1087cb1853c42
102
+ unicode-emoji (4.2.0) sha256=519e69150f75652e40bf736106cfbc8f0f73aa3fb6a65afe62fefa7f80b0f80f
103
+ yard (0.9.38) sha256=721fb82afb10532aa49860655f6cc2eaa7130889df291b052e1e6b268283010f
104
+ yard-lint (1.4.0) sha256=7dd88fbb08fd77cb840bea899d58812817b36d92291b5693dd0eeb3af9f91f0f
105
+ zeitwerk (2.7.4) sha256=2bef90f356bdafe9a6c2bd32bcd804f83a4f9b8bc27f3600fff051eb3edcec8b
106
+
107
+ BUNDLED WITH
108
+ 4.0.3
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- karafka-testing (2.5.5)
4
+ karafka-testing (2.5.6)
5
5
  karafka (>= 2.5.0, < 2.6.0)
6
6
  waterdrop (>= 2.8.0)
7
7
 
@@ -11,34 +11,41 @@ GEM
11
11
  base64 (0.3.0)
12
12
  diff-lcs (1.6.2)
13
13
  docile (1.4.1)
14
- ffi (1.17.2)
15
- ffi (1.17.2-x86_64-linux-gnu)
16
- json (2.15.1)
14
+ drb (2.2.3)
15
+ ffi (1.17.3)
16
+ ffi (1.17.3-x86_64-linux-gnu)
17
+ json (2.19.2)
17
18
  karafka (2.5.1)
18
19
  base64 (~> 0.2)
19
20
  karafka-core (>= 2.5.6, < 2.6.0)
20
21
  karafka-rdkafka (>= 0.22.0)
21
22
  waterdrop (>= 2.8.9, < 3.0.0)
22
23
  zeitwerk (~> 2.3)
23
- karafka-core (2.5.7)
24
+ karafka-core (2.5.10)
24
25
  karafka-rdkafka (>= 0.20.0)
25
26
  logger (>= 1.6.0)
26
- karafka-rdkafka (0.22.2)
27
- ffi (~> 1.15)
27
+ karafka-rdkafka (0.24.0)
28
+ ffi (~> 1.17.1)
28
29
  json (> 2.0)
29
30
  logger
30
31
  mini_portile2 (~> 2.6)
31
32
  rake (> 12)
32
- karafka-rdkafka (0.22.2-x86_64-linux-gnu)
33
- ffi (~> 1.15)
33
+ karafka-rdkafka (0.24.0-x86_64-linux-gnu)
34
+ ffi (~> 1.17.1)
34
35
  json (> 2.0)
35
36
  logger
36
37
  mini_portile2 (~> 2.6)
37
38
  rake (> 12)
38
39
  logger (1.7.0)
39
40
  mini_portile2 (2.8.9)
41
+ minitest (6.0.2)
42
+ drb (~> 2.0)
43
+ prism (~> 1.5)
44
+ mocha (3.1.0)
45
+ ruby2_keywords (>= 0.0.5)
40
46
  ostruct (0.6.3)
41
- rake (13.3.0)
47
+ prism (1.9.0)
48
+ rake (13.3.1)
42
49
  rspec (3.13.2)
43
50
  rspec-core (~> 3.13.0)
44
51
  rspec-expectations (~> 3.13.0)
@@ -52,6 +59,7 @@ GEM
52
59
  diff-lcs (>= 1.2.0, < 2.0)
53
60
  rspec-support (~> 3.13.0)
54
61
  rspec-support (3.13.6)
62
+ ruby2_keywords (0.0.5)
55
63
  simplecov (0.22.0)
56
64
  docile (~> 1.1)
57
65
  simplecov-html (~> 0.11)
@@ -63,10 +71,6 @@ GEM
63
71
  karafka-core (>= 2.4.9, < 3.0.0)
64
72
  karafka-rdkafka (>= 0.20.0)
65
73
  zeitwerk (~> 2.3)
66
- yard (0.9.37)
67
- yard-lint (1.2.3)
68
- yard (~> 0.9)
69
- zeitwerk (~> 2.6)
70
74
  zeitwerk (2.7.3)
71
75
 
72
76
  PLATFORMS
@@ -75,11 +79,12 @@ PLATFORMS
75
79
 
76
80
  DEPENDENCIES
77
81
  karafka-testing!
82
+ minitest
83
+ mocha
78
84
  ostruct
79
85
  rspec
80
86
  simplecov
81
87
  warning
82
- yard-lint
83
88
 
84
89
  BUNDLED WITH
85
90
  2.6.3
data/Rakefile CHANGED
@@ -1,4 +1,14 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'bundler/setup'
4
- require 'bundler/gem_tasks'
3
+ require "bundler/setup"
4
+ require "bundler/gem_tasks"
5
+
6
+ require "rake/testtask"
7
+
8
+ Rake::TestTask.new(:test) do |t|
9
+ t.libs << "test"
10
+ t.test_files = FileList["test/test_helper.rb", "test/**/*_test.rb"]
11
+ .exclude("test/integration/**/*")
12
+ end
13
+
14
+ task default: :test
@@ -1,35 +1,35 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- lib = File.expand_path('lib', __dir__)
3
+ lib = File.expand_path("lib", __dir__)
4
4
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
5
5
 
6
- require 'karafka/testing/version'
6
+ require "karafka/testing/version"
7
7
 
8
8
  Gem::Specification.new do |spec|
9
- spec.name = 'karafka-testing'
10
- spec.platform = Gem::Platform::RUBY
11
- spec.version = Karafka::Testing::VERSION
12
- spec.authors = ['Maciej Mensfeld']
13
- spec.email = %w[contact@karafka.io]
14
- spec.summary = 'Library which provides helpers for easier Karafka consumers tests'
15
- spec.description = 'Library which provides helpers for easier Karafka consumers tests'
16
- spec.homepage = 'https://karafka.io'
17
- spec.license = 'MIT'
18
- spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(spec)/}) }
19
- spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
9
+ spec.name = "karafka-testing"
10
+ spec.platform = Gem::Platform::RUBY
11
+ spec.version = Karafka::Testing::VERSION
12
+ spec.authors = ["Maciej Mensfeld"]
13
+ spec.email = %w[contact@karafka.io]
14
+ spec.summary = "Library which provides helpers for easier Karafka consumers tests"
15
+ spec.description = "Library which provides helpers for easier Karafka consumers tests"
16
+ spec.homepage = "https://karafka.io"
17
+ spec.license = "MIT"
18
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test)/}) }
19
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
20
20
  spec.require_paths = %w[lib]
21
21
 
22
- spec.add_dependency 'karafka', '>= 2.5.0', '< 2.6.0'
23
- spec.add_dependency 'waterdrop', '>= 2.8.0'
22
+ spec.add_dependency "karafka", ">= 2.5.0", "< 2.6.0"
23
+ spec.add_dependency "waterdrop", ">= 2.8.0"
24
24
 
25
- spec.required_ruby_version = '>= 3.2.0'
25
+ spec.required_ruby_version = ">= 3.2.0"
26
26
 
27
27
  spec.metadata = {
28
- 'homepage_uri' => 'https://karafka.io',
29
- 'changelog_uri' => 'https://karafka.io/docs/Changelog-Karafka-Testing',
30
- 'bug_tracker_uri' => 'https://github.com/karafka/karafka-testing/issues',
31
- 'source_code_uri' => 'https://github.com/karafka/karafka-testing',
32
- 'documentation_uri' => 'https://karafka.io/docs',
33
- 'rubygems_mfa_required' => 'true'
28
+ "homepage_uri" => "https://karafka.io",
29
+ "changelog_uri" => "https://karafka.io/docs/Changelog-Karafka-Testing",
30
+ "bug_tracker_uri" => "https://github.com/karafka/karafka-testing/issues",
31
+ "source_code_uri" => "https://github.com/karafka/karafka-testing",
32
+ "documentation_uri" => "https://karafka.io/docs",
33
+ "rubygems_mfa_required" => "true"
34
34
  }
35
35
  end
@@ -1,11 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'karafka/testing'
4
- require 'karafka/testing/errors'
5
- require 'karafka/testing/helpers'
6
- require 'karafka/testing/spec_consumer_client'
7
- require 'karafka/testing/spec_producer_client'
8
- require 'karafka/testing/minitest/proxy'
3
+ require "karafka/testing"
4
+ require "karafka/testing/errors"
5
+ require "karafka/testing/helpers"
6
+ require "karafka/testing/spec_consumer_client"
7
+ require "karafka/testing/spec_producer_client"
8
+ require "karafka/testing/minitest/proxy"
9
9
 
10
10
  module Karafka
11
11
  module Testing
@@ -42,14 +42,23 @@ module Karafka
42
42
  Karafka.producer.stubs(:client).returns(@_karafka_producer_client)
43
43
  end
44
44
 
45
- if base.to_s == 'Minitest::Spec'
45
+ if base.to_s == "Minitest::Spec"
46
46
  base.class_eval do
47
47
  before(&eval_flow)
48
48
  end
49
- else
49
+ elsif base.respond_to?(:setup) && base.method(:setup).arity != 0
50
50
  base.class_eval do
51
51
  setup(&eval_flow)
52
52
  end
53
+ else
54
+ setup_mod = Module.new do
55
+ define_method(:setup) do
56
+ instance_exec(&eval_flow)
57
+ super()
58
+ end
59
+ end
60
+
61
+ base.prepend(setup_mod)
53
62
  end
54
63
  end
55
64
  end
@@ -89,11 +98,13 @@ module Karafka
89
98
  # @example Send a json message to consumer and simulate, that it is partition 6
90
99
  # @karafka.produce({ 'hello' => 'world' }.to_json, 'partition' => 6)
91
100
  def _karafka_add_message_to_consumer_if_needed(message)
92
- consumer_obj = if defined?(@consumer)
93
- @consumer
94
- else
95
- _karafka_find_consumer_for_message(message)
96
- end
101
+ consumer_obj = if message[:consumer_group]
102
+ _karafka_find_consumer_for_message(message)
103
+ elsif defined?(@consumer)
104
+ @consumer
105
+ else
106
+ _karafka_find_consumer_for_message(message)
107
+ end
97
108
  # Consumer needs to be defined in order to pass messages to it
98
109
  return unless defined?(consumer_obj)
99
110
  # We're interested in adding message to consumer only when it is a Karafka consumer
@@ -105,7 +116,7 @@ module Karafka
105
116
  return unless message[:topic] == consumer_obj.topic.name
106
117
  # If consumer_group is explicitly specified, verify it matches
107
118
  return if message[:consumer_group] &&
108
- message[:consumer_group].to_s != consumer_obj.topic.consumer_group.name
119
+ message[:consumer_group].to_s != consumer_obj.topic.consumer_group.name
109
120
 
110
121
  # Build message metadata and copy any metadata that would come from the message
111
122
  metadata = _karafka_message_metadata_defaults(consumer_obj)
@@ -140,22 +151,30 @@ module Karafka
140
151
 
141
152
  # Produces message with a given payload to the consumer matching topic
142
153
  # @param payload [String] payload we want to dispatch
143
- # @param metadata [Hash] any metadata we want to dispatch alongside the payload
154
+ # @param metadata [Hash] any metadata we want to dispatch alongside the payload.
155
+ # Supports an `offset` key to set a custom offset for the message (otherwise
156
+ # offsets auto-increment from 0).
144
157
  def _karafka_produce(payload, metadata = {})
158
+ # Extract offset before passing to WaterDrop since it is not a valid
159
+ # WaterDrop message attribute (Kafka assigns offsets, not producers)
160
+ @_karafka_next_offset = metadata.delete(:offset)
161
+
145
162
  topic = if metadata[:topic]
146
- metadata[:topic]
147
- elsif defined?(@consumer)
148
- @consumer.topic.name
149
- else
150
- last_consumer = @_karafka_consumer_mappings&.values&.last
151
- last_consumer&.topic&.name
152
- end
163
+ metadata[:topic]
164
+ elsif defined?(@consumer)
165
+ @consumer.topic.name
166
+ else
167
+ last_consumer = @_karafka_consumer_mappings&.values&.last
168
+ last_consumer&.topic&.name
169
+ end
153
170
  Karafka.producer.produce_sync(
154
171
  {
155
172
  topic: topic,
156
173
  payload: payload
157
174
  }.merge(metadata)
158
175
  )
176
+ ensure
177
+ @_karafka_next_offset = nil
159
178
  end
160
179
 
161
180
  # @return [Array<Hash>] messages that were produced
@@ -211,7 +230,7 @@ module Karafka
211
230
  # No consumer group specified - find all consumers for this topic
212
231
  matching = @_karafka_consumer_mappings.values.select { |c| c.topic.name == topic_name }
213
232
  # If exactly one consumer matches, use it (backward compatible)
214
- matching.size == 1 ? matching.first : nil
233
+ (matching.size == 1) ? matching.first : nil
215
234
  end
216
235
  end
217
236
 
@@ -223,7 +242,7 @@ module Karafka
223
242
  timestamp: Time.now,
224
243
  raw_headers: {},
225
244
  raw_key: nil,
226
- offset: @_karafka_consumer_messages.size,
245
+ offset: @_karafka_next_offset.nil? ? @_karafka_consumer_messages.size : @_karafka_next_offset,
227
246
  partition: 0,
228
247
  received_at: Time.now,
229
248
  topic: consumer_obj.topic.name
@@ -1,12 +1,12 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'waterdrop'
4
- require 'karafka/testing'
5
- require 'karafka/testing/errors'
6
- require 'karafka/testing/helpers'
7
- require 'karafka/testing/spec_consumer_client'
8
- require 'karafka/testing/spec_producer_client'
9
- require 'karafka/testing/rspec/proxy'
3
+ require "waterdrop"
4
+ require "karafka/testing"
5
+ require "karafka/testing/errors"
6
+ require "karafka/testing/helpers"
7
+ require "karafka/testing/spec_consumer_client"
8
+ require "karafka/testing/spec_producer_client"
9
+ require "karafka/testing/rspec/proxy"
10
10
 
11
11
  module Karafka
12
12
  module Testing
@@ -55,7 +55,7 @@ module Karafka
55
55
  # that patches are available because some users have Mocha as part of their
56
56
  # supply chain, but do not use it when running Karafka specs. In such cases, without
57
57
  # such check `karafka-testing` would falsely assume, that Mocha is in use.
58
- if Object.const_defined?('Mocha', false) && Karafka.producer.respond_to?(:stubs)
58
+ if Object.const_defined?("Mocha", false) && Karafka.producer.respond_to?(:stubs)
59
59
  Karafka.producer.stubs(:client).returns(_karafka_producer_client)
60
60
  else
61
61
  allow(Karafka.producer).to receive(:client).and_return(_karafka_producer_client)
@@ -105,10 +105,12 @@ module Karafka
105
105
  # end
106
106
  def _karafka_add_message_to_consumer_if_needed(message)
107
107
  consumer_obj = if defined?(consumer)
108
- consumer
109
- else
110
- _karafka_find_consumer_for_message(message)
111
- end
108
+ consumer
109
+ elsif _karafka_described_class_is_consumer?
110
+ _karafka_resolve_subject_consumer || _karafka_find_consumer_for_message(message)
111
+ else
112
+ _karafka_find_consumer_for_message(message)
113
+ end
112
114
  # Consumer needs to be defined in order to pass messages to it
113
115
  return unless consumer_obj
114
116
  # We're interested in adding message to consumer only when it is a Karafka consumer
@@ -120,7 +122,7 @@ module Karafka
120
122
  return unless message[:topic] == consumer_obj.topic.name
121
123
  # If consumer_group is explicitly specified, verify it matches
122
124
  return if message[:consumer_group] &&
123
- message[:consumer_group].to_s != consumer_obj.topic.consumer_group.name
125
+ message[:consumer_group].to_s != consumer_obj.topic.consumer_group.name
124
126
 
125
127
  # Build message metadata and copy any metadata that would come from the message
126
128
  metadata = _karafka_message_metadata_defaults(consumer_obj)
@@ -156,22 +158,32 @@ module Karafka
156
158
 
157
159
  # Produces message with a given payload to the consumer matching topic
158
160
  # @param payload [String] payload we want to dispatch
159
- # @param metadata [Hash] any metadata we want to dispatch alongside the payload
161
+ # @param metadata [Hash] any metadata we want to dispatch alongside the payload.
162
+ # Supports an `offset` key to set a custom offset for the message (otherwise
163
+ # offsets auto-increment from 0).
160
164
  def _karafka_produce(payload, metadata = {})
165
+ # Extract offset before passing to WaterDrop since it is not a valid
166
+ # WaterDrop message attribute (Kafka assigns offsets, not producers)
167
+ @_karafka_next_offset = metadata.delete(:offset)
168
+
161
169
  topic = if metadata[:topic]
162
- metadata[:topic]
163
- elsif defined?(consumer)
164
- consumer.topic.name
165
- else
166
- last_consumer = @_karafka_consumer_mappings&.values&.last
167
- last_consumer&.topic&.name
168
- end
170
+ metadata[:topic]
171
+ elsif defined?(consumer)
172
+ consumer.topic.name
173
+ elsif _karafka_described_class_is_consumer? && (sub = _karafka_resolve_subject_consumer)
174
+ sub.topic.name
175
+ else
176
+ last_consumer = @_karafka_consumer_mappings&.values&.last
177
+ last_consumer&.topic&.name
178
+ end
169
179
  Karafka.producer.produce_sync(
170
180
  {
171
181
  topic: topic,
172
182
  payload: payload
173
183
  }.merge(metadata)
174
184
  )
185
+ ensure
186
+ @_karafka_next_offset = nil
175
187
  end
176
188
 
177
189
  # @return [Array<Hash>] messages that were produced
@@ -206,6 +218,32 @@ module Karafka
206
218
 
207
219
  private
208
220
 
221
+ # Checks if the RSpec described_class is a Karafka consumer class.
222
+ # When true, the implicit RSpec subject can be used as a consumer fallback.
223
+ #
224
+ # @return [Boolean] true if described_class inherits from Karafka::BaseConsumer
225
+ def _karafka_described_class_is_consumer?
226
+ respond_to?(:described_class) &&
227
+ described_class.is_a?(Class) &&
228
+ described_class < Karafka::BaseConsumer
229
+ end
230
+
231
+ # Resolves the RSpec implicit subject as a usable Karafka consumer instance.
232
+ # Returns nil if subject is not a properly configured consumer (e.g. a default
233
+ # described_class.new without topic setup via karafka.consumer_for).
234
+ #
235
+ # @return [Karafka::BaseConsumer, nil] the subject if it is a configured consumer
236
+ def _karafka_resolve_subject_consumer
237
+ candidate = subject
238
+
239
+ return nil unless candidate.is_a?(Karafka::BaseConsumer)
240
+ # Unconfigured consumers (default subject = described_class.new) have nil
241
+ # coordinator, so topic delegation would fail. Check coordinator presence first.
242
+ return nil unless candidate.coordinator
243
+
244
+ candidate.topic&.name ? candidate : nil
245
+ end
246
+
209
247
  # Finds a consumer for the given message with backward-compatible fallback
210
248
  # @param message [Hash] the message being routed
211
249
  # @return [Object, nil] the consumer instance or nil
@@ -224,7 +262,7 @@ module Karafka
224
262
  # No consumer group specified - find all consumers for this topic
225
263
  matching = @_karafka_consumer_mappings.values.select { |c| c.topic.name == topic_name }
226
264
  # If exactly one consumer matches, use it (backward compatible)
227
- matching.size == 1 ? matching.first : nil
265
+ (matching.size == 1) ? matching.first : nil
228
266
  end
229
267
  end
230
268
 
@@ -236,7 +274,7 @@ module Karafka
236
274
  timestamp: Time.now,
237
275
  raw_headers: {},
238
276
  raw_key: nil,
239
- offset: _karafka_consumer_messages.size,
277
+ offset: @_karafka_next_offset.nil? ? _karafka_consumer_messages.size : @_karafka_next_offset,
240
278
  partition: 0,
241
279
  received_at: Time.now,
242
280
  topic: consumer_obj.topic.name
@@ -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.5'
7
+ VERSION = "2.5.6"
8
8
  end
9
9
  end
@@ -10,11 +10,11 @@ module Karafka
10
10
  # You should never use karafka-testing primitives when framework is not loaded because
11
11
  # testing lib stubs certain pieces of Karafka that need to be initialized.
12
12
  def ensure_karafka_loaded!
13
- return if ::Karafka.const_defined?('App', false)
13
+ return if ::Karafka.const_defined?("App", false)
14
14
 
15
15
  raise(
16
16
  Karafka::Testing::Errors::KarafkaNotLoadedError,
17
- 'Make sure to load Karafka framework prior to usage of the testing components.'
17
+ "Make sure to load Karafka framework prior to usage of the testing components."
18
18
  )
19
19
  end
20
20
 
@@ -27,7 +27,7 @@ module Karafka
27
27
 
28
28
  raise(
29
29
  Karafka::Testing::Errors::KarafkaNotInitializedError,
30
- 'Make sure to initialize Karafka framework prior to usage of the testing components.'
30
+ "Make sure to initialize Karafka framework prior to usage of the testing components."
31
31
  )
32
32
  end
33
33
  end
@@ -1,3 +1,3 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'karafka/testing'
3
+ require "karafka/testing"