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