slangerq 0.6.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,43 @@
1
+ require 'bundler/setup'
2
+
3
+ require 'active_support/json'
4
+ require 'active_support/core_ext/hash'
5
+ require 'eventmachine'
6
+ require 'em-http-request'
7
+ require 'pusher'
8
+ require 'thin'
9
+ require 'slanger_helper_methods'
10
+ require 'have_attributes'
11
+ require 'openssl'
12
+ require 'socket'
13
+ require 'timecop'
14
+ require 'pry'
15
+ require 'webmock/rspec'
16
+ require 'slanger'
17
+
18
+ WebMock.disable!
19
+
20
+ module Slanger; end
21
+
22
+ def errback
23
+ @errback ||= Proc.new { |e| fail 'cannot connect to slanger. your box might be too slow. try increasing sleep value in the before block' }
24
+ end
25
+
26
+ RSpec.configure do |config|
27
+ config.formatter = 'documentation'
28
+ config.color = true
29
+ config.mock_framework = :mocha
30
+ config.order = 'random'
31
+ config.include SlangerHelperMethods
32
+ config.fail_fast = true
33
+ config.after(:each) { stop_slanger if @server_pid }
34
+ config.before :all do
35
+ Pusher.tap do |p|
36
+ p.host = '0.0.0.0'
37
+ p.port = 4567
38
+ p.app_id = 'your-pusher-app-id'
39
+ p.secret = 'your-pusher-secret'
40
+ p.key = '765ec374ae0a69f4ce44'
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,105 @@
1
+ require 'spec_helper'
2
+ require 'slanger'
3
+ require 'oj'
4
+
5
+ def clear_redis_connections
6
+ Slanger::Redis.instance_variables.each do |ivar|
7
+ Slanger::Redis.send :remove_instance_variable, ivar
8
+ end
9
+ end
10
+
11
+ describe 'Slanger::Channel' do
12
+ let(:channel) { Slanger::Channel.create channel_id: 'test' }
13
+
14
+ before(:each) do
15
+ EM::Hiredis.stubs(:connect).returns stub_everything('redis', :pubsub => stub_everything('redis'))
16
+ clear_redis_connections
17
+ end
18
+
19
+ after(:each) do
20
+ clear_redis_connections
21
+ EM::Hiredis.unstub(:connect)
22
+ end
23
+
24
+ describe '#unsubscribe' do
25
+ it 'decrements channel subscribers on Redis' do
26
+ Slanger::Redis.expects(:hincrby).
27
+ with('channel_subscriber_count', channel.channel_id, -1).
28
+ once.returns mock { expects(:callback).once.yields(2) }
29
+
30
+ channel.unsubscribe 1
31
+ end
32
+
33
+ it 'activates a webhook when the last subscriber of a channel unsubscribes' do
34
+ Slanger::Webhook.expects(:post).
35
+ with(name: 'channel_vacated', channel: channel.channel_id).
36
+ once
37
+
38
+ Slanger::Redis.expects(:hincrby).
39
+ with('channel_subscriber_count', channel.channel_id, -1).
40
+ times(3).returns mock {
41
+ expects(:callback).times(3).yields(2).then.yields(1).then.yields(0)
42
+ }
43
+
44
+ 3.times { |i| channel.unsubscribe i + 1 }
45
+ end
46
+ end
47
+
48
+ describe '#subscribe' do
49
+ it 'increments channel subscribers on Redis' do
50
+ Slanger::Redis.expects(:hincrby).
51
+ with('channel_subscriber_count', channel.channel_id, 1).
52
+ once.returns mock { expects(:callback).once.yields(2) }
53
+ channel.subscribe { |m| nil }
54
+ end
55
+
56
+ it 'activates a webhook when the first subscriber of a channel joins' do
57
+ Slanger::Webhook.expects(:post).
58
+ with(name: 'channel_occupied', channel: channel.channel_id).
59
+ once
60
+
61
+ Slanger::Redis.expects(:hincrby).
62
+ with('channel_subscriber_count', channel.channel_id, 1).
63
+ times(3).returns mock {
64
+ expects(:callback).times(3).yields(1).then.yields(2).then.yields(3)
65
+ }
66
+
67
+ 3.times { channel.subscribe { |m| nil } }
68
+ end
69
+ end
70
+
71
+ describe '#dispatch' do
72
+ it 'activates a webhook when client events are received' do
73
+ message = {
74
+ 'event' => 'client-test_event',
75
+ 'channel' => 'private-test_channel',
76
+ 'socket_id' => '8.422225',
77
+ 'data' => { 'key' => 'value' }
78
+ }
79
+
80
+ expected_params = message.merge({
81
+ 'name' => 'client_event',
82
+ 'data' => Oj.dump(message['data'])
83
+ })
84
+
85
+ Slanger::Webhook.expects(:post).
86
+ with(expected_params).
87
+ once
88
+
89
+ channel.dispatch(message, 'private-test_channel')
90
+ end
91
+
92
+ it 'does not activate a webhook when non-client event messages are received' do
93
+ message = {
94
+ 'event' => 'non_client_event',
95
+ 'channel' => 'private-test_channel',
96
+ 'socket_id' => '8.422225',
97
+ 'data' => { 'key' => 'value' }
98
+ }
99
+
100
+ Slanger::Webhook.expects(:post).never
101
+
102
+ channel.dispatch(message, 'private-test_channel')
103
+ end
104
+ end
105
+ end
@@ -0,0 +1,72 @@
1
+ #encoding: utf-8
2
+ require 'spec_helper'
3
+
4
+ describe Slanger::Api::RequestValidation do
5
+ describe '#socket_id' do
6
+ it 'validation' do
7
+ socket_id = "POST\n/apps/99759/events\nauth_key=840543d97de9803651b1&auth_timestamp=123&auth_version=1.0&body_md5=some_md5&dummy="
8
+
9
+ expect {validate(nil) }.not_to raise_error
10
+ expect {validate("1234.123455") }.not_to raise_error
11
+
12
+ expect {validate(socket_id) }.to raise_error Slanger::Api::InvalidRequest
13
+ expect {validate("something 123")}.to raise_error Slanger::Api::InvalidRequest
14
+
15
+ expect {validate("1234.12345 ") }.to raise_error Slanger::Api::InvalidRequest
16
+ expect {validate(" 1234.12345") }.to raise_error Slanger::Api::InvalidRequest
17
+ expect {validate("hello\n1234.123456\nhomakov") }.to raise_error Slanger::Api::InvalidRequest
18
+ end
19
+ end
20
+
21
+ before do
22
+ request = mock("request")
23
+ request.expects(:authenticate).times(0..2)
24
+ Signature::Request.expects(:new).times(0..2).returns request
25
+ end
26
+
27
+ describe "#channels" do
28
+ let(:body) { {socket_id: "1234.5678", channels: channels}.to_json }
29
+
30
+ context "with valid channels" do
31
+ let(:channels) { ["MY_CHANNEL", "presence-abcd", "foo-bar_1234@=,.;", "a"*164] }
32
+
33
+ it "returns an array of valid channel_id values" do
34
+ rv = Slanger::Api::RequestValidation.new(body, {}, "")
35
+
36
+ expect(rv.channels).to eq ["MY_CHANNEL", "presence-abcd", "foo-bar_1234@=,.;", "a"*164]
37
+ end
38
+ end
39
+
40
+ context "with invalid characters" do
41
+ let(:channels) { ["MY_CHANNEL:presence-abcd", "presence-abcd"] }
42
+
43
+ it "rejects invalid channels" do
44
+ expect{ Slanger::Api::RequestValidation.new(body, {}, "")}.to raise_error Slanger::Api::InvalidRequest
45
+ end
46
+ end
47
+
48
+ context "with invalid channel length" do
49
+ let(:channels) { ["a"*165] }
50
+
51
+ it "rejects names longer than 164 characters" do
52
+ expect{ Slanger::Api::RequestValidation.new(body, {}, "")}.to raise_error Slanger::Api::InvalidRequest
53
+ end
54
+ end
55
+ end
56
+
57
+ describe "#socket_id" do
58
+ it do
59
+ rv = Slanger::Api::RequestValidation.new(body("1234.5678"), {}, "")
60
+ expect(rv.socket_id).to eq "1234.5678"
61
+ end
62
+ end
63
+
64
+ def validate(socket_id)
65
+ Slanger::Api::RequestValidation.new(body(socket_id), {}, "").socket_id
66
+ end
67
+
68
+ def body(socket_id)
69
+ {socket_id: socket_id}.to_json
70
+ end
71
+ end
72
+
@@ -0,0 +1,43 @@
1
+ require 'spec_helper'
2
+ require 'lib/slanger/webhook'
3
+
4
+ describe 'Slanger::Webhook' do
5
+
6
+ around do |example|
7
+ Slanger::Config.load webhook_url: 'https://example.com/pusher',
8
+ app_key: 'PUSHER_APP_KEY', secret: 'secret'
9
+ WebMock.enable!
10
+ Timecop.freeze(Time.now) { example.run }
11
+ WebMock.disable!
12
+ Slanger::Config.load webhook_url: nil
13
+ end
14
+
15
+ describe '.post' do
16
+ it 'make a POST request to the endpoint' do
17
+ payload = {
18
+ time_ms: Time.now.strftime('%s%L'),
19
+ events: [{ name: 'channel_occupied', channel: 'test channel' }]
20
+ }.to_json
21
+
22
+ digest = OpenSSL::Digest::SHA256.new
23
+ hmac = OpenSSL::HMAC.hexdigest(digest, Slanger::Config.secret, payload)
24
+
25
+ stub_request(:post, Slanger::Config.webhook_url).
26
+ with(body: payload, headers: {
27
+ "X-Pusher-Key" => Slanger::Config.app_key,
28
+ "X-Pusher-Signature" => hmac,
29
+ "Content-Type" => 'application/json'
30
+ }).
31
+ to_return(:status => 200, :body => {}.to_json, :headers => {})
32
+
33
+ Slanger::Webhook.post name: 'channel_occupied', channel: 'test channel'
34
+
35
+ expect(WebMock).to have_requested(:post, Slanger::Config.webhook_url).
36
+ with(body: payload, headers: {
37
+ "X-Pusher-Key" => Slanger::Config.app_key,
38
+ "X-Pusher-Signature" => hmac,
39
+ "Content-Type" => 'application/json'
40
+ })
41
+ end
42
+ end
43
+ end
metadata ADDED
@@ -0,0 +1,392 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: slangerq
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.6.1
5
+ platform: ruby
6
+ authors:
7
+ - Stevie Graham
8
+ - Mark Burns
9
+ - KP
10
+ autorequire:
11
+ bindir: bin
12
+ cert_chain: []
13
+ date: 2018-06-05 00:00:00.000000000 Z
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: eventmachine
17
+ requirement: !ruby/object:Gem::Requirement
18
+ requirements:
19
+ - - "~>"
20
+ - !ruby/object:Gem::Version
21
+ version: 1.0.0
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ requirements:
26
+ - - "~>"
27
+ - !ruby/object:Gem::Version
28
+ version: 1.0.0
29
+ - !ruby/object:Gem::Dependency
30
+ name: em-hiredis
31
+ requirement: !ruby/object:Gem::Requirement
32
+ requirements:
33
+ - - "~>"
34
+ - !ruby/object:Gem::Version
35
+ version: 0.2.0
36
+ type: :runtime
37
+ prerelease: false
38
+ version_requirements: !ruby/object:Gem::Requirement
39
+ requirements:
40
+ - - "~>"
41
+ - !ruby/object:Gem::Version
42
+ version: 0.2.0
43
+ - !ruby/object:Gem::Dependency
44
+ name: em-websocket
45
+ requirement: !ruby/object:Gem::Requirement
46
+ requirements:
47
+ - - "~>"
48
+ - !ruby/object:Gem::Version
49
+ version: 0.5.1
50
+ type: :runtime
51
+ prerelease: false
52
+ version_requirements: !ruby/object:Gem::Requirement
53
+ requirements:
54
+ - - "~>"
55
+ - !ruby/object:Gem::Version
56
+ version: 0.5.1
57
+ - !ruby/object:Gem::Dependency
58
+ name: rack
59
+ requirement: !ruby/object:Gem::Requirement
60
+ requirements:
61
+ - - "~>"
62
+ - !ruby/object:Gem::Version
63
+ version: 1.4.5
64
+ type: :runtime
65
+ prerelease: false
66
+ version_requirements: !ruby/object:Gem::Requirement
67
+ requirements:
68
+ - - "~>"
69
+ - !ruby/object:Gem::Version
70
+ version: 1.4.5
71
+ - !ruby/object:Gem::Dependency
72
+ name: rack-fiber_pool
73
+ requirement: !ruby/object:Gem::Requirement
74
+ requirements:
75
+ - - "~>"
76
+ - !ruby/object:Gem::Version
77
+ version: 0.9.2
78
+ type: :runtime
79
+ prerelease: false
80
+ version_requirements: !ruby/object:Gem::Requirement
81
+ requirements:
82
+ - - "~>"
83
+ - !ruby/object:Gem::Version
84
+ version: 0.9.2
85
+ - !ruby/object:Gem::Dependency
86
+ name: signature
87
+ requirement: !ruby/object:Gem::Requirement
88
+ requirements:
89
+ - - "~>"
90
+ - !ruby/object:Gem::Version
91
+ version: 0.1.6
92
+ type: :runtime
93
+ prerelease: false
94
+ version_requirements: !ruby/object:Gem::Requirement
95
+ requirements:
96
+ - - "~>"
97
+ - !ruby/object:Gem::Version
98
+ version: 0.1.6
99
+ - !ruby/object:Gem::Dependency
100
+ name: activesupport
101
+ requirement: !ruby/object:Gem::Requirement
102
+ requirements:
103
+ - - "~>"
104
+ - !ruby/object:Gem::Version
105
+ version: 4.2.1
106
+ type: :runtime
107
+ prerelease: false
108
+ version_requirements: !ruby/object:Gem::Requirement
109
+ requirements:
110
+ - - "~>"
111
+ - !ruby/object:Gem::Version
112
+ version: 4.2.1
113
+ - !ruby/object:Gem::Dependency
114
+ name: sinatra
115
+ requirement: !ruby/object:Gem::Requirement
116
+ requirements:
117
+ - - "~>"
118
+ - !ruby/object:Gem::Version
119
+ version: 1.4.4
120
+ type: :runtime
121
+ prerelease: false
122
+ version_requirements: !ruby/object:Gem::Requirement
123
+ requirements:
124
+ - - "~>"
125
+ - !ruby/object:Gem::Version
126
+ version: 1.4.4
127
+ - !ruby/object:Gem::Dependency
128
+ name: thin
129
+ requirement: !ruby/object:Gem::Requirement
130
+ requirements:
131
+ - - "~>"
132
+ - !ruby/object:Gem::Version
133
+ version: 1.6.0
134
+ type: :runtime
135
+ prerelease: false
136
+ version_requirements: !ruby/object:Gem::Requirement
137
+ requirements:
138
+ - - "~>"
139
+ - !ruby/object:Gem::Version
140
+ version: 1.6.0
141
+ - !ruby/object:Gem::Dependency
142
+ name: em-http-request
143
+ requirement: !ruby/object:Gem::Requirement
144
+ requirements:
145
+ - - "~>"
146
+ - !ruby/object:Gem::Version
147
+ version: 0.3.0
148
+ type: :runtime
149
+ prerelease: false
150
+ version_requirements: !ruby/object:Gem::Requirement
151
+ requirements:
152
+ - - "~>"
153
+ - !ruby/object:Gem::Version
154
+ version: 0.3.0
155
+ - !ruby/object:Gem::Dependency
156
+ name: oj
157
+ requirement: !ruby/object:Gem::Requirement
158
+ requirements:
159
+ - - "~>"
160
+ - !ruby/object:Gem::Version
161
+ version: 3.6.2
162
+ type: :runtime
163
+ prerelease: false
164
+ version_requirements: !ruby/object:Gem::Requirement
165
+ requirements:
166
+ - - "~>"
167
+ - !ruby/object:Gem::Version
168
+ version: 3.6.2
169
+ - !ruby/object:Gem::Dependency
170
+ name: rspec
171
+ requirement: !ruby/object:Gem::Requirement
172
+ requirements:
173
+ - - "~>"
174
+ - !ruby/object:Gem::Version
175
+ version: 3.6.0
176
+ type: :development
177
+ prerelease: false
178
+ version_requirements: !ruby/object:Gem::Requirement
179
+ requirements:
180
+ - - "~>"
181
+ - !ruby/object:Gem::Version
182
+ version: 3.6.0
183
+ - !ruby/object:Gem::Dependency
184
+ name: pusher
185
+ requirement: !ruby/object:Gem::Requirement
186
+ requirements:
187
+ - - "~>"
188
+ - !ruby/object:Gem::Version
189
+ version: 0.14.2
190
+ type: :development
191
+ prerelease: false
192
+ version_requirements: !ruby/object:Gem::Requirement
193
+ requirements:
194
+ - - "~>"
195
+ - !ruby/object:Gem::Version
196
+ version: 0.14.2
197
+ - !ruby/object:Gem::Dependency
198
+ name: haml
199
+ requirement: !ruby/object:Gem::Requirement
200
+ requirements:
201
+ - - "~>"
202
+ - !ruby/object:Gem::Version
203
+ version: 3.1.2
204
+ type: :development
205
+ prerelease: false
206
+ version_requirements: !ruby/object:Gem::Requirement
207
+ requirements:
208
+ - - "~>"
209
+ - !ruby/object:Gem::Version
210
+ version: 3.1.2
211
+ - !ruby/object:Gem::Dependency
212
+ name: timecop
213
+ requirement: !ruby/object:Gem::Requirement
214
+ requirements:
215
+ - - "~>"
216
+ - !ruby/object:Gem::Version
217
+ version: 0.3.5
218
+ type: :development
219
+ prerelease: false
220
+ version_requirements: !ruby/object:Gem::Requirement
221
+ requirements:
222
+ - - "~>"
223
+ - !ruby/object:Gem::Version
224
+ version: 0.3.5
225
+ - !ruby/object:Gem::Dependency
226
+ name: webmock
227
+ requirement: !ruby/object:Gem::Requirement
228
+ requirements:
229
+ - - "~>"
230
+ - !ruby/object:Gem::Version
231
+ version: 1.8.7
232
+ type: :development
233
+ prerelease: false
234
+ version_requirements: !ruby/object:Gem::Requirement
235
+ requirements:
236
+ - - "~>"
237
+ - !ruby/object:Gem::Version
238
+ version: 1.8.7
239
+ - !ruby/object:Gem::Dependency
240
+ name: mocha
241
+ requirement: !ruby/object:Gem::Requirement
242
+ requirements:
243
+ - - "~>"
244
+ - !ruby/object:Gem::Version
245
+ version: 0.13.2
246
+ type: :development
247
+ prerelease: false
248
+ version_requirements: !ruby/object:Gem::Requirement
249
+ requirements:
250
+ - - "~>"
251
+ - !ruby/object:Gem::Version
252
+ version: 0.13.2
253
+ - !ruby/object:Gem::Dependency
254
+ name: pry
255
+ requirement: !ruby/object:Gem::Requirement
256
+ requirements:
257
+ - - "~>"
258
+ - !ruby/object:Gem::Version
259
+ version: 0.10.1
260
+ type: :development
261
+ prerelease: false
262
+ version_requirements: !ruby/object:Gem::Requirement
263
+ requirements:
264
+ - - "~>"
265
+ - !ruby/object:Gem::Version
266
+ version: 0.10.1
267
+ - !ruby/object:Gem::Dependency
268
+ name: pry-byebug
269
+ requirement: !ruby/object:Gem::Requirement
270
+ requirements:
271
+ - - "~>"
272
+ - !ruby/object:Gem::Version
273
+ version: 2.0.0
274
+ type: :development
275
+ prerelease: false
276
+ version_requirements: !ruby/object:Gem::Requirement
277
+ requirements:
278
+ - - "~>"
279
+ - !ruby/object:Gem::Version
280
+ version: 2.0.0
281
+ - !ruby/object:Gem::Dependency
282
+ name: bundler
283
+ requirement: !ruby/object:Gem::Requirement
284
+ requirements:
285
+ - - "~>"
286
+ - !ruby/object:Gem::Version
287
+ version: '1.5'
288
+ type: :development
289
+ prerelease: false
290
+ version_requirements: !ruby/object:Gem::Requirement
291
+ requirements:
292
+ - - "~>"
293
+ - !ruby/object:Gem::Version
294
+ version: '1.5'
295
+ - !ruby/object:Gem::Dependency
296
+ name: rake
297
+ requirement: !ruby/object:Gem::Requirement
298
+ requirements:
299
+ - - "~>"
300
+ - !ruby/object:Gem::Version
301
+ version: 10.4.2
302
+ type: :development
303
+ prerelease: false
304
+ version_requirements: !ruby/object:Gem::Requirement
305
+ requirements:
306
+ - - "~>"
307
+ - !ruby/object:Gem::Version
308
+ version: 10.4.2
309
+ description: A websocket service compatible with Pusher libraries
310
+ email:
311
+ - sjtgraham@mac.com
312
+ - markthedeveloper@gmail.com
313
+ executables:
314
+ - slanger
315
+ extensions: []
316
+ extra_rdoc_files: []
317
+ files:
318
+ - README.md
319
+ - bin/slanger
320
+ - lib/slanger/api.rb
321
+ - lib/slanger/api/event.rb
322
+ - lib/slanger/api/event_publisher.rb
323
+ - lib/slanger/api/request_validation.rb
324
+ - lib/slanger/api/server.rb
325
+ - lib/slanger/channel.rb
326
+ - lib/slanger/config.rb
327
+ - lib/slanger/connection.rb
328
+ - lib/slanger/handler.rb
329
+ - lib/slanger/logger.rb
330
+ - lib/slanger/presence_channel.rb
331
+ - lib/slanger/presence_subscription.rb
332
+ - lib/slanger/private_subscription.rb
333
+ - lib/slanger/redis.rb
334
+ - lib/slanger/service.rb
335
+ - lib/slanger/subscription.rb
336
+ - lib/slanger/version.rb
337
+ - lib/slanger/web_socket_server.rb
338
+ - lib/slanger/webhook.rb
339
+ - slanger.rb
340
+ - spec/have_attributes.rb
341
+ - spec/integration/channel_spec.rb
342
+ - spec/integration/integration_spec.rb
343
+ - spec/integration/presence_channel_spec.rb
344
+ - spec/integration/private_channel_spec.rb
345
+ - spec/integration/replaced_handler_spec.rb
346
+ - spec/integration/ssl_spec.rb
347
+ - spec/server.crt
348
+ - spec/server.key
349
+ - spec/slanger_helper_methods.rb
350
+ - spec/spec_helper.rb
351
+ - spec/unit/channel_spec.rb
352
+ - spec/unit/request_validation_spec.rb
353
+ - spec/unit/webhook_spec.rb
354
+ homepage: https://github.com/stevegraham/slanger
355
+ licenses:
356
+ - MIT
357
+ metadata: {}
358
+ post_install_message:
359
+ rdoc_options: []
360
+ require_paths:
361
+ - "."
362
+ required_ruby_version: !ruby/object:Gem::Requirement
363
+ requirements:
364
+ - - ">="
365
+ - !ruby/object:Gem::Version
366
+ version: 2.0.0
367
+ required_rubygems_version: !ruby/object:Gem::Requirement
368
+ requirements:
369
+ - - ">="
370
+ - !ruby/object:Gem::Version
371
+ version: '0'
372
+ requirements: []
373
+ rubyforge_project:
374
+ rubygems_version: 2.7.6
375
+ signing_key:
376
+ specification_version: 4
377
+ summary: A websocket service compatible with Pusher libraries
378
+ test_files:
379
+ - spec/have_attributes.rb
380
+ - spec/integration/channel_spec.rb
381
+ - spec/integration/integration_spec.rb
382
+ - spec/integration/presence_channel_spec.rb
383
+ - spec/integration/private_channel_spec.rb
384
+ - spec/integration/replaced_handler_spec.rb
385
+ - spec/integration/ssl_spec.rb
386
+ - spec/server.crt
387
+ - spec/server.key
388
+ - spec/slanger_helper_methods.rb
389
+ - spec/spec_helper.rb
390
+ - spec/unit/channel_spec.rb
391
+ - spec/unit/request_validation_spec.rb
392
+ - spec/unit/webhook_spec.rb