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.
- checksums.yaml +4 -4
- data/.codeclimate.yml +29 -0
- data/.dockerignore +2 -0
- data/.gitignore +3 -1
- data/.rubocop.yml +1156 -0
- data/.ruby-version +1 -0
- data/.travis.yml +3 -7
- data/CHANGELOG.md +15 -0
- data/Dockerfile +11 -0
- data/Gemfile +4 -2
- data/Procfile.dev +0 -1
- data/README.md +66 -8
- data/Rakefile +43 -32
- data/coffeelint.json +129 -0
- data/docker-compose.yml +17 -0
- data/firehose.gemspec +5 -9
- data/karma.config.coffee +89 -0
- data/lib/assets/javascripts/firehose.js.coffee +1 -2
- data/lib/assets/javascripts/firehose/consumer.js.coffee +18 -2
- data/lib/assets/javascripts/firehose/core.js.coffee +2 -1
- data/lib/assets/javascripts/firehose/long_poll.js.coffee +69 -8
- data/lib/assets/javascripts/firehose/multiplexed_consumer.js.coffee +74 -0
- data/lib/assets/javascripts/firehose/transport.js.coffee +4 -2
- data/lib/assets/javascripts/firehose/web_socket.js.coffee +51 -5
- data/lib/firehose/cli.rb +2 -1
- data/lib/firehose/client/producer.rb +10 -4
- data/lib/firehose/rack/consumer.rb +39 -0
- data/lib/firehose/rack/consumer/http_long_poll.rb +118 -45
- data/lib/firehose/rack/consumer/web_socket.rb +133 -28
- data/lib/firehose/rack/ping.rb +1 -1
- data/lib/firehose/rack/publisher.rb +10 -4
- data/lib/firehose/server.rb +9 -9
- data/lib/firehose/server/channel.rb +23 -31
- data/lib/firehose/server/message_buffer.rb +59 -0
- data/lib/firehose/server/publisher.rb +16 -17
- data/lib/firehose/server/redis.rb +32 -0
- data/lib/firehose/server/subscriber.rb +7 -7
- data/lib/firehose/version.rb +2 -2
- data/package.json +14 -2
- data/spec/integrations/shared_examples.rb +89 -7
- data/spec/javascripts/firehose/multiplexed_consumer_spec.coffee +72 -0
- data/spec/javascripts/firehose/transport_spec.coffee +0 -2
- data/spec/javascripts/firehose/websocket_spec.coffee +2 -0
- data/spec/javascripts/helpers/spec_helper.js +1 -0
- data/spec/javascripts/support/jquery-1.11.1.js +10308 -0
- data/{lib/assets/javascripts/vendor → spec/javascripts/support}/json2.js +0 -0
- data/spec/javascripts/support/spec_helper.coffee +3 -0
- data/spec/lib/assets_spec.rb +8 -8
- data/spec/lib/client/producer_spec.rb +14 -14
- data/spec/lib/firehose_spec.rb +2 -2
- data/spec/lib/rack/consumer/http_long_poll_spec.rb +21 -3
- data/spec/lib/rack/consumer_spec.rb +4 -4
- data/spec/lib/rack/ping_spec.rb +4 -4
- data/spec/lib/rack/publisher_spec.rb +5 -5
- data/spec/lib/server/app_spec.rb +2 -2
- data/spec/lib/server/channel_spec.rb +58 -44
- data/spec/lib/server/message_buffer_spec.rb +148 -0
- data/spec/lib/server/publisher_spec.rb +29 -22
- data/spec/lib/server/redis_spec.rb +13 -0
- data/spec/lib/server/subscriber_spec.rb +14 -13
- data/spec/spec_helper.rb +8 -1
- metadata +34 -95
- data/.rbenv-version +0 -1
- data/Guardfile +0 -31
- data/config/evergreen.rb +0 -9
File without changes
|
data/spec/lib/assets_spec.rb
CHANGED
@@ -3,19 +3,19 @@ require 'sprockets'
|
|
3
3
|
|
4
4
|
describe Firehose::Assets do
|
5
5
|
describe ".path" do
|
6
|
-
it "
|
7
|
-
Firehose::Assets.path('poop').
|
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 "
|
11
|
-
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 "
|
16
|
+
it "configures environment" do
|
17
17
|
env = Firehose::Assets::Sprockets.configure Sprockets::Environment.new
|
18
|
-
env.paths.
|
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 "
|
32
|
-
Firehose::Assets::Sprockets.javascript.
|
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 "
|
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.
|
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 "
|
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.
|
32
|
+
expect(WebMock).to have_requested(:put, url).with { |req| req.body == message }
|
33
33
|
end
|
34
34
|
end
|
35
35
|
|
36
|
-
it "
|
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.
|
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 "
|
45
|
+
it "raises PublishError if not 201" do
|
46
46
|
publish_stub.to_return(:body => "", :status => 500)
|
47
47
|
|
48
|
-
|
48
|
+
expect {
|
49
49
|
Firehose::Client::Producer::Http.new.publish(message).to(channel)
|
50
|
-
}.
|
50
|
+
}.to raise_exception(Firehose::Client::Producer::Http::PublishError)
|
51
51
|
end
|
52
52
|
|
53
|
-
it "
|
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.
|
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 "
|
63
|
+
it "raises TimeoutError if timed out" do
|
64
64
|
publish_stub.to_timeout
|
65
65
|
|
66
|
-
|
66
|
+
expect {
|
67
67
|
Firehose::Client::Producer::Http.new.publish(message).to(channel)
|
68
|
-
}.
|
68
|
+
}.to raise_exception(Firehose::Client::Producer::Http::TimeoutError)
|
69
69
|
end
|
70
70
|
end
|
71
71
|
end
|
data/spec/lib/firehose_spec.rb
CHANGED
@@ -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 "
|
9
|
-
Firehose::Rack::Consumer::HttpLongPoll.new(200).timeout.
|
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 "
|
10
|
-
it "
|
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 "
|
16
|
-
app.new{ |a| a.http_long_poll.timeout = 300 }.http_long_poll.timeout.
|
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
|
data/spec/lib/rack/ping_spec.rb
CHANGED
@@ -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 "
|
15
|
+
it "returns 200" do
|
16
16
|
ahead path
|
17
|
-
last_response.status.
|
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 "
|
23
|
+
it "returns 500" do
|
24
24
|
ahead path
|
25
|
-
last_response.status.
|
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 "
|
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.
|
17
|
+
expect(last_response.status).to eql(202)
|
18
18
|
end
|
19
19
|
|
20
|
-
it "
|
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'].
|
23
|
+
expect(last_response.headers['Content-Length']).to eql('0')
|
24
24
|
end
|
25
25
|
|
26
|
-
it "
|
26
|
+
it "parses Cache-Control max-age" do
|
27
27
|
body = "howdy dude!"
|
28
28
|
ttl = '92'
|
29
29
|
|
data/spec/lib/server/app_spec.rb
CHANGED
@@ -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 "
|
12
|
-
app.new{ |a| a.consumer.http_long_poll.timeout = 300 }.consumer.http_long_poll.timeout.
|
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,
|
8
|
-
let(:subscriber) { Firehose::Server::Subscriber.new(
|
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
|
-
|
13
|
-
|
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.
|
16
|
-
msg
|
17
|
-
|
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 "
|
26
|
-
|
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.
|
31
|
-
msg
|
32
|
-
|
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 "
|
41
|
-
|
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.
|
46
|
-
msg
|
47
|
-
seq
|
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 "
|
56
|
-
|
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.
|
61
|
-
msg
|
62
|
-
seq
|
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 "
|
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.
|
76
|
-
msg
|
77
|
-
seq
|
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 "
|
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.
|
92
|
-
msg
|
93
|
-
seq
|
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 "
|
104
|
-
|
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.
|
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.
|
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 "
|
122
|
-
|
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.
|
127
|
-
msg
|
128
|
-
seq
|
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
|