fastly_nsq 0.6.0 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
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