firehose 1.2.20 → 1.3.6

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 (65) hide show
  1. checksums.yaml +4 -4
  2. data/.codeclimate.yml +29 -0
  3. data/.dockerignore +2 -0
  4. data/.gitignore +3 -1
  5. data/.rubocop.yml +1156 -0
  6. data/.ruby-version +1 -0
  7. data/.travis.yml +3 -7
  8. data/CHANGELOG.md +15 -0
  9. data/Dockerfile +11 -0
  10. data/Gemfile +4 -2
  11. data/Procfile.dev +0 -1
  12. data/README.md +66 -8
  13. data/Rakefile +43 -32
  14. data/coffeelint.json +129 -0
  15. data/docker-compose.yml +17 -0
  16. data/firehose.gemspec +5 -9
  17. data/karma.config.coffee +89 -0
  18. data/lib/assets/javascripts/firehose.js.coffee +1 -2
  19. data/lib/assets/javascripts/firehose/consumer.js.coffee +18 -2
  20. data/lib/assets/javascripts/firehose/core.js.coffee +2 -1
  21. data/lib/assets/javascripts/firehose/long_poll.js.coffee +69 -8
  22. data/lib/assets/javascripts/firehose/multiplexed_consumer.js.coffee +74 -0
  23. data/lib/assets/javascripts/firehose/transport.js.coffee +4 -2
  24. data/lib/assets/javascripts/firehose/web_socket.js.coffee +51 -5
  25. data/lib/firehose/cli.rb +2 -1
  26. data/lib/firehose/client/producer.rb +10 -4
  27. data/lib/firehose/rack/consumer.rb +39 -0
  28. data/lib/firehose/rack/consumer/http_long_poll.rb +118 -45
  29. data/lib/firehose/rack/consumer/web_socket.rb +133 -28
  30. data/lib/firehose/rack/ping.rb +1 -1
  31. data/lib/firehose/rack/publisher.rb +10 -4
  32. data/lib/firehose/server.rb +9 -9
  33. data/lib/firehose/server/channel.rb +23 -31
  34. data/lib/firehose/server/message_buffer.rb +59 -0
  35. data/lib/firehose/server/publisher.rb +16 -17
  36. data/lib/firehose/server/redis.rb +32 -0
  37. data/lib/firehose/server/subscriber.rb +7 -7
  38. data/lib/firehose/version.rb +2 -2
  39. data/package.json +14 -2
  40. data/spec/integrations/shared_examples.rb +89 -7
  41. data/spec/javascripts/firehose/multiplexed_consumer_spec.coffee +72 -0
  42. data/spec/javascripts/firehose/transport_spec.coffee +0 -2
  43. data/spec/javascripts/firehose/websocket_spec.coffee +2 -0
  44. data/spec/javascripts/helpers/spec_helper.js +1 -0
  45. data/spec/javascripts/support/jquery-1.11.1.js +10308 -0
  46. data/{lib/assets/javascripts/vendor → spec/javascripts/support}/json2.js +0 -0
  47. data/spec/javascripts/support/spec_helper.coffee +3 -0
  48. data/spec/lib/assets_spec.rb +8 -8
  49. data/spec/lib/client/producer_spec.rb +14 -14
  50. data/spec/lib/firehose_spec.rb +2 -2
  51. data/spec/lib/rack/consumer/http_long_poll_spec.rb +21 -3
  52. data/spec/lib/rack/consumer_spec.rb +4 -4
  53. data/spec/lib/rack/ping_spec.rb +4 -4
  54. data/spec/lib/rack/publisher_spec.rb +5 -5
  55. data/spec/lib/server/app_spec.rb +2 -2
  56. data/spec/lib/server/channel_spec.rb +58 -44
  57. data/spec/lib/server/message_buffer_spec.rb +148 -0
  58. data/spec/lib/server/publisher_spec.rb +29 -22
  59. data/spec/lib/server/redis_spec.rb +13 -0
  60. data/spec/lib/server/subscriber_spec.rb +14 -13
  61. data/spec/spec_helper.rb +8 -1
  62. metadata +34 -95
  63. data/.rbenv-version +0 -1
  64. data/Guardfile +0 -31
  65. data/config/evergreen.rb +0 -9
@@ -0,0 +1,3 @@
1
+ #= require ./jquery-1.11.1
2
+ #= require ./json2
3
+ #= require ./sinon-1.7.3
@@ -3,19 +3,19 @@ require 'sprockets'
3
3
 
4
4
  describe Firehose::Assets do
5
5
  describe ".path" do
6
- it "should have root path" do
7
- Firehose::Assets.path('poop').should == File.expand_path('../../../lib/assets/poop', __FILE__)
6
+ it "has root path" do
7
+ expect(Firehose::Assets.path('poop')).to eql(File.expand_path('../../../lib/assets/poop', __FILE__))
8
8
  end
9
9
 
10
- it "should accept folders" do
11
- Firehose::Assets.path('poop').should == File.join(Firehose::Assets.path, 'poop')
10
+ it "accepts folders" do
11
+ expect(Firehose::Assets.path('poop')).to eql(File.join(Firehose::Assets.path, 'poop'))
12
12
  end
13
13
  end
14
14
 
15
15
  describe "Sprockets.configure" do
16
- it "should configure environment" do
16
+ it "configures environment" do
17
17
  env = Firehose::Assets::Sprockets.configure Sprockets::Environment.new
18
- env.paths.should include(Firehose::Assets.path('javascripts'))
18
+ expect(env.paths).to include(Firehose::Assets.path('javascripts'))
19
19
  end
20
20
  end
21
21
 
@@ -28,8 +28,8 @@ describe Firehose::Assets do
28
28
  "window.Firehose || (window.Firehose = {})"
29
29
  ]
30
30
  end
31
- it "should compile javascript" do
32
- Firehose::Assets::Sprockets.javascript.should include(*js_spot_checks)
31
+ it "compiles javascript" do
32
+ expect(Firehose::Assets::Sprockets.javascript).to include(*js_spot_checks)
33
33
  end
34
34
  end
35
35
  end
@@ -14,58 +14,58 @@ describe Firehose::Client::Producer::Http do
14
14
  Firehose::Client::Producer::Http.adapter = nil
15
15
  end
16
16
 
17
- it "should publish message to channel" do
17
+ it "publishes message to channel" do
18
18
  publish_stub.to_return(:body => "", :status => 202)
19
19
 
20
20
  Firehose::Client::Producer::Http.new.publish(message).to(channel)
21
- WebMock.should have_requested(:put, url).with { |req| req.body == message }
21
+ expect(WebMock).to have_requested(:put, url).with { |req| req.body == message }
22
22
  end
23
23
 
24
24
  context 'prefix is specified in URI' do
25
25
  let(:firehose_uri) {"#{Firehose::URI}/prefix"}
26
26
  let(:url) { "#{firehose_uri}#{channel}"}
27
27
 
28
- it "should publish message to channel" do
28
+ it "publishes message to channel" do
29
29
  publish_stub.to_return(:body => "", :status => 202)
30
30
 
31
31
  Firehose::Client::Producer::Http.new(firehose_uri).publish(message).to(channel)
32
- WebMock.should have_requested(:put, url).with { |req| req.body == message }
32
+ expect(WebMock).to have_requested(:put, url).with { |req| req.body == message }
33
33
  end
34
34
  end
35
35
 
36
- it "should publish message to channel with expiry headers" do
36
+ it "publishes message to channel with expiry headers" do
37
37
  publish_stub.to_return(:body => "", :status => 202)
38
38
  ttl = 20
39
39
 
40
40
  Firehose::Client::Producer::Http.new.publish(message).to(channel, :ttl => ttl)
41
- WebMock.should have_requested(:put, url).with { |req| req.body == message and req.headers['Cache-Control'] == "max-age=#{ttl}" }
41
+ expect(WebMock).to have_requested(:put, url).with { |req| req.body == message and req.headers['Cache-Control'] == "max-age=#{ttl}" }
42
42
  end
43
43
 
44
44
  describe "connection error handling" do
45
- it "should raise PublishError if not 201" do
45
+ it "raises PublishError if not 201" do
46
46
  publish_stub.to_return(:body => "", :status => 500)
47
47
 
48
- lambda{
48
+ expect {
49
49
  Firehose::Client::Producer::Http.new.publish(message).to(channel)
50
- }.should raise_exception(Firehose::Client::Producer::Http::PublishError)
50
+ }.to raise_exception(Firehose::Client::Producer::Http::PublishError)
51
51
  end
52
52
 
53
- it "should use .error_handler if not 201" do
53
+ it "uses .error_handler if not 201" do
54
54
  publish_stub.to_return(:body => "", :status => 500)
55
55
 
56
56
  producer = Firehose::Client::Producer::Http.new
57
57
  producer.on_error do |e|
58
- e.message.should =~ /could not publish.+to/i
58
+ expect(e.message).to match(/could not publish.+to/i)
59
59
  end
60
60
  producer.publish(message).to(channel)
61
61
  end
62
62
 
63
- it "should raise TimeoutError if timed out" do
63
+ it "raises TimeoutError if timed out" do
64
64
  publish_stub.to_timeout
65
65
 
66
- lambda{
66
+ expect {
67
67
  Firehose::Client::Producer::Http.new.publish(message).to(channel)
68
- }.should raise_exception(Firehose::Client::Producer::Http::TimeoutError)
68
+ }.to raise_exception(Firehose::Client::Producer::Http::TimeoutError)
69
69
  end
70
70
  end
71
71
  end
@@ -1,7 +1,7 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe Firehose do
4
- it "should have 7474 for default port" do
5
- Firehose::URI.to_s.should == '//0.0.0.0:7474'
4
+ it "has 7474 for default port" do
5
+ expect(Firehose::URI.to_s).to eql('//0.0.0.0:7474')
6
6
  end
7
7
  end
@@ -1,12 +1,30 @@
1
1
  require 'spec_helper'
2
+ require 'rack/test'
3
+ require 'async_rack_test'
2
4
 
3
5
  describe Firehose::Rack::Consumer::HttpLongPoll do
6
+ include AsyncRackTest::Methods
7
+ let(:app) { Firehose::Rack::Consumer::HttpLongPoll.new }
8
+
4
9
  context "transport" do
5
10
  # Transport for Firehose::Rack::App class is tested via the spec/integrations suite.
6
11
  end
7
12
  context "configuration" do
8
- it "should have #timeout" do
9
- Firehose::Rack::Consumer::HttpLongPoll.new(200).timeout.should == 200
13
+ it "has #timeout" do
14
+ expect(Firehose::Rack::Consumer::HttpLongPoll.new(200).timeout).to eql(200)
15
+ end
16
+ end
17
+
18
+ context "POST request" do
19
+ before do
20
+ post "/blah"
21
+ end
22
+ it "returns 405 for POST" do
23
+ expect(last_response.status).to eql(405)
24
+ end
25
+
26
+ it "specifies GET in the Allow header" do
27
+ expect(last_response.headers["Allow"]).to eql("GET")
10
28
  end
11
29
  end
12
- end
30
+ end
@@ -6,14 +6,14 @@ describe Firehose::Rack::Consumer, :type => :request do
6
6
  let(:app) { Firehose::Rack::Consumer.new }
7
7
  let(:path) { "/test/path/#{Time.now.to_i}" }
8
8
 
9
- it "should have Content-Length on OPTIONS request"
10
- it "should have Content-Length on GET request"
9
+ it "has Content-Length on OPTIONS request"
10
+ it "has Content-Length on GET request"
11
11
 
12
12
  context "configuration" do
13
13
  let(:app) { Firehose::Rack::Consumer }
14
14
 
15
- it "should configure long polling timeout" do
16
- app.new{ |a| a.http_long_poll.timeout = 300 }.http_long_poll.timeout.should == 300
15
+ it "configures long polling timeout" do
16
+ expect(app.new{ |a| a.http_long_poll.timeout = 300 }.http_long_poll.timeout).to eql(300)
17
17
  end
18
18
  end
19
19
  end
@@ -12,17 +12,17 @@ describe Firehose::Rack::Ping, :type => :request do
12
12
  context 'redis is available' do
13
13
  before { deferrable.succeed Firehose::Rack::Ping::PingCheck::TEST_VALUE }
14
14
 
15
- it "should return 200" do
15
+ it "returns 200" do
16
16
  ahead path
17
- last_response.status.should == 200
17
+ expect(last_response.status).to eql(200)
18
18
  end
19
19
  end
20
20
 
21
21
  context 'redis is not available' do
22
22
  before { deferrable.fail 'some error' }
23
- it "should return 500" do
23
+ it "returns 500" do
24
24
  ahead path
25
- last_response.status.should == 500
25
+ expect(last_response.status).to eql(500)
26
26
  end
27
27
  end
28
28
  end
@@ -11,19 +11,19 @@ describe Firehose::Rack::Publisher, :type => :request do
11
11
  context 'publishing is successful' do
12
12
  before { deferrable.succeed }
13
13
 
14
- it "should return 202" do
14
+ it "returns 202" do
15
15
  app.stub(:publisher => double('publisher', :publish => deferrable))
16
16
  aput path, :body => "some nice little message"
17
- last_response.status.should == 202
17
+ expect(last_response.status).to eql(202)
18
18
  end
19
19
 
20
- it "should have Content-Length of zero" do
20
+ it "has Content-Length of zero" do
21
21
  app.stub(:publisher => double('publisher', :publish => deferrable))
22
22
  aput path, :body => "some nice little message"
23
- last_response.headers['Content-Length'].should == '0'
23
+ expect(last_response.headers['Content-Length']).to eql('0')
24
24
  end
25
25
 
26
- it "should parse Cache-Control max-age" do
26
+ it "parses Cache-Control max-age" do
27
27
  body = "howdy dude!"
28
28
  ttl = '92'
29
29
 
@@ -8,8 +8,8 @@ describe Firehose::Rack::App do
8
8
  context "configuration" do
9
9
  let(:app) { Firehose::Rack::App }
10
10
 
11
- it "should configure long polling timeout" do
12
- app.new{ |a| a.consumer.http_long_poll.timeout = 300 }.consumer.http_long_poll.timeout.should == 300
11
+ it "configures long polling timeout" do
12
+ expect(app.new{ |a| a.consumer.http_long_poll.timeout = 300 }.consumer.http_long_poll.timeout).to eql(300)
13
13
  end
14
14
  end
15
15
  end
@@ -4,17 +4,23 @@ describe Firehose::Server::Channel do
4
4
  include EM::TestHelper
5
5
 
6
6
  let(:channel_key) { '/bears/are/mean' }
7
- let(:channel) { Firehose::Server::Channel.new(channel_key, EM::Hiredis.connect, subscriber) }
8
- let(:subscriber) { Firehose::Server::Subscriber.new(EM::Hiredis.connect) }
7
+ let(:channel) { Firehose::Server::Channel.new(channel_key, Firehose::Server.redis.connection, subscriber) }
8
+ let(:subscriber) { Firehose::Server::Subscriber.new(Firehose::Server.redis.connection) }
9
9
  let(:message) { 'Raaaarrrrrr!!!!' }
10
10
  let(:publisher) { Firehose::Server::Publisher.new }
11
11
 
12
- describe "#next_message" do
13
- it "should wait for message if message was not published before subscription" do
12
+ def push_message
13
+ redis_exec 'lpush', "firehose:#{channel_key}:list", message
14
+ redis_exec 'set', "firehose:#{channel_key}:sequence", '100'
15
+ end
16
+
17
+ describe "#next_messages" do
18
+ it "waits for message if message was not published before subscription" do
14
19
  em do
15
- channel.next_message.callback do |msg, seq|
16
- msg.should == message
17
- seq.should == 1
20
+ channel.next_messages.callback do |messages|
21
+ msg = messages.first
22
+ expect(msg.payload).to eql(message)
23
+ expect(msg.sequence).to eql(1)
18
24
  em.next_tick { em.stop }
19
25
  end
20
26
 
@@ -22,14 +28,14 @@ describe Firehose::Server::Channel do
22
28
  end
23
29
  end
24
30
 
25
- it "should return the latest message and sequence if no sequence is given" do
26
- redis_exec 'lpush', "firehose:#{channel_key}:list", message
27
- redis_exec 'set', "firehose:#{channel_key}:sequence", '100'
31
+ it "returns the latest message and sequence if no sequence is given" do
32
+ push_message
28
33
 
29
34
  em do
30
- channel.next_message.callback do |msg, seq|
31
- msg.should == message
32
- seq.should == 100
35
+ channel.next_messages.callback do |messages|
36
+ msg = messages.first
37
+ expect(msg.payload).to eql(message)
38
+ expect(msg.sequence).to eql(100)
33
39
 
34
40
  # This must happen _after_ the callback runs in order to pass consistently.
35
41
  em.next_tick { em.stop }
@@ -37,14 +43,15 @@ describe Firehose::Server::Channel do
37
43
  end
38
44
  end
39
45
 
40
- it "should wait for message if most recent sequence is given" do
41
- redis_exec 'lpush', "firehose:#{channel_key}:list", message
42
- redis_exec 'set', "firehose:#{channel_key}:sequence", '100'
46
+ it "waits for message if most recent sequence is given" do
47
+ push_message
43
48
 
44
49
  em 3 do
45
- channel.next_message(100).callback do |msg, seq|
46
- msg.should == message
47
- seq.should == 101
50
+ channel.next_messages(100).callback do |messages|
51
+ msg = messages.first.payload
52
+ seq = messages.first.sequence
53
+ expect(msg).to eql(message)
54
+ expect(seq).to eql(101)
48
55
  em.next_tick { em.stop }
49
56
  end.errback
50
57
 
@@ -52,14 +59,15 @@ describe Firehose::Server::Channel do
52
59
  end
53
60
  end
54
61
 
55
- it "should wait for message if a future sequence is given" do
56
- redis_exec 'lpush', "firehose:#{channel_key}:list", message
57
- redis_exec 'set', "firehose:#{channel_key}:sequence", '100'
62
+ it "waits for message if a future sequence is given" do
63
+ push_message
58
64
 
59
65
  em 3 do
60
- channel.next_message(101).callback do |msg, seq|
61
- msg.should == message
62
- seq.should == 101
66
+ channel.next_messages(101).callback do |messages|
67
+ msg = messages.first.payload
68
+ seq = messages.first.sequence
69
+ expect(msg).to eql(message)
70
+ expect(seq).to eql(101)
63
71
  em.next_tick { em.stop }
64
72
  end.errback
65
73
 
@@ -67,14 +75,16 @@ describe Firehose::Server::Channel do
67
75
  end
68
76
  end
69
77
 
70
- it "should immediatly get a message if message sequence is behind and in list" do
78
+ it "immediatly gets a message if message sequence is behind and in list" do
71
79
  messages = %w[a b c d e]
72
80
 
73
81
  em 3 do
74
82
  publish_messages(messages) do
75
- channel.next_message(2).callback do |msg, seq|
76
- msg.should == 'c'
77
- seq.should == 3
83
+ channel.next_messages(2).callback do |messages|
84
+ msg = messages.first.payload
85
+ seq = messages.first.sequence
86
+ expect(msg).to eql('c')
87
+ expect(seq).to eql(3)
78
88
 
79
89
  # This must happen _after_ the callback runs in order to pass consistently.
80
90
  em.next_tick { em.stop }
@@ -83,14 +93,16 @@ describe Firehose::Server::Channel do
83
93
  end
84
94
  end
85
95
 
86
- it "should get current message if sequence is really far behind in list" do
96
+ it "gets current message if sequence is really far behind in list" do
87
97
  messages = ('aa'..'zz').to_a
88
98
 
89
99
  em 3 do
90
100
  publish_messages(messages) do
91
- channel.next_message(2).callback do |msg, seq|
92
- msg.should == messages.last
93
- seq.should == messages.size
101
+ channel.next_messages(2).callback do |msgs|
102
+ msg = msgs.last.payload
103
+ seq = msgs.last.sequence
104
+ expect(msg).to eql(messages.last)
105
+ expect(seq).to eql(messages.size)
94
106
 
95
107
  # This must happen _after_ the callback runs in order to pass consistently.
96
108
  em.next_tick { em.stop }
@@ -100,15 +112,16 @@ describe Firehose::Server::Channel do
100
112
  end
101
113
 
102
114
  context "a timeout is set" do
103
- it "should timeout if message isn't published in time" do
104
- redis_exec 'lpush', "firehose:#{channel_key}:list", message
105
- redis_exec 'set', "firehose:#{channel_key}:sequence", '100'
115
+ it "times out if message isn't published in time" do
116
+ push_message
106
117
 
107
118
  em 3 do
108
- channel.next_message(100, :timeout => 1).callback do |msg, seq|
119
+ channel.next_messages(100, :timeout => 1).callback do |messages|
120
+ msg = messages.first.payload
121
+ seq = messages.first.sequence
109
122
  raise 'test failed'
110
123
  end.errback do |e|
111
- e.should == :timeout
124
+ expect(e).to eql(:timeout)
112
125
  em.next_tick { em.stop }
113
126
  end
114
127
 
@@ -118,14 +131,15 @@ describe Firehose::Server::Channel do
118
131
  end
119
132
  end
120
133
 
121
- it "should not timeout if message is published in time" do
122
- redis_exec 'lpush', "firehose:#{channel_key}:list", message
123
- redis_exec 'set', "firehose:#{channel_key}:sequence", '100'
134
+ it "does not timeout if message is published in time" do
135
+ push_message
124
136
 
125
137
  em 3 do
126
- d = channel.next_message(100, :timeout => 2).callback do |msg, seq|
127
- msg.should == message
128
- seq.should == 101
138
+ d = channel.next_messages(100, :timeout => 2).callback do |messages|
139
+ msg = messages.first.payload
140
+ seq = messages.first.sequence
141
+ expect(msg).to eql(message)
142
+ expect(seq).to eql(101)
129
143
  EM::add_timer(1) do
130
144
  em.stop
131
145
  end
@@ -0,0 +1,148 @@
1
+ require 'spec_helper'
2
+
3
+ describe Firehose::Server::MessageBuffer do
4
+ it "has 100 DEFAULT_SIZE" do
5
+ expect(Firehose::Server::MessageBuffer::DEFAULT_SIZE).to eql(100)
6
+ end
7
+
8
+ subject { Firehose::Server::MessageBuffer.new(messages, channel_sequence, consumer_sequence) }
9
+
10
+ context "no messages" do
11
+ let(:messages) { [] }
12
+ let(:channel_sequence) { 0 }
13
+ shared_examples "empty channel" do
14
+ it "has empty remaining" do
15
+ expect(subject.remaining_messages).to be_empty
16
+ end
17
+ end
18
+ context "nil sequence" do
19
+ let(:consumer_sequence) { nil }
20
+ it_behaves_like "empty channel"
21
+ end
22
+ context "0 sequence" do
23
+ let(:consumer_sequence) { 0 }
24
+ it_behaves_like "empty channel"
25
+ end
26
+ context "negative sequence" do
27
+ let(:consumer_sequence) { -1 }
28
+ it_behaves_like "empty channel"
29
+ end
30
+ context "positive sequence" do
31
+ let(:consumer_sequence) { 100 }
32
+ it_behaves_like "empty channel"
33
+ end
34
+ end
35
+
36
+ context "has messages (buffer size of 5, at channel sequence 5)" do
37
+ let(:messages) { %w[a b c d e] }
38
+ let(:channel_sequence) { 5 }
39
+
40
+ shared_examples "latest message" do
41
+ it "returns just the latest messages" do
42
+ expect(subject.remaining_messages.map(&:payload)).to eql(["e"])
43
+ end
44
+ it "has the correct sequence" do
45
+ expect(subject.remaining_messages.map(&:sequence)).to eql([5])
46
+ end
47
+ end
48
+
49
+ context "nil sequence" do
50
+ let(:consumer_sequence) { nil }
51
+ it_behaves_like "latest message"
52
+ end
53
+ context "0 sequence" do
54
+ let(:consumer_sequence) { 0 }
55
+ it_behaves_like "latest message"
56
+ end
57
+ context "negative sequence" do
58
+ let(:consumer_sequence) { -1 }
59
+ it_behaves_like "latest message"
60
+ end
61
+
62
+ context "running behind" do
63
+ let(:consumer_sequence) { 2 }
64
+ it "has some remaining messages" do
65
+ expect(subject.remaining_messages.map(&:payload)).to eql(%w[c d e])
66
+ end
67
+ it "has the correct sequences" do
68
+ expect(subject.remaining_messages.map(&:sequence)).to eql((3..5).to_a)
69
+ end
70
+ end
71
+
72
+ context "caught up" do
73
+ let(:consumer_sequence) { 5 }
74
+ it "has no remaining messages" do
75
+ expect(subject.remaining_messages).to be_empty
76
+ end
77
+ end
78
+ context "ahead" do
79
+ let(:consumer_sequence) { 10 }
80
+ it "has no remaining messages" do
81
+ expect(subject.remaining_messages).to be_empty
82
+ end
83
+ end
84
+ end
85
+ context "has messages (buffer size of 5, at channel sequence 10)" do
86
+ let(:messages) { %w[f g h i j] }
87
+ let(:channel_sequence) { 10 }
88
+
89
+ shared_examples "latest message" do
90
+ it "returns just the latest messages" do
91
+ expect(subject.remaining_messages.map(&:payload)).to eql(["j"])
92
+ end
93
+ it "has the correct sequence" do
94
+ expect(subject.remaining_messages.map(&:sequence)).to eql([10])
95
+ end
96
+ end
97
+
98
+ context "nil sequence" do
99
+ let(:consumer_sequence) { nil }
100
+ it_behaves_like "latest message"
101
+ end
102
+ context "0 sequence" do
103
+ let(:consumer_sequence) { 0 }
104
+ it_behaves_like "latest message"
105
+ end
106
+ context "negative sequence" do
107
+ let(:consumer_sequence) { -1 }
108
+ it_behaves_like "latest message"
109
+ end
110
+ context "underwater" do
111
+ let(:consumer_sequence) { 2 }
112
+ it_behaves_like "latest message"
113
+ end
114
+
115
+ context "almost underwater" do
116
+ let(:consumer_sequence) { 5 }
117
+ it "has all remaining messages" do
118
+ expect(subject.remaining_messages.map(&:payload)).to eql(%w[f g h i j])
119
+ end
120
+ it "has the correct sequences" do
121
+ expect(subject.remaining_messages.map(&:sequence)).to eql((6..10).to_a)
122
+ end
123
+ end
124
+
125
+ context "running behind" do
126
+ let(:consumer_sequence) { 7 }
127
+ it "has some remaining messages" do
128
+ expect(subject.remaining_messages.map(&:payload)).to eql(%w[h i j])
129
+ end
130
+ it "has the correct sequences" do
131
+ expect(subject.remaining_messages.map(&:sequence)).to eql((8..10).to_a)
132
+ end
133
+ end
134
+
135
+ context "caught up" do
136
+ let(:consumer_sequence) { 10 }
137
+ it "has no remaining messages" do
138
+ expect(subject.remaining_messages).to be_empty
139
+ end
140
+ end
141
+ context "ahead" do
142
+ let(:consumer_sequence) { 15 }
143
+ it "has no remaining messages" do
144
+ expect(subject.remaining_messages).to be_empty
145
+ end
146
+ end
147
+ end
148
+ end