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 +4 -4
- checksums.yaml.gz.sig +0 -0
- data/.github/workflows/ci.yml +3 -1
- data/CHANGELOG.md +3 -0
- data/Gemfile.lock +1 -1
- data/lib/karafka/testing/minitest/helpers.rb +205 -0
- data/lib/karafka/testing/minitest/proxy.rb +31 -0
- data/lib/karafka/testing/version.rb +1 -1
- data.tar.gz.sig +0 -0
- metadata +4 -2
- metadata.gz.sig +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a79a60538d27c37844e17b112d7640e9ee9d975c8537bb2b78d8b8399faf656b
|
4
|
+
data.tar.gz: bcd86d7094908a5ac1e261651722f387c4188772890da390cadd19ed874aef04
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: abc39e5c005227a77ba7145e263c93e10c8c3e0406cdccf66cc3406c72d479436f8b3f0a1f665d5f793c3b416760d87beb02f25f319594e1bbc9d9093537a7da
|
7
|
+
data.tar.gz: 91c4255aa2897199498879a7e400b6609204d322c39307ba131449752d9b6b562fca70d37513170e1c6563ee351018a7c8bb11ec9e833e426020cb2bec011e67
|
checksums.yaml.gz.sig
CHANGED
Binary file
|
data/.github/workflows/ci.yml
CHANGED
data/CHANGELOG.md
CHANGED
data/Gemfile.lock
CHANGED
@@ -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
|
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.
|
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-
|
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
|