fastly_nsq 0.6.0 → 0.7.0

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.
Files changed (38) hide show
  1. checksums.yaml +4 -4
  2. data/.github/PULL_REQUEST_TEMPLATE.md +6 -3
  3. data/.travis.yml +1 -1
  4. data/README.md +23 -73
  5. data/env_configuration_for_local_gem_tests.yml +0 -1
  6. data/examples/.sample.env +0 -2
  7. data/examples/Rakefile +20 -67
  8. data/lib/fastly_nsq/{message_queue/consumer.rb → consumer.rb} +8 -8
  9. data/lib/fastly_nsq/fake_backend.rb +100 -0
  10. data/lib/fastly_nsq/listener.rb +59 -0
  11. data/lib/fastly_nsq/message.rb +18 -0
  12. data/lib/fastly_nsq/{message_queue/producer.rb → producer.rb} +8 -8
  13. data/lib/fastly_nsq/rake_task.rb +39 -43
  14. data/lib/fastly_nsq/ssl_context.rb +35 -33
  15. data/lib/fastly_nsq/strategy.rb +38 -0
  16. data/lib/fastly_nsq/version.rb +1 -1
  17. data/lib/fastly_nsq.rb +21 -1
  18. data/spec/lib/fastly_nsq/consumer_spec.rb +68 -0
  19. data/spec/lib/fastly_nsq/{fake_message_queue_spec.rb → fake_backend_spec.rb} +23 -23
  20. data/spec/lib/fastly_nsq/listener_spec.rb +116 -0
  21. data/spec/lib/fastly_nsq/message_spec.rb +24 -0
  22. data/spec/lib/fastly_nsq/producer_spec.rb +56 -0
  23. data/spec/lib/fastly_nsq/rake_task_spec.rb +87 -74
  24. data/spec/lib/fastly_nsq/ssl_context_spec.rb +6 -6
  25. data/spec/lib/fastly_nsq/{message_queue/strategy_spec.rb → strategy_spec.rb} +6 -5
  26. data/spec/lib/fastly_nsq_spec.rb +18 -0
  27. data/spec/spec_helper.rb +1 -6
  28. metadata +15 -16
  29. data/lib/fastly_nsq/fake_message_queue.rb +0 -98
  30. data/lib/fastly_nsq/message_queue/listener.rb +0 -49
  31. data/lib/fastly_nsq/message_queue/strategy.rb +0 -34
  32. data/lib/fastly_nsq/message_queue.rb +0 -21
  33. data/lib/fastly_nsq/sample_message_processor.rb +0 -50
  34. data/spec/lib/fastly_nsq/message_queue/consumer_spec.rb +0 -60
  35. data/spec/lib/fastly_nsq/message_queue/listener_spec.rb +0 -90
  36. data/spec/lib/fastly_nsq/message_queue/producer_spec.rb +0 -49
  37. data/spec/lib/fastly_nsq/message_queue_spec.rb +0 -32
  38. data/spec/lib/fastly_nsq/sample_message_processor_spec.rb +0 -38
@@ -1,9 +1,10 @@
1
1
  require 'rake'
2
2
  require 'rake/tasklib'
3
3
 
4
- module MessageQueue
4
+ module FastlyNsq
5
5
  class RakeTask < Rake::TaskLib
6
- attr_accessor :name, :channel
6
+ attr_accessor :name, :channel, :topics, :preprocessor
7
+ attr_writer :listener, :logger
7
8
 
8
9
  def initialize(*args, &task_block)
9
10
  @name = args.shift || :begin_listening
@@ -15,68 +16,63 @@ module MessageQueue
15
16
  yield(*[self, task_args].slice(0, task_block.arity))
16
17
  end
17
18
 
18
- if task_args[:channel]
19
- @channel = task_args[:channel]
20
- end
21
-
22
- guard_missing_channel
23
- run_tasks
19
+ initialize_values task_args
20
+ listen_to_configured_topics
24
21
  end
25
22
  end
26
23
  end
27
24
 
28
25
  private
29
26
 
30
- def add_rake_task_description_if_one_needed
31
- unless ::Rake.application.last_description
32
- desc 'Listen to NSQ on topic using channel'
27
+ def listen_to_configured_topics
28
+ topic_per_thread do |topic, processor|
29
+ logger.info "Listening to queue, topic:'#{topic}' and channel: '#{channel}'"
30
+ listener.listen_to topic: topic,
31
+ channel: channel,
32
+ logger: logger,
33
+ processor: processor,
34
+ preprocessor: preprocessor
35
+ logger.info "... done listening on topic:'#{topic}' and channel: '#{channel}'."
33
36
  end
34
37
  end
35
38
 
36
- def run_tasks
37
- topics.each do |topic|
38
- Thread.new do
39
- wrap_helpful_output(topic) do
40
- MessageQueue::Listener.new(topic: topic, channel: channel).go
41
- end
42
- end
43
- end
44
-
45
- non_main_threads.map(&:join)
39
+ def require_arg(arg, arg_list)
40
+ arg_list.fetch(arg) { raise ArgumentError, "required configuration '#{arg}' is missing." }
46
41
  end
47
42
 
48
- def non_main_threads
49
- (Thread.list - [Thread.main])
43
+ def add_rake_task_description_if_one_needed
44
+ unless ::Rake.application.last_description
45
+ desc 'Listen to NSQ on topic using channel'
46
+ end
50
47
  end
51
48
 
52
- def guard_missing_channel
53
- unless channel
54
- raise ArgumentError, "channel is required. Received channel: #{channel}"
49
+ def topic_per_thread
50
+ listener_threads = []
51
+ topics.each do |(topic, processor)|
52
+ thread = Thread.new do
53
+ yield topic, processor
54
+ end
55
+ thread.abort_on_exception = true
56
+ listener_threads << thread
55
57
  end
56
- end
57
58
 
58
- def wrap_helpful_output(topic)
59
- output "Listening to queue, topic:'#{topic}' and channel: '#{channel}'"
60
- yield
61
- output "... done listening on topic:'#{topic}' and channel: '#{channel}'."
59
+ listener_threads.map(&:join)
62
60
  end
63
61
 
64
- def topics
65
- MessageProcessor.topics
66
- rescue NoMethodError => exception
67
- if exception.message =~ /undefined method `topics'/
68
- raise ArgumentError, 'MessageProcessor.topics is not defined.'
69
- else
70
- raise exception
71
- end
62
+ def listener
63
+ @listener || FastlyNsq::Listener
72
64
  end
73
65
 
74
- def output(string)
75
- logger.info(string)
66
+ def logger
67
+ @logger || FastlyNsq.logger || Logger.new(STDOUT)
76
68
  end
77
69
 
78
- def logger
79
- MessageQueue.logger = Logger.new(STDOUT)
70
+ def initialize_values(task_args)
71
+ @channel ||= require_arg :channel, task_args
72
+ @topics ||= require_arg :topics, task_args
73
+ @listener ||= task_args[:listener]
74
+ @logger ||= task_args[:logger]
75
+ @preprocessor ||= task_args[:preprocessor]
80
76
  end
81
77
  end
82
78
  end
@@ -1,44 +1,46 @@
1
- class SSLContext
2
- def initialize(context = nil)
3
- @context = context || {}
4
- end
1
+ module FastlyNsq
2
+ class SSLContext
3
+ def initialize(context = nil)
4
+ @context = context || {}
5
+ end
5
6
 
6
- def to_h
7
- merge_contexts
8
- if empty_context?
9
- nil
10
- else
11
- @context
7
+ def to_h
8
+ merge_contexts
9
+ if empty_context?
10
+ nil
11
+ else
12
+ @context
13
+ end
12
14
  end
13
- end
14
15
 
15
- private
16
+ private
16
17
 
17
- def env_key
18
- ENV.fetch('NSQ_SSL_KEY', nil)
19
- end
18
+ def env_key
19
+ ENV.fetch('NSQ_SSL_KEY', nil)
20
+ end
20
21
 
21
- def env_certificate
22
- ENV.fetch('NSQ_SSL_CERTIFICATE', nil)
23
- end
22
+ def env_certificate
23
+ ENV.fetch('NSQ_SSL_CERTIFICATE', nil)
24
+ end
24
25
 
25
- def env_ca_certificate
26
- ENV.fetch('NSQ_SSL_CA_CERTIFICATE', nil)
27
- end
26
+ def env_ca_certificate
27
+ ENV.fetch('NSQ_SSL_CA_CERTIFICATE', nil)
28
+ end
28
29
 
29
- def env_default_hash
30
- {
31
- key: env_key,
32
- certificate: env_certificate,
33
- ca_certificate: env_ca_certificate,
34
- }
35
- end
30
+ def env_default_hash
31
+ {
32
+ key: env_key,
33
+ certificate: env_certificate,
34
+ ca_certificate: env_ca_certificate,
35
+ }
36
+ end
36
37
 
37
- def merge_contexts
38
- @context = env_default_hash.merge(@context)
39
- end
38
+ def merge_contexts
39
+ @context = env_default_hash.merge(@context)
40
+ end
40
41
 
41
- def empty_context?
42
- @context.all? { |_key, value| value.nil? }
42
+ def empty_context?
43
+ @context.all? { |_key, value| value.nil? }
44
+ end
43
45
  end
44
46
  end
@@ -0,0 +1,38 @@
1
+ module FastlyNsq
2
+ module Strategy
3
+ class InvalidParameterError < StandardError; end
4
+
5
+ module_function
6
+
7
+ def for_queue
8
+ real_queue || fake_queue || error
9
+ end
10
+
11
+ private_class_method
12
+
13
+ ERR_MESSAGE = "You must set ENV['FAKE_QUEUE'] to either true or false".freeze
14
+
15
+ def error
16
+ raise InvalidParameterError, ERR_MESSAGE
17
+ end
18
+
19
+ FALSY_VALUES = [false, 0, '0', 'false', 'FALSE', 'off', 'OFF', nil].freeze
20
+ TRUTHY_VALUES = [true, 1, '1', 'true', 'TRUE', 'on', 'ON'].freeze
21
+
22
+ def fake_queue
23
+ FastlyNsq::FakeBackend if should_use_fake_queue?
24
+ end
25
+
26
+ def should_use_real_queue?
27
+ FALSY_VALUES.include? ENV['FAKE_QUEUE']
28
+ end
29
+
30
+ def real_queue
31
+ Nsq if should_use_real_queue?
32
+ end
33
+
34
+ def should_use_fake_queue?
35
+ TRUTHY_VALUES.include? ENV['FAKE_QUEUE']
36
+ end
37
+ end
38
+ end
@@ -1,3 +1,3 @@
1
1
  module FastlyNsq
2
- VERSION = '0.6.0'.freeze
2
+ VERSION = '0.7.0'.freeze
3
3
  end
data/lib/fastly_nsq.rb CHANGED
@@ -1,3 +1,23 @@
1
+ require 'nsq'
2
+ require 'fastly_nsq/consumer'
3
+ require 'fastly_nsq/fake_backend'
4
+ require 'fastly_nsq/listener'
5
+ require 'fastly_nsq/message'
6
+ require 'fastly_nsq/producer'
7
+ require 'fastly_nsq/strategy'
8
+ require 'fastly_nsq/ssl_context'
1
9
  require 'fastly_nsq/version'
2
10
 
3
- require 'fastly_nsq/message_queue'
11
+ module FastlyNsq
12
+ def self.logger=(logger)
13
+ strategy.logger = logger
14
+ end
15
+
16
+ def self.logger
17
+ strategy.logger
18
+ end
19
+
20
+ def self.strategy
21
+ Strategy.for_queue
22
+ end
23
+ end
@@ -0,0 +1,68 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe FastlyNsq::Consumer do
4
+ let(:channel) { 'star_killer_base' }
5
+ let(:topic) { 'death_star' }
6
+ let(:consumer) { FastlyNsq::Consumer.new topic: topic, channel: channel }
7
+
8
+ describe 'when connected to a backend Consumer' do
9
+ let(:backend) { instance_double FastlyNsq::FakeBackend::Consumer, pop: nil, pop_without_blocking: nil, size: nil, terminate: nil }
10
+ let(:connector) { double 'Connector strategy', new: backend }
11
+
12
+ let(:consumer) do
13
+ FastlyNsq::Consumer.new topic: topic, channel: channel, connector: connector
14
+ end
15
+
16
+ it 'forwards #pop' do
17
+ expect(backend).to receive(:pop)
18
+ consumer.pop
19
+ end
20
+
21
+ it 'forwards #pop_without_blocking' do
22
+ expect(backend).to receive(:pop_without_blocking)
23
+ consumer.pop_without_blocking
24
+ end
25
+
26
+ it 'forwards #size' do
27
+ expect(backend).to receive(:size)
28
+ consumer.size
29
+ end
30
+
31
+ it 'forwards #terminate' do
32
+ expect(backend).to receive(:terminate)
33
+ consumer.terminate
34
+ end
35
+ end
36
+
37
+ describe 'using strategy to determine the consumer' do
38
+ module TestStrategy
39
+ module Consumer
40
+ @@never_terminated = true
41
+
42
+ module_function
43
+
44
+ def new(*_)
45
+ self
46
+ end
47
+
48
+ def terminate
49
+ raise 'Already terminated once' unless @@never_terminated
50
+ @@never_terminated = false
51
+ end
52
+
53
+ def was_terminated
54
+ !@@never_terminated
55
+ end
56
+ end
57
+ end
58
+
59
+ before do
60
+ allow(FastlyNsq).to receive(:strategy).and_return(TestStrategy)
61
+ end
62
+
63
+ it 'instantiates a consumer via Strategy' do
64
+ consumer.terminate
65
+ expect(TestStrategy::Consumer.was_terminated).to be_truthy
66
+ end
67
+ end
68
+ end
@@ -1,49 +1,49 @@
1
1
  require 'spec_helper'
2
2
 
3
- RSpec.describe FakeMessageQueue do
3
+ RSpec.describe FastlyNsq::FakeBackend do
4
4
  describe '@@queue' do
5
5
  it 'is initalized as an empty array' do
6
- expect(FakeMessageQueue.queue).to eq []
6
+ expect(FastlyNsq::FakeBackend.queue).to eq []
7
7
  end
8
8
  end
9
9
 
10
10
  describe '@@logger' do
11
11
  after do
12
- FakeMessageQueue.logger = Logger.new(nil)
12
+ FastlyNsq::FakeBackend.logger = Logger.new(nil)
13
13
  end
14
14
 
15
15
  it 'is initalized as an empty Ruby Logger' do
16
- expect(FakeMessageQueue.logger).to be_a Logger
16
+ expect(FastlyNsq::FakeBackend.logger).to be_a Logger
17
17
  end
18
18
 
19
19
  it 'can be set and retrieved' do
20
20
  logger = double('some logger')
21
- FakeMessageQueue.logger = logger
21
+ FastlyNsq::FakeBackend.logger = logger
22
22
 
23
- expect(FakeMessageQueue.logger).to eq logger
23
+ expect(FastlyNsq::FakeBackend.logger).to eq logger
24
24
  end
25
25
  end
26
26
 
27
27
  describe '.reset!' do
28
28
  it 'resets the fake message queue' do
29
- FakeMessageQueue.queue = ['hello']
30
- expect(FakeMessageQueue.queue.size).to eq 1
29
+ FastlyNsq::FakeBackend.queue = ['hello']
30
+ expect(FastlyNsq::FakeBackend.queue.size).to eq 1
31
31
 
32
- FakeMessageQueue.reset!
32
+ FastlyNsq::FakeBackend.reset!
33
33
 
34
- expect(FakeMessageQueue.queue).to be_empty
34
+ expect(FastlyNsq::FakeBackend.queue).to be_empty
35
35
  end
36
36
  end
37
37
  end
38
38
 
39
- RSpec.describe FakeMessageQueue::Producer do
39
+ RSpec.describe FastlyNsq::FakeBackend::Producer do
40
40
  let(:topic) { 'death_star' }
41
- let(:producer) { FakeMessageQueue::Producer.new topic: topic }
41
+ let(:producer) { FastlyNsq::FakeBackend::Producer.new topic: topic }
42
42
 
43
43
  it 'adds a new message to the queue' do
44
44
  producer.write('hello')
45
45
 
46
- expect(FakeMessageQueue.queue.size).to eq 1
46
+ expect(FastlyNsq::FakeBackend.queue.size).to eq 1
47
47
  end
48
48
 
49
49
  it 'has a `terminate` method which is a noop' do
@@ -51,18 +51,18 @@ RSpec.describe FakeMessageQueue::Producer do
51
51
  end
52
52
  end
53
53
 
54
- RSpec.describe FakeMessageQueue::Message do
54
+ RSpec.describe FastlyNsq::FakeBackend::Message do
55
55
  describe '#body' do
56
56
  it 'returns the body of the message' do
57
57
  topic = 'death_star'
58
58
  content = 'hello'
59
- producer = FakeMessageQueue::Producer.new(
59
+ producer = FastlyNsq::FakeBackend::Producer.new(
60
60
  nsqd: ENV.fetch('NSQD_TCP_ADDRESS'),
61
61
  topic: topic,
62
62
  )
63
63
  producer.write(content)
64
64
 
65
- message = FakeMessageQueue.queue.pop
65
+ message = FastlyNsq::FakeBackend.queue.pop
66
66
  body = message.body
67
67
 
68
68
  expect(content).to eq body
@@ -70,10 +70,10 @@ RSpec.describe FakeMessageQueue::Message do
70
70
  end
71
71
  end
72
72
 
73
- RSpec.describe FakeMessageQueue::Consumer do
73
+ RSpec.describe FastlyNsq::FakeBackend::Consumer do
74
74
  let(:topic) { 'death_star' }
75
75
  let(:channel) { 'star_killer_base' }
76
- let(:consumer) { FakeMessageQueue::Consumer.new topic: topic, channel: channel }
76
+ let(:consumer) { FastlyNsq::FakeBackend::Consumer.new topic: topic, channel: channel }
77
77
 
78
78
  describe 'when there are no messages on the queue' do
79
79
  it 'tells you there are 0 messages in the queue' do
@@ -81,8 +81,8 @@ RSpec.describe FakeMessageQueue::Consumer do
81
81
  end
82
82
 
83
83
  it 'blocks forever (until timeout) from #pop' do
84
- FakeMessageQueue.delay = 0.1
85
- delay = FakeMessageQueue.delay + 0.1
84
+ FastlyNsq::FakeBackend.delay = 0.1
85
+ delay = FastlyNsq::FakeBackend.delay + 0.1
86
86
 
87
87
  expect do
88
88
  Timeout.timeout(delay) do
@@ -99,8 +99,8 @@ RSpec.describe FakeMessageQueue::Consumer do
99
99
  end
100
100
 
101
101
  describe 'when there is a message on the queue' do
102
- let(:message) { FakeMessageQueue::Message.new 'hello' }
103
- before { FakeMessageQueue.queue = [message] }
102
+ let(:message) { FastlyNsq::FakeBackend::Message.new 'hello' }
103
+ before { FastlyNsq::FakeBackend.queue = [message] }
104
104
 
105
105
  it 'tells you there are messages in the queue' do
106
106
  expect(consumer.size).to eq 1
@@ -121,7 +121,7 @@ RSpec.describe FakeMessageQueue::Consumer do
121
121
 
122
122
  describe '#terminate' do
123
123
  it 'has a terminate method which is a noop' do
124
- consumer = instance_double('FakeMessageQueue::Consumer')
124
+ consumer = instance_double('FastlyNsq::FakeBackend::Consumer')
125
125
  allow(consumer).to receive(:terminate)
126
126
  end
127
127
  end
@@ -0,0 +1,116 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe FastlyNsq::Listener do
4
+ let(:topic) { 'testing_topic' }
5
+ let(:channel) { 'testing_channel' }
6
+ let(:consumer) { FastlyNsq::FakeBackend::Consumer.new topic: topic, channel: channel }
7
+ let(:logger) { double 'Logger', info: nil }
8
+
9
+ module TestMessageProcessor
10
+ @@messages_processed = []
11
+ Message = Struct.new(:body, :topic) do
12
+ def finish
13
+ @did_finish = true
14
+ end
15
+ end
16
+
17
+ def self.process(incoming_message)
18
+ @@messages_processed.push Message.new(incoming_message.to_s)
19
+ end
20
+
21
+ def self.messages_processed
22
+ @@messages_processed
23
+ end
24
+
25
+ def self.clear
26
+ @@messages_processed = []
27
+ end
28
+ end
29
+
30
+ let(:listener) do
31
+ FastlyNsq::Listener.new topic: topic,
32
+ processor: TestMessageProcessor,
33
+ consumer: consumer,
34
+ logger: logger
35
+ end
36
+
37
+ let(:message) { TestMessageProcessor::Message.new 'this is message body' }
38
+ let(:messages_processed) { TestMessageProcessor.messages_processed }
39
+ let(:expected_message) { TestMessageProcessor::Message.new('this is message body') }
40
+ let(:expected_messages) { [expected_message] }
41
+
42
+ describe 'instantiating without a consumer' do
43
+ it 'instantiates a consumer, passing the topic and channel' do
44
+ allow(FastlyNsq::Consumer).to receive(:new)
45
+
46
+ FastlyNsq::Listener.new topic: topic,
47
+ channel: channel,
48
+ processor: TestMessageProcessor,
49
+ consumer: nil
50
+
51
+ expect(FastlyNsq::Consumer).to have_received(:new).
52
+ with(topic: topic, channel: channel)
53
+ end
54
+ end
55
+
56
+ context 'when using the fake queue and it is empty', fake_queue: true do
57
+ before do
58
+ TestMessageProcessor.clear
59
+ FastlyNsq::FakeBackend.delay = 0.1
60
+ end
61
+
62
+ it 'blocks on the process for longer than the check cycle' do
63
+ delay = FastlyNsq::FakeBackend.delay + 0.1
64
+
65
+ expect do
66
+ Timeout.timeout(delay) do
67
+ listener.go run_once: true
68
+ end
69
+ end.to raise_error(Timeout::Error)
70
+ end
71
+ end
72
+
73
+ describe 'when processing next message' do
74
+ before(:each) do
75
+ TestMessageProcessor.clear
76
+ allow(consumer).to receive(:pop).and_return(message)
77
+ end
78
+
79
+ it 'processes the next message' do
80
+ listener.go run_once: true
81
+
82
+ expect(messages_processed).to eql(expected_messages)
83
+ end
84
+
85
+ it 'finishes the message' do
86
+ allow(message).to receive(:finish)
87
+
88
+ listener.go run_once: true
89
+
90
+ expect(message).to have_received(:finish).once
91
+ end
92
+
93
+ it 'logs info for the message body' do
94
+ allow(logger).to receive(:info)
95
+ listener.go run_once: true
96
+
97
+ expect(logger).to have_received(:info).once.with(/\[NSQ\] Message Received: #{message.body}/)
98
+ end
99
+
100
+ context 'when preprocessor is provided' do
101
+ it 'calls the preprocessor' do
102
+ preprocessor_was_called = false
103
+ preprocessor = ->(*_args) { preprocessor_was_called = true }
104
+
105
+ listener = FastlyNsq::Listener.new topic: topic,
106
+ processor: TestMessageProcessor,
107
+ consumer: consumer,
108
+ logger: logger,
109
+ preprocessor: preprocessor
110
+
111
+ listener.go run_once: true
112
+ expect(preprocessor_was_called).to be_truthy
113
+ end
114
+ end
115
+ end
116
+ end
@@ -0,0 +1,24 @@
1
+ require 'spec_helper'
2
+ require 'json'
3
+
4
+ RSpec.describe FastlyNsq::Message do
5
+ let(:body) { { 'data' => 'goes here', 'other_field' => 'is over here' } }
6
+ let(:json_body) { body.to_json }
7
+ subject { FastlyNsq::Message.new json_body }
8
+
9
+ it 'preserves original message body as raw_body' do
10
+ expect(subject.raw_body).to eq(json_body)
11
+ end
12
+
13
+ it 'presents parsed message body as body' do
14
+ expect(subject.body).to eq(body)
15
+ end
16
+
17
+ it 'plucks data as data' do
18
+ expect(subject.data).to eq('goes here')
19
+ end
20
+
21
+ it 'aliases raw_body to to_s' do
22
+ expect(subject.to_s).to eq(json_body)
23
+ end
24
+ end
@@ -0,0 +1,56 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe FastlyNsq::Producer do
4
+ let(:topic) { 'death_star' }
5
+ let(:producer) { FastlyNsq::Producer.new(topic: topic) }
6
+
7
+ describe 'when connector connects to a backend Producer' do
8
+ let(:backend) { instance_double FastlyNsq::FakeBackend::Producer, write: nil, terminate: nil }
9
+ let(:connector) { double 'Connector', new: backend }
10
+ let(:producer) do
11
+ FastlyNsq::Producer.new topic: topic, connector: connector
12
+ end
13
+
14
+ it 'forwards #write' do
15
+ expect(backend).to receive(:write).with("it's a message")
16
+ producer.write "it's a message"
17
+ end
18
+
19
+ it 'forwards #terminate' do
20
+ expect(backend).to receive(:terminate)
21
+ producer.terminate
22
+ end
23
+ end
24
+
25
+ describe 'using the default connector' do
26
+ module TestStrategy
27
+ module Producer
28
+ @@never_terminated = true
29
+
30
+ module_function
31
+
32
+ def new(*_)
33
+ self
34
+ end
35
+
36
+ def terminate
37
+ raise 'Already terminated once' unless @@never_terminated
38
+ @@never_terminated = false
39
+ end
40
+
41
+ def was_terminated
42
+ !@@never_terminated
43
+ end
44
+ end
45
+ end
46
+
47
+ before do
48
+ allow(FastlyNsq).to receive(:strategy).and_return(TestStrategy)
49
+ end
50
+
51
+ it 'instantiates a producer via Strategy' do
52
+ producer.terminate
53
+ expect(TestStrategy::Producer.was_terminated).to be_truthy
54
+ end
55
+ end
56
+ end