Module: Deimos::TestHelpers

Extended by:
ActiveSupport::Concern
Defined in:
lib/deimos/test_helpers.rb

Overview

Include this module in your RSpec spec_helper to stub out external dependencies and add methods to use to test encoding/decoding.

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.schemas_compatible?(schema1, schema2) ⇒ Boolean

hash representing a schema. hash representing a schema.

Parameters:

  • schema1 (String|Hash) —

    a file path, JSON string, or

  • schema2 (String|Hash) —

    a file path, JSON string, or

Returns:

  • (Boolean) —

    true if the schemas are compatible, false otherwise.



322
323
324
325
326
327
328
329
330
331
332
333
334
# File 'lib/deimos/test_helpers.rb', line 322

def self.schemas_compatible?(schema1, schema2)
  json1, json2 = [schema1, schema2].map do |schema|
    if schema.is_a?(String)
      schema = File.read(schema) unless schema.strip.starts_with?('{') # file path
      MultiJson.load(schema)
    else
      schema
    end
  end
  avro_schema1 = Avro::Schema.real_parse(json1, {})
  avro_schema2 = Avro::Schema.real_parse(json2, {})
  Avro::SchemaCompatibility.mutual_read?(avro_schema1, avro_schema2)
end

.sent_messages ⇒ Array<Hash>

Returns:

  • (Array<Hash>)


18
19
20
# File 'lib/deimos/test_helpers.rb', line 18

def sent_messages
  @sent_messages ||= []
end

Instance Method Details

#_frk_failure_message(topic, message, key = nil, partition_key = nil, was_negated = false) ⇒ Object

:nodoc:



157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
# File 'lib/deimos/test_helpers.rb', line 157

def _frk_failure_message(topic, message, key=nil, partition_key=nil, was_negated=false)
  messages = Deimos::TestHelpers.sent_messages.
    select { |m| m[:topic] == topic }.
    map { |m| m.except(:topic) }
  message_string = ''
  diff = nil
  min_hash_diff = nil
  if messages.any?
    message_string = messages.map(&:inspect).join("\n")
    min_hash_diff = messages.min_by { |m| _hash_diff(m, message).keys.size }
    diff = RSpec::Expectations.differ.
      diff_as_object(message, min_hash_diff[:payload])
  end
  description = if message.respond_to?(:description)
                  message.description
                elsif message.nil?
                  'nil'
                else
                  message
                end
  str = "Expected #{topic} #{'not ' if was_negated}to have sent #{description}"
  str += " with key #{key}" if key
  str += " with partition key #{partition_key}" if partition_key
  str += "\nClosest message received: #{min_hash_diff}" if min_hash_diff
  str += "\nDiff: #{diff}" if diff
  str + "\nAll Messages received:\n#{message_string}"
end

#_hash_diff(hash1, hash2) ⇒ Object

get the difference of 2 hashes.

Parameters:

  • hash1 (Hash)
  • hash2 (Hash)


144
145
146
147
148
149
150
151
152
153
154
# File 'lib/deimos/test_helpers.rb', line 144

def _hash_diff(hash1, hash2)
  if hash1.nil? || !hash1.is_a?(Hash)
    hash2
  elsif hash2.nil? || !hash2.is_a?(Hash)
    hash1
  else
    hash1.dup.
      delete_if { |k, v| hash2[k] == v }.
      merge!(hash2.dup.delete_if { |k, _v| hash1.key?(k) })
  end
end

#clear_kafka_messages! ⇒ Object

Clear all sent messages - e.g. if we want to check that particular messages were sent or not sent after a point in time.



222
223
224
# File 'lib/deimos/test_helpers.rb', line 222

def clear_kafka_messages!
  Deimos::TestHelpers.sent_messages.clear
end

#stub_consumer(klass) ⇒ Object

Stub a given consumer class.

Parameters:



128
129
130
131
132
133
134
135
136
137
138
139
# File 'lib/deimos/test_helpers.rb', line 128

def stub_consumer(klass)
  allow(klass).to receive(:decoder) do
    create_decoder(klass.config[:schema], klass.config[:namespace])
  end
  klass.class_eval do
    alias_method(:old_consume, :consume) unless self.instance_methods.include?(:old_consume)
  end
  allow_any_instance_of(klass).to receive(:consume) do |instance, payload, |
    [:key] = klass.new.decode_key([:key])
    instance.old_consume(payload, )
  end
end

#stub_producer(klass) ⇒ Object

Stub a given producer class.

Parameters:



117
118
119
120
121
122
123
124
# File 'lib/deimos/test_helpers.rb', line 117

def stub_producer(klass)
  allow(klass).to receive(:encoder) do
    create_encoder(klass.config[:schema], klass.config[:namespace])
  end
  allow(klass).to receive(:key_encoder) do
    create_encoder(klass.config[:key_schema], klass.config[:namespace])
  end
end

#stub_producers_and_consumers! ⇒ Object

Stub all already-loaded producers and consumers fir unit testing purposes.



95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
# File 'lib/deimos/test_helpers.rb', line 95

def stub_producers_and_consumers!
  Deimos::TestHelpers.sent_messages.clear

  allow(Deimos::Producer).to receive(:produce_batch) do |_, batch|
    Deimos::TestHelpers.sent_messages.concat(batch.map(&:to_h))
  end

  Deimos::Producer.descendants.each do |klass|
    next if klass == Deimos::ActiveRecordProducer # "abstract" class

    stub_producer(klass)
  end

  Deimos::Consumer.descendants.each do |klass|
    next if klass == Deimos::ActiveRecordConsumer # "abstract" class

    stub_consumer(klass)
  end
end

#test_consume_invalid_message(handler_class, payload) ⇒ Object

Check to see that a given message will fail due to Avro errors.

Parameters:

  • handler_class (Class)
  • payload (Hash)


295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
# File 'lib/deimos/test_helpers.rb', line 295

def test_consume_invalid_message(handler_class, payload)
  handler = handler_class.new
  allow(handler_class).to receive(:new).and_return(handler)
  listener = double('listener',
                    handler_class: handler_class,
                    encoding: nil)
  message = double('message',
                   key: _key_from_consumer(handler_class),
                   partition_key: nil,
                   partition: 1,
                   offset: 1,
                   value: payload)

  expect {
    Phobos::Actions::ProcessMessage.new(
      listener: listener,
      message: message,
      listener_metadata: { topic: 'my-topic' }
    ).send(:process_message, payload)
  }.to raise_error(Avro::SchemaValidator::ValidationError)
end

#test_consume_message(handler_class_or_topic, payload, call_original: false, key: nil, partition_key: nil, skip_expectation: false, &block) ⇒ Object

Test that a given handler will consume a given payload correctly, i.e. that the Avro schema is correct. If a block is given, that block will be executed when consume is called. Otherwise it will just confirm that consume is called at all. Deimos::Consumer or the topic as a string to continue as normal. Not compatible with a block. expectations on the consumer. Primarily used internally to Deimos.

Parameters:

  • handler_class_or_topic (Class|String) —

    Class which inherits from

  • payload (Hash) —

    the payload to consume

  • call_original (Boolean) (defaults to: false) —

    if true, allow the consume handler

  • ignore_expectation (Boolean) —

    Set to true to not place any

  • key (Object) (defaults to: nil) —

    the key to use.

  • partition_key (Object) (defaults to: nil) —

    the partition key to use.



252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
# File 'lib/deimos/test_helpers.rb', line 252

def test_consume_message(handler_class_or_topic,
                         payload,
                         call_original: false,
                         key: nil,
                         partition_key: nil,
                         skip_expectation: false,
                         &block)
  raise 'Cannot have both call_original and be given a block!' if call_original && block_given?

  payload&.stringify_keys!
  handler_class = if handler_class_or_topic.is_a?(String)
                    _get_handler_class_from_topic(handler_class_or_topic)
                  else
                    handler_class_or_topic
                  end
  handler = handler_class.new
  allow(handler_class).to receive(:new).and_return(handler)
  listener = double('listener',
                    handler_class: handler_class,
                    encoding: nil)
  key ||= _key_from_consumer(handler_class)
  message = double('message',
                   'key' => key,
                   'partition_key' => partition_key,
                   'partition' => 1,
                   'offset' => 1,
                   'value' => payload)

  unless skip_expectation
    expectation = expect(handler).to receive(:consume).
      with(payload, anything, &block)
    expectation.and_call_original if call_original
  end
  Phobos::Actions::ProcessMessage.new(
    listener: listener,
    message: message,
    listener_metadata: { topic: 'my-topic' }
  ).send(:process_message, payload)
end

#was_message_sent?(message, topic, key = nil) ⇒ Boolean

test that a message was sent on the given topic. DEPRECATED - use the "have_sent" matcher instead.

Parameters:

  • message (Hash)
  • topic (String)
  • key (String|Integer) (defaults to: nil)

Returns:

  • (Boolean)


232
233
234
235
236
237
# File 'lib/deimos/test_helpers.rb', line 232

def was_message_sent?(message, topic, key=nil)
  Deimos::TestHelpers.sent_messages.any? do |m|
    message == m[:payload] && m[:topic] == topic &&
      (key.present? ? m[:key] == key : true)
  end
end