karafka-testing 2.2.1 → 2.2.2

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: 073f7b386365e017596e6ad5d8c1f5753d72dab2eaf90c46504a4676f2690066
4
- data.tar.gz: c64a949126353354af0218ed81fc2ee0229fea1edbc3dacf08d129f495053929
3
+ metadata.gz: a79a60538d27c37844e17b112d7640e9ee9d975c8537bb2b78d8b8399faf656b
4
+ data.tar.gz: bcd86d7094908a5ac1e261651722f387c4188772890da390cadd19ed874aef04
5
5
  SHA512:
6
- metadata.gz: eedf2b5d5ac5c5507f0e5ddf7c3a1f24efb729966c7de9e5f1ecc785f27a08a8fa16d38b8cd12aa5446ed3e5f94d2a3db3e71b675ce1ca250cad7b28635937b3
7
- data.tar.gz: 709de9d3f9f6d4351c5abd33c4c0a1888d680fb8159ca07f643bfe4153860e6ed290272cd7545a697b7eeaef4b754f446c945f40cef872413831aefa444c33ec
6
+ metadata.gz: abc39e5c005227a77ba7145e263c93e10c8c3e0406cdccf66cc3406c72d479436f8b3f0a1f665d5f793c3b416760d87beb02f25f319594e1bbc9d9093537a7da
7
+ data.tar.gz: 91c4255aa2897199498879a7e400b6609204d322c39307ba131449752d9b6b562fca70d37513170e1c6563ee351018a7c8bb11ec9e833e426020cb2bec011e67
checksums.yaml.gz.sig CHANGED
Binary file
@@ -1,6 +1,8 @@
1
1
  name: ci
2
2
 
3
- concurrency: ci-${{ github.ref }}
3
+ concurrency:
4
+ group: ${{ github.workflow }}-${{ github.ref }}
5
+ cancel-in-progress: true
4
6
 
5
7
  on:
6
8
  pull_request:
data/CHANGELOG.md CHANGED
@@ -1,5 +1,8 @@
1
1
  # Karafka Test gem changelog
2
2
 
3
+ ## 2.2.2 (2023-11-16)
4
+ - [**Feature**] Provide support for Minitest (ValentinoRusconi-EH)
5
+
3
6
  ## 2.2.1 (2023-10-26)
4
7
  - [Enhancement] Support patterns in `#consumer_for` consumer builder.
5
8
 
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- karafka-testing (2.2.1)
4
+ karafka-testing (2.2.2)
5
5
  karafka (>= 2.2.0, < 3.0.0)
6
6
  waterdrop (>= 2.6.6)
7
7
 
@@ -0,0 +1,205 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'karafka/testing/errors'
4
+ require 'karafka/testing/spec_consumer_client'
5
+ require 'karafka/testing/spec_producer_client'
6
+ require 'karafka/testing/minitest/proxy'
7
+
8
+ module Karafka
9
+ module Testing
10
+ # All the things related to extra functionalities needed to easier spec out
11
+ # Karafka things using Minitest
12
+ module Minitest
13
+ # Minitest helpers module that needs to be included
14
+ module Helpers
15
+ class << self
16
+ # Adds all the needed extra functionalities to the minitest group
17
+ # @param base [Class] Minitest example group we want to extend
18
+ def included(base)
19
+ base.class_eval do
20
+ setup do
21
+ @karafka = Karafka::Testing::Minitest::Proxy.new(self)
22
+ @_karafka_consumer_messages = []
23
+ @_karafka_consumer_client = Karafka::Testing::SpecConsumerClient.new
24
+ @_karafka_producer_client = Karafka::Testing::SpecProducerClient.new(self)
25
+
26
+ @_karafka_consumer_messages.clear
27
+ @_karafka_producer_client.reset
28
+
29
+ Karafka.producer.stubs(:client).returns(@_karafka_producer_client)
30
+ end
31
+ end
32
+ end
33
+ end
34
+
35
+ # Creates a consumer instance for a given topic
36
+ #
37
+ # @param requested_topic [String, Symbol] name of the topic for which we want to
38
+ # create a consumer instance
39
+ # @param requested_consumer_group [String, Symbol, nil] optional name of the consumer group
40
+ # if we have multiple consumer groups listening on the same topic
41
+ # @return [Object] Karafka consumer instance
42
+ # @raise [Karafka::Testing::Errors::TopicNotFoundError] raised when we're unable to find
43
+ # topic that was requested
44
+ #
45
+ # @example Creates a consumer instance with settings for `my_requested_topic`
46
+ # consumer = @karafka.consumer_for(:my_requested_topic)
47
+ def _karafka_consumer_for(requested_topic, requested_consumer_group = nil)
48
+ selected_topics = _karafka_consumer_find_candidate_topics(
49
+ requested_topic.to_s,
50
+ requested_consumer_group.to_s
51
+ )
52
+
53
+ raise Errors::TopicInManyConsumerGroupsError, requested_topic if selected_topics.size > 1
54
+ raise Errors::TopicNotFoundError, requested_topic if selected_topics.empty?
55
+
56
+ _karafka_build_consumer_for(selected_topics.first)
57
+ end
58
+
59
+ # Adds a new Karafka message instance if needed with given payload and options into an
60
+ # internal consumer buffer that will be used to simulate messages delivery to the consumer
61
+ #
62
+ # @param message [Hash] message that was sent to Kafka
63
+ #
64
+ # @example Send a json message to consumer
65
+ # @karafka.produce({ 'hello' => 'world' }.to_json)
66
+ #
67
+ # @example Send a json message to consumer and simulate, that it is partition 6
68
+ # @karafka.produce({ 'hello' => 'world' }.to_json, 'partition' => 6)
69
+ def _karafka_add_message_to_consumer_if_needed(message)
70
+ # Consumer needs to be defined in order to pass messages to it
71
+ return unless defined?(@consumer)
72
+ # We're interested in adding message to consumer only when it is a Karafka consumer
73
+ # Users may want to test other things (models producing messages for example) and in
74
+ # their case consumer will not be a consumer
75
+ return unless @consumer.is_a?(Karafka::BaseConsumer)
76
+ # We target to the consumer only messages that were produced to it, since specs may also
77
+ # produce other messages targeting other topics
78
+ return unless message[:topic] == @consumer.topic.name
79
+
80
+ # Build message metadata and copy any metadata that would come from the message
81
+ metadata = _karafka_message_metadata_defaults
82
+
83
+ metadata.keys.each do |key|
84
+ next unless message.key?(key)
85
+
86
+ metadata[key] = message.fetch(key)
87
+ end
88
+ # Add this message to previously produced messages
89
+ @_karafka_consumer_messages << Karafka::Messages::Message.new(
90
+ message[:payload],
91
+ Karafka::Messages::Metadata.new(metadata).freeze
92
+ )
93
+ # Update batch metadata
94
+ batch_metadata = Karafka::Messages::Builders::BatchMetadata.call(
95
+ @_karafka_consumer_messages,
96
+ @consumer.topic,
97
+ 0,
98
+ Time.now
99
+ )
100
+
101
+ # Update consumer messages batch
102
+ @consumer.messages = Karafka::Messages::Messages.new(
103
+ @_karafka_consumer_messages,
104
+ batch_metadata
105
+ )
106
+ end
107
+
108
+ # Produces message with a given payload to the consumer matching topic
109
+ # @param payload [String] payload we want to dispatch
110
+ # @param metadata [Hash] any metadata we want to dispatch alongside the payload
111
+ def _karafka_produce(payload, metadata = {})
112
+ Karafka.producer.produce_sync(
113
+ {
114
+ topic: @consumer.topic.name,
115
+ payload: payload
116
+ }.merge(metadata)
117
+ )
118
+ end
119
+
120
+ # @return [Array<Hash>] messages that were produced
121
+ def _karafka_produced_messages
122
+ @_karafka_producer_client.messages
123
+ end
124
+
125
+ private
126
+
127
+ # @return [Hash] message default options
128
+ def _karafka_message_metadata_defaults
129
+ {
130
+ deserializer: @consumer.topic.deserializer,
131
+ timestamp: Time.now,
132
+ headers: {},
133
+ key: nil,
134
+ offset: @_karafka_consumer_messages.size,
135
+ partition: 0,
136
+ received_at: Time.now,
137
+ topic: @consumer.topic.name
138
+ }
139
+ end
140
+
141
+ # Builds the consumer instance based on the provided topic
142
+ #
143
+ # @param topic [Karafka::Routing::Topic] topic for which we want to build the consumer
144
+ # @return [Object] karafka consumer
145
+ def _karafka_build_consumer_for(topic)
146
+ coordinators = Karafka::Processing::CoordinatorsBuffer.new(
147
+ Karafka::Routing::Topics.new([topic])
148
+ )
149
+ @consumer = topic.consumer.new
150
+ @consumer.producer = Karafka::App.producer
151
+ # Inject appropriate strategy so needed options and components are available
152
+ strategy = Karafka::App.config.internal.processing.strategy_selector.find(topic)
153
+ @consumer.singleton_class.include(strategy)
154
+ @consumer.client = @karafka_consumer_client
155
+ @consumer.coordinator = coordinators.find_or_create(topic.name, 0)
156
+ @consumer.coordinator.seek_offset = 0
157
+ # Indicate usage as for tests no direct enqueuing happens
158
+ @consumer.instance_variable_set('@used', true)
159
+ @consumer
160
+ end
161
+
162
+ # Finds all the routing topics matching requested topic within all topics or within
163
+ # provided consumer group based on name
164
+ #
165
+ # @param requested_topic [String] requested topic name
166
+ # @param requested_consumer_group [String] requested consumer group or nil to look in all
167
+ # @return [Array<Karafka::Routing::Topic>] all matching topics
168
+ #
169
+ # @note Since we run the lookup on subscription groups, the search will automatically
170
+ # expand with matching patterns
171
+ def _karafka_consumer_find_candidate_topics(requested_topic, requested_consumer_group)
172
+ _karafka_consumer_find_subscription_groups(requested_consumer_group)
173
+ .map(&:topics)
174
+ .filter_map do |topics|
175
+ topics.find(requested_topic.to_s)
176
+ rescue Karafka::Errors::TopicNotFoundError
177
+ nil
178
+ end
179
+ end
180
+
181
+ # Finds subscription groups from the requested consumer group or selects all if no
182
+ # consumer group specified
183
+ # @param requested_consumer_group [String] requested consumer group or nil to look in all
184
+ # @return [Array<Karafka::Routing::SubscriptionGroup>] requested subscription groups
185
+ def _karafka_consumer_find_subscription_groups(requested_consumer_group)
186
+ if requested_consumer_group && !requested_consumer_group.empty?
187
+ ::Karafka::App
188
+ .subscription_groups
189
+ # Find matching consumer group
190
+ .find { |cg, _sgs| cg.name == requested_consumer_group.to_s }
191
+ # Raise error if not found
192
+ .tap { |cg| cg || raise(Errors::ConsumerGroupNotFound, requested_consumer_group) }
193
+ # Since lookup was on a hash, get the value, that is subscription groups
194
+ .last
195
+ else
196
+ ::Karafka::App
197
+ .subscription_groups
198
+ .values
199
+ .flatten
200
+ end
201
+ end
202
+ end
203
+ end
204
+ end
205
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Karafka
4
+ module Testing
5
+ module Minitest
6
+ # Proxy object for a nicer `karafka.` API within Minitest
7
+ # None other should be used by the end users
8
+ class Proxy
9
+ # @param minitest_example [Minitest::Test] minitest context
10
+ def initialize(minitest_example)
11
+ @minitest_example = minitest_example
12
+ end
13
+
14
+ # @param args Anything that the `#_karafka_consumer_for` accepts
15
+ def consumer_for(*args)
16
+ @minitest_example._karafka_consumer_for(*args)
17
+ end
18
+
19
+ # @param args Anything that `#_karafka_produce` accepts
20
+ def produce(*args)
21
+ @minitest_example._karafka_produce(*args)
22
+ end
23
+
24
+ # @return [Array<Hash>] messages produced via `Karafka#producer`
25
+ def produced_messages
26
+ @minitest_example._karafka_produced_messages
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -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.2.1'
7
+ VERSION = '2.2.2'
8
8
  end
9
9
  end
data.tar.gz.sig CHANGED
Binary file
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.2.1
4
+ version: 2.2.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Maciej Mensfeld
@@ -35,7 +35,7 @@ cert_chain:
35
35
  AnG1dJU+yL2BK7vaVytLTstJME5mepSZ46qqIJXMuWob/YPDmVaBF39TDSG9e34s
36
36
  msG3BiCqgOgHAnL23+CN3Rt8MsuRfEtoTKpJVcCfoEoNHOkc
37
37
  -----END CERTIFICATE-----
38
- date: 2023-10-26 00:00:00.000000000 Z
38
+ date: 2023-11-16 00:00:00.000000000 Z
39
39
  dependencies:
40
40
  - !ruby/object:Gem::Dependency
41
41
  name: karafka
@@ -97,6 +97,8 @@ files:
97
97
  - lib/karafka-testing.rb
98
98
  - lib/karafka/testing.rb
99
99
  - lib/karafka/testing/errors.rb
100
+ - lib/karafka/testing/minitest/helpers.rb
101
+ - lib/karafka/testing/minitest/proxy.rb
100
102
  - lib/karafka/testing/rspec/helpers.rb
101
103
  - lib/karafka/testing/rspec/proxy.rb
102
104
  - lib/karafka/testing/spec_consumer_client.rb
metadata.gz.sig CHANGED
Binary file