firehose 1.3.6 → 2.1.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 (104) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +31 -10
  3. data/{LICENSE.txt → LICENSE} +6 -6
  4. data/README.md +379 -187
  5. data/app/assets/javascripts/firehose.js +258 -0
  6. data/config/importmap.rb +1 -0
  7. data/db/migrate/20260214185244_create_firehose_events.rb +23 -0
  8. data/lib/commands/firehose_command.rb +119 -0
  9. data/lib/firehose/channel.rb +40 -0
  10. data/lib/firehose/cleanup_job.rb +21 -0
  11. data/lib/firehose/engine.rb +36 -0
  12. data/lib/firehose/helper.rb +40 -0
  13. data/lib/firehose/models/channel.rb +12 -0
  14. data/lib/firehose/models/message.rb +11 -0
  15. data/lib/firehose/queue.rb +44 -0
  16. data/lib/firehose/server.rb +930 -17
  17. data/lib/firehose/sse.rb +187 -0
  18. data/lib/firehose/stream.rb +32 -0
  19. data/lib/firehose/streamable.rb +25 -0
  20. data/lib/firehose/websocket.rb +242 -0
  21. data/lib/firehose.rb +67 -15
  22. data/lib/generators/firehose/install/install_generator.rb +43 -0
  23. data/lib/generators/firehose/install/templates/firehose_controller.rb +16 -0
  24. metadata +39 -366
  25. data/.codeclimate.yml +0 -29
  26. data/.dockerignore +0 -2
  27. data/.env.sample +0 -17
  28. data/.gitignore +0 -14
  29. data/.rspec +0 -1
  30. data/.rubocop.yml +0 -1156
  31. data/.ruby-version +0 -1
  32. data/.travis.yml +0 -12
  33. data/Dockerfile +0 -11
  34. data/Gemfile +0 -6
  35. data/Procfile +0 -1
  36. data/Procfile.dev +0 -3
  37. data/Rakefile +0 -64
  38. data/bin/firehose +0 -9
  39. data/coffeelint.json +0 -129
  40. data/config/rainbows.rb +0 -17
  41. data/docker-compose.yml +0 -17
  42. data/firehose.gemspec +0 -45
  43. data/karma.config.coffee +0 -89
  44. data/lib/assets/javascripts/firehose/base.js.coffee +0 -4
  45. data/lib/assets/javascripts/firehose/consumer.js.coffee +0 -86
  46. data/lib/assets/javascripts/firehose/core.js.coffee +0 -6
  47. data/lib/assets/javascripts/firehose/long_poll.js.coffee +0 -210
  48. data/lib/assets/javascripts/firehose/multiplexed_consumer.js.coffee +0 -74
  49. data/lib/assets/javascripts/firehose/transport.js.coffee +0 -44
  50. data/lib/assets/javascripts/firehose/version.js.coffee.erb +0 -2
  51. data/lib/assets/javascripts/firehose/web_socket.js.coffee +0 -148
  52. data/lib/assets/javascripts/firehose.js.coffee +0 -2
  53. data/lib/firehose/assets.rb +0 -56
  54. data/lib/firehose/cli.rb +0 -79
  55. data/lib/firehose/client/consumer.rb +0 -94
  56. data/lib/firehose/client/producer.rb +0 -114
  57. data/lib/firehose/client.rb +0 -10
  58. data/lib/firehose/logging.rb +0 -32
  59. data/lib/firehose/rack/app.rb +0 -49
  60. data/lib/firehose/rack/consumer/http_long_poll.rb +0 -159
  61. data/lib/firehose/rack/consumer/web_socket.rb +0 -200
  62. data/lib/firehose/rack/consumer.rb +0 -78
  63. data/lib/firehose/rack/ping.rb +0 -88
  64. data/lib/firehose/rack/publisher.rb +0 -59
  65. data/lib/firehose/rack.rb +0 -32
  66. data/lib/firehose/rails.rb +0 -7
  67. data/lib/firehose/server/app.rb +0 -52
  68. data/lib/firehose/server/channel.rb +0 -72
  69. data/lib/firehose/server/message_buffer.rb +0 -59
  70. data/lib/firehose/server/publisher.rb +0 -133
  71. data/lib/firehose/server/redis.rb +0 -32
  72. data/lib/firehose/server/subscriber.rb +0 -51
  73. data/lib/firehose/version.rb +0 -4
  74. data/package.json +0 -17
  75. data/spec/integrations/integration_test_helper.rb +0 -22
  76. data/spec/integrations/rainbows_spec.rb +0 -7
  77. data/spec/integrations/shared_examples.rb +0 -202
  78. data/spec/integrations/thin_spec.rb +0 -7
  79. data/spec/javascripts/firehose/base_spec.coffee +0 -2
  80. data/spec/javascripts/firehose/consumer_spec.coffee +0 -45
  81. data/spec/javascripts/firehose/long_poll_spec.coffee +0 -44
  82. data/spec/javascripts/firehose/multiplexed_consumer_spec.coffee +0 -72
  83. data/spec/javascripts/firehose/transport_spec.coffee +0 -30
  84. data/spec/javascripts/firehose/websocket_spec.coffee +0 -27
  85. data/spec/javascripts/helpers/spec_helper.js +0 -31
  86. data/spec/javascripts/support/jquery-1.11.1.js +0 -10308
  87. data/spec/javascripts/support/json2.js +0 -487
  88. data/spec/javascripts/support/sinon-1.7.3.js +0 -4290
  89. data/spec/javascripts/support/spec_helper.coffee +0 -3
  90. data/spec/lib/assets_spec.rb +0 -35
  91. data/spec/lib/client/consumer_spec.rb +0 -13
  92. data/spec/lib/client/producer_spec.rb +0 -71
  93. data/spec/lib/firehose_spec.rb +0 -7
  94. data/spec/lib/rack/consumer/http_long_poll_spec.rb +0 -30
  95. data/spec/lib/rack/consumer_spec.rb +0 -19
  96. data/spec/lib/rack/ping_spec.rb +0 -28
  97. data/spec/lib/rack/publisher_spec.rb +0 -34
  98. data/spec/lib/server/app_spec.rb +0 -15
  99. data/spec/lib/server/channel_spec.rb +0 -178
  100. data/spec/lib/server/message_buffer_spec.rb +0 -148
  101. data/spec/lib/server/publisher_spec.rb +0 -91
  102. data/spec/lib/server/redis_spec.rb +0 -13
  103. data/spec/lib/server/subscriber_spec.rb +0 -70
  104. data/spec/spec_helper.rb +0 -84
@@ -1,3 +0,0 @@
1
- #= require ./jquery-1.11.1
2
- #= require ./json2
3
- #= require ./sinon-1.7.3
@@ -1,35 +0,0 @@
1
- require 'spec_helper'
2
- require 'sprockets'
3
-
4
- describe Firehose::Assets do
5
- describe ".path" do
6
- it "has root path" do
7
- expect(Firehose::Assets.path('poop')).to eql(File.expand_path('../../../lib/assets/poop', __FILE__))
8
- end
9
-
10
- it "accepts folders" do
11
- expect(Firehose::Assets.path('poop')).to eql(File.join(Firehose::Assets.path, 'poop'))
12
- end
13
- end
14
-
15
- describe "Sprockets.configure" do
16
- it "configures environment" do
17
- env = Firehose::Assets::Sprockets.configure Sprockets::Environment.new
18
- expect(env.paths).to include(Firehose::Assets.path('javascripts'))
19
- end
20
- end
21
-
22
- describe "Sprockets.javascript" do
23
- let(:js_spot_checks) do
24
- [
25
- "Firehose.Transport",
26
- "Firehose.version",
27
- "global.Firehose || (global.Firehose = {})",
28
- "window.Firehose || (window.Firehose = {})"
29
- ]
30
- end
31
- it "compiles javascript" do
32
- expect(Firehose::Assets::Sprockets.javascript).to include(*js_spot_checks)
33
- end
34
- end
35
- end
@@ -1,13 +0,0 @@
1
- require 'spec_helper'
2
-
3
- describe Firehose::Client::Consumer::WebSocket do
4
- context "transport" do
5
- # Transport for Firehose::Client::App class is tested via the spec/integrations suite.
6
- end
7
- end
8
-
9
- describe Firehose::Client::Consumer::HttpLongPoll do
10
- context "transport" do
11
- # Transport for Firehose::Client::App class is tested via the spec/integrations suite.
12
- end
13
- end
@@ -1,71 +0,0 @@
1
- require 'spec_helper'
2
-
3
- describe Firehose::Client::Producer::Http do
4
- let(:channel) { "/channel-awesome" }
5
- let(:url) { "#{Firehose::URI}#{channel}"}
6
- let(:publish_stub) { stub_request(:put, url) }
7
- let(:message) { "hey dude" }
8
-
9
- before(:all) do
10
- Firehose::Client::Producer::Http.adapter = :net_http
11
- end
12
-
13
- after(:all) do
14
- Firehose::Client::Producer::Http.adapter = nil
15
- end
16
-
17
- it "publishes message to channel" do
18
- publish_stub.to_return(:body => "", :status => 202)
19
-
20
- Firehose::Client::Producer::Http.new.publish(message).to(channel)
21
- expect(WebMock).to have_requested(:put, url).with { |req| req.body == message }
22
- end
23
-
24
- context 'prefix is specified in URI' do
25
- let(:firehose_uri) {"#{Firehose::URI}/prefix"}
26
- let(:url) { "#{firehose_uri}#{channel}"}
27
-
28
- it "publishes message to channel" do
29
- publish_stub.to_return(:body => "", :status => 202)
30
-
31
- Firehose::Client::Producer::Http.new(firehose_uri).publish(message).to(channel)
32
- expect(WebMock).to have_requested(:put, url).with { |req| req.body == message }
33
- end
34
- end
35
-
36
- it "publishes message to channel with expiry headers" do
37
- publish_stub.to_return(:body => "", :status => 202)
38
- ttl = 20
39
-
40
- Firehose::Client::Producer::Http.new.publish(message).to(channel, :ttl => ttl)
41
- expect(WebMock).to have_requested(:put, url).with { |req| req.body == message and req.headers['Cache-Control'] == "max-age=#{ttl}" }
42
- end
43
-
44
- describe "connection error handling" do
45
- it "raises PublishError if not 201" do
46
- publish_stub.to_return(:body => "", :status => 500)
47
-
48
- expect {
49
- Firehose::Client::Producer::Http.new.publish(message).to(channel)
50
- }.to raise_exception(Firehose::Client::Producer::Http::PublishError)
51
- end
52
-
53
- it "uses .error_handler if not 201" do
54
- publish_stub.to_return(:body => "", :status => 500)
55
-
56
- producer = Firehose::Client::Producer::Http.new
57
- producer.on_error do |e|
58
- expect(e.message).to match(/could not publish.+to/i)
59
- end
60
- producer.publish(message).to(channel)
61
- end
62
-
63
- it "raises TimeoutError if timed out" do
64
- publish_stub.to_timeout
65
-
66
- expect {
67
- Firehose::Client::Producer::Http.new.publish(message).to(channel)
68
- }.to raise_exception(Firehose::Client::Producer::Http::TimeoutError)
69
- end
70
- end
71
- end
@@ -1,7 +0,0 @@
1
- require 'spec_helper'
2
-
3
- describe Firehose do
4
- it "has 7474 for default port" do
5
- expect(Firehose::URI.to_s).to eql('//0.0.0.0:7474')
6
- end
7
- end
@@ -1,30 +0,0 @@
1
- require 'spec_helper'
2
- require 'rack/test'
3
- require 'async_rack_test'
4
-
5
- describe Firehose::Rack::Consumer::HttpLongPoll do
6
- include AsyncRackTest::Methods
7
- let(:app) { Firehose::Rack::Consumer::HttpLongPoll.new }
8
-
9
- context "transport" do
10
- # Transport for Firehose::Rack::App class is tested via the spec/integrations suite.
11
- end
12
- context "configuration" do
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")
28
- end
29
- end
30
- end
@@ -1,19 +0,0 @@
1
- require 'spec_helper'
2
- require 'rack/test'
3
-
4
- describe Firehose::Rack::Consumer, :type => :request do
5
- include Rack::Test::Methods
6
- let(:app) { Firehose::Rack::Consumer.new }
7
- let(:path) { "/test/path/#{Time.now.to_i}" }
8
-
9
- it "has Content-Length on OPTIONS request"
10
- it "has Content-Length on GET request"
11
-
12
- context "configuration" do
13
- let(:app) { Firehose::Rack::Consumer }
14
-
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
- end
18
- end
19
- end
@@ -1,28 +0,0 @@
1
- require 'spec_helper'
2
- require 'rack/test'
3
- require 'async_rack_test'
4
-
5
- describe Firehose::Rack::Ping, :type => :request do
6
- include AsyncRackTest::Methods
7
- let(:app) { Firehose::Rack::Ping.new dummy_redis }
8
- let(:path) { "/test/path/#{Time.now.to_i}" }
9
- let(:deferrable) { EM::DefaultDeferrable.new }
10
- let(:dummy_redis) { double 'redis', :set => deferrable, :get => deferrable, :expire => deferrable }
11
-
12
- context 'redis is available' do
13
- before { deferrable.succeed Firehose::Rack::Ping::PingCheck::TEST_VALUE }
14
-
15
- it "returns 200" do
16
- ahead path
17
- expect(last_response.status).to eql(200)
18
- end
19
- end
20
-
21
- context 'redis is not available' do
22
- before { deferrable.fail 'some error' }
23
- it "returns 500" do
24
- ahead path
25
- expect(last_response.status).to eql(500)
26
- end
27
- end
28
- end
@@ -1,34 +0,0 @@
1
- require 'spec_helper'
2
- require 'rack/test'
3
- require 'async_rack_test'
4
-
5
- describe Firehose::Rack::Publisher, :type => :request do
6
- include AsyncRackTest::Methods
7
- let(:app) { Firehose::Rack::Publisher.new }
8
- let(:path) { "/test/path/#{Time.now.to_i}" }
9
- let(:deferrable) { EM::DefaultDeferrable.new }
10
-
11
- context 'publishing is successful' do
12
- before { deferrable.succeed }
13
-
14
- it "returns 202" do
15
- app.stub(:publisher => double('publisher', :publish => deferrable))
16
- aput path, :body => "some nice little message"
17
- expect(last_response.status).to eql(202)
18
- end
19
-
20
- it "has Content-Length of zero" do
21
- app.stub(:publisher => double('publisher', :publish => deferrable))
22
- aput path, :body => "some nice little message"
23
- expect(last_response.headers['Content-Length']).to eql('0')
24
- end
25
-
26
- it "parses Cache-Control max-age" do
27
- body = "howdy dude!"
28
- ttl = '92'
29
-
30
- Firehose::Server::Publisher.any_instance.stub(:publish).with(path, body, :ttl => ttl).and_return(deferrable)
31
- aput path, body, 'HTTP_CACHE_CONTROL' => 'max-age=92'
32
- end
33
- end
34
- end
@@ -1,15 +0,0 @@
1
- require 'spec_helper'
2
-
3
- describe Firehose::Rack::App do
4
- context "transport" do
5
- # Transport for Firehose::Rack::App class is tested via the spec/integrations suite.
6
- end
7
-
8
- context "configuration" do
9
- let(:app) { Firehose::Rack::App }
10
-
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
- end
14
- end
15
- end
@@ -1,178 +0,0 @@
1
- require 'spec_helper'
2
-
3
- describe Firehose::Server::Channel do
4
- include EM::TestHelper
5
-
6
- let(:channel_key) { '/bears/are/mean' }
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
- let(:message) { 'Raaaarrrrrr!!!!' }
10
- let(:publisher) { Firehose::Server::Publisher.new }
11
-
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
19
- em do
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)
24
- em.next_tick { em.stop }
25
- end
26
-
27
- publisher.publish(channel_key, message)
28
- end
29
- end
30
-
31
- it "returns the latest message and sequence if no sequence is given" do
32
- push_message
33
-
34
- em do
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)
39
-
40
- # This must happen _after_ the callback runs in order to pass consistently.
41
- em.next_tick { em.stop }
42
- end
43
- end
44
- end
45
-
46
- it "waits for message if most recent sequence is given" do
47
- push_message
48
-
49
- em 3 do
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)
55
- em.next_tick { em.stop }
56
- end.errback
57
-
58
- publisher.publish(channel_key, message)
59
- end
60
- end
61
-
62
- it "waits for message if a future sequence is given" do
63
- push_message
64
-
65
- em 3 do
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)
71
- em.next_tick { em.stop }
72
- end.errback
73
-
74
- publisher.publish(channel_key, message)
75
- end
76
- end
77
-
78
- it "immediatly gets a message if message sequence is behind and in list" do
79
- messages = %w[a b c d e]
80
-
81
- em 3 do
82
- publish_messages(messages) do
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)
88
-
89
- # This must happen _after_ the callback runs in order to pass consistently.
90
- em.next_tick { em.stop }
91
- end
92
- end
93
- end
94
- end
95
-
96
- it "gets current message if sequence is really far behind in list" do
97
- messages = ('aa'..'zz').to_a
98
-
99
- em 3 do
100
- publish_messages(messages) do
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)
106
-
107
- # This must happen _after_ the callback runs in order to pass consistently.
108
- em.next_tick { em.stop }
109
- end
110
- end
111
- end
112
- end
113
-
114
- context "a timeout is set" do
115
- it "times out if message isn't published in time" do
116
- push_message
117
-
118
- em 3 do
119
- channel.next_messages(100, :timeout => 1).callback do |messages|
120
- msg = messages.first.payload
121
- seq = messages.first.sequence
122
- raise 'test failed'
123
- end.errback do |e|
124
- expect(e).to eql(:timeout)
125
- em.next_tick { em.stop }
126
- end
127
-
128
- EM::add_timer(2) do
129
- publisher.publish(channel_key, message)
130
- end
131
- end
132
- end
133
-
134
- it "does not timeout if message is published in time" do
135
- push_message
136
-
137
- em 3 do
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)
143
- EM::add_timer(1) do
144
- em.stop
145
- end
146
- end.errback do |e|
147
- raise 'test failed'
148
- end
149
-
150
- d.should_not_receive(:fail)
151
-
152
- EM::add_timer(1) do
153
- publisher.publish(channel_key, message)
154
- end
155
- end
156
- end
157
- end
158
- end
159
-
160
-
161
- # Publishes the given messages and executes the given block when finished.
162
- def publish_messages(all_messages)
163
- publish = Proc.new do |messages_to_publish|
164
- msg = messages_to_publish.shift
165
- publisher.publish(channel_key, msg).callback do
166
- if messages_to_publish.empty?
167
- # Publishing is done, proceed with the test
168
- yield
169
- else
170
- # Publish the next message
171
- publish.call messages_to_publish
172
- end
173
- end
174
- end
175
-
176
- publish.call all_messages.dup
177
- end
178
- end
@@ -1,148 +0,0 @@
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
@@ -1,91 +0,0 @@
1
- require 'spec_helper'
2
-
3
- describe Firehose::Server::Publisher do
4
- include EM::TestHelper
5
-
6
- let(:publisher) { Firehose::Server::Publisher.new }
7
- let(:channel_key) { "/firehose/publisher/test/#{Time.now.to_i}" }
8
- let(:message) { "howdy friends!" }
9
-
10
- it "has 1 day TTL" do
11
- expect(Firehose::Server::Publisher::TTL).to eql(86400)
12
- end
13
-
14
- describe "#publish" do
15
- it "publishes message change" do
16
- em do
17
- hiredis = Firehose::Server.redis.connection.pubsub
18
- hiredis.subscribe "firehose:channel_updates"
19
- hiredis.on(:message) {|_, msg|
20
- expect(msg).to eql("#{channel_key}\n1\n#{message}")
21
- em.next_tick { em.stop }
22
- }
23
- Firehose::Server::Publisher.new.publish channel_key, message
24
- end
25
- end
26
-
27
- "\"'\r\t\n!@\#$%^&*()[]\v\f\a\b\e{}/=?+\\|".each_char do |char|
28
- it "publishes messages with the '#{char.inspect}' character" do
29
- msg = [char, message, char].join
30
- em 1 do
31
- Firehose::Server::Publisher.new.publish(channel_key, msg).callback { em.stop }
32
- end
33
- expect(redis_exec('lpop', "firehose:#{channel_key}:list")).to eql(msg)
34
- end
35
- end
36
-
37
- it "adds message to list" do
38
- em do
39
- Firehose::Server::Publisher.new.publish(channel_key, message).callback { em.stop }
40
- end
41
- expect(redis_exec('lpop', "firehose:#{channel_key}:list")).to eql(message)
42
- end
43
-
44
- it "limits list to DEFAULT_SIZE messages" do
45
- em do
46
- Firehose::Server::MessageBuffer::DEFAULT_SIZE.times do |n|
47
- publisher.publish(channel_key, message)
48
- end
49
- publisher.publish(channel_key, message).callback { em.stop }
50
- end
51
- expect(redis_exec('llen', "firehose:#{channel_key}:list")).to eql(Firehose::Server::MessageBuffer::DEFAULT_SIZE)
52
- end
53
-
54
- it "limits message list to a custom buffer size" do
55
- buffer_size = rand(100)
56
- em do
57
- Firehose::Server::MessageBuffer::DEFAULT_SIZE.times do |n|
58
- publisher.publish(channel_key, message)
59
- end
60
- publisher.publish(channel_key, message, buffer_size: buffer_size).callback { em.stop }
61
- end
62
- redis_exec('llen', "firehose:#{channel_key}:list").should == buffer_size
63
- end
64
-
65
- it "increments sequence" do
66
- sequence_key = "firehose:#{channel_key}:sequence"
67
-
68
- @done_counter = 0
69
- expect(redis_exec('get', sequence_key)).to be_nil
70
- em do
71
- publisher.publish(channel_key, message).callback { @done_counter += 1; em.next_tick { em.stop } if @done_counter > 1 }
72
- publisher.publish(channel_key, message).callback { @done_counter += 1; em.next_tick { em.stop } if @done_counter > 1 }
73
- end
74
- expect(redis_exec('get', sequence_key).to_i).to eql(2)
75
- end
76
-
77
- it "sets expiry on sequence and list keys" do
78
- ttl = 78 # Why 78? Why not!
79
-
80
- em do
81
- publisher.publish(channel_key, message, :ttl => 78).callback do
82
- # Allow for 1 second of slippage/delay
83
- expect(redis_exec('TTL', "firehose:#{channel_key}:sequence")).to be > (ttl- 1)
84
- expect(redis_exec('TTL', "firehose:#{channel_key}:list")).to be > (ttl - 1)
85
- em.stop
86
- end
87
- end
88
- end
89
-
90
- end
91
- end