pusher-fake 1.9.0 → 2.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/bin/pusher-fake +5 -1
- data/lib/pusher-fake.rb +2 -2
- data/lib/pusher-fake/channel.rb +9 -8
- data/lib/pusher-fake/channel/presence.rb +2 -0
- data/lib/pusher-fake/channel/private.rb +3 -1
- data/lib/pusher-fake/channel/public.rb +2 -0
- data/lib/pusher-fake/configuration.rb +20 -5
- data/lib/pusher-fake/connection.rb +10 -7
- data/lib/pusher-fake/server.rb +2 -0
- data/lib/pusher-fake/server/application.rb +14 -11
- data/lib/pusher-fake/support/base.rb +2 -0
- data/lib/pusher-fake/support/cucumber.rb +2 -0
- data/lib/pusher-fake/support/rspec.rb +2 -0
- data/lib/pusher-fake/webhook.rb +3 -1
- data/spec/features/api/channels_spec.rb +2 -0
- data/spec/features/api/users_spec.rb +2 -0
- data/spec/features/client/connect_spec.rb +2 -0
- data/spec/features/client/event_spec.rb +2 -0
- data/spec/features/client/presence_spec.rb +2 -0
- data/spec/features/client/subscribe_spec.rb +9 -5
- data/spec/features/server/event_spec.rb +2 -0
- data/spec/features/server/webhooks_spec.rb +25 -13
- data/spec/lib/pusher-fake/channel/presence_spec.rb +2 -0
- data/spec/lib/pusher-fake/channel/private_spec.rb +4 -2
- data/spec/lib/pusher-fake/channel/public_spec.rb +2 -0
- data/spec/lib/pusher-fake/channel_spec.rb +5 -3
- data/spec/lib/pusher-fake/configuration_spec.rb +36 -7
- data/spec/lib/pusher-fake/connection_spec.rb +4 -2
- data/spec/lib/pusher-fake/server/application_spec.rb +21 -2
- data/spec/lib/pusher-fake/server_spec.rb +2 -0
- data/spec/lib/pusher-fake/webhook_spec.rb +3 -1
- data/spec/lib/pusher_fake_spec.rb +2 -0
- data/spec/spec_helper.rb +3 -1
- data/spec/support/application.rb +3 -1
- data/spec/support/application/public/javascripts/vendor/polyfill.min.js +1 -0
- data/spec/support/application/public/javascripts/vendor/pusher-7.0.0.js +4565 -0
- data/spec/support/application/views/index.erb +2 -1
- data/spec/support/capybara.rb +3 -0
- data/spec/support/coveralls.rb +2 -0
- data/spec/support/helpers/connect.rb +2 -0
- data/spec/support/helpers/event.rb +2 -0
- data/spec/support/helpers/subscription.rb +2 -0
- data/spec/support/helpers/user.rb +2 -0
- data/spec/support/matchers/have_configuration_option.rb +2 -0
- data/spec/support/pusher_fake.rb +2 -0
- data/spec/support/webhooks.rb +14 -10
- metadata +41 -27
- data/lib/pusher-fake/cucumber.rb +0 -4
- data/spec/support/application/public/javascripts/vendor/pusher-4.2.1.js +0 -4179
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: c43e78151b832a81f5a36093a7d0433f5b7d1a9206f9a8afd746c160385ac6e3
|
4
|
+
data.tar.gz: 2b2138bcbebf26177a05291ac932a180f29c56c56743f766bfad2e0d593ad9a5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 012ae6c1cc4ad2a3efdd24031ed3aeaa66df0870811f86493fa3898f03be9ea068d0fe8a3651d0f842d39af704bc3e9ef5013ba28e0ac6499f06f787f965628c
|
7
|
+
data.tar.gz: 4dcb8d470769855aedf13d803b428332387679feb9bc9b630023e0de25388db07138f5e9068febdbb2cece782396e7f2bf40b895d077f98042e634a64be0a818
|
data/bin/pusher-fake
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
|
3
|
-
#
|
3
|
+
# frozen_string_literal: true
|
4
4
|
|
5
5
|
require "optparse"
|
6
6
|
require "pusher"
|
@@ -42,6 +42,10 @@ PusherFake.configure do |configuration|
|
|
42
42
|
options.on("--web-port PORT", Integer, "Use PORT for the web server") do |port|
|
43
43
|
configuration.web_options[:port] = port
|
44
44
|
end
|
45
|
+
|
46
|
+
options.on("--webhooks URLS", Array, "Use URLS for the webhooks") do |hooks|
|
47
|
+
configuration.webhooks = hooks
|
48
|
+
end
|
45
49
|
end.parse!
|
46
50
|
end
|
47
51
|
|
data/lib/pusher-fake.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
#
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require "em-http-request"
|
4
4
|
require "em-websocket"
|
@@ -9,7 +9,7 @@ require "thin"
|
|
9
9
|
# A Pusher fake.
|
10
10
|
module PusherFake
|
11
11
|
# The current version string.
|
12
|
-
VERSION = "1.
|
12
|
+
VERSION = "2.1.0"
|
13
13
|
|
14
14
|
autoload :Channel, "pusher-fake/channel"
|
15
15
|
autoload :Configuration, "pusher-fake/configuration"
|
data/lib/pusher-fake/channel.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module PusherFake
|
2
4
|
# Channel creation and management.
|
3
5
|
module Channel
|
@@ -5,13 +7,13 @@ module PusherFake
|
|
5
7
|
autoload :Private, "pusher-fake/channel/private"
|
6
8
|
autoload :Presence, "pusher-fake/channel/presence"
|
7
9
|
|
8
|
-
|
9
|
-
|
10
|
-
PRIVATE_CHANNEL_MATCHER = /\Aprivate-/
|
10
|
+
# Prefix for private channels.
|
11
|
+
PRIVATE_CHANNEL_PREFIX = "private-"
|
11
12
|
|
12
|
-
|
13
|
-
|
13
|
+
# Prefix for presence channels.
|
14
|
+
PRESENCE_CHANNEL_PREFIX = "presence-"
|
14
15
|
|
16
|
+
class << self
|
15
17
|
# @return [Hash] Cache of existing channels.
|
16
18
|
attr_writer :channels
|
17
19
|
|
@@ -58,10 +60,9 @@ module PusherFake
|
|
58
60
|
# @param [String] name The name of the channel.
|
59
61
|
# @return [Class] The class to use for the channel.
|
60
62
|
def class_for(name)
|
61
|
-
|
62
|
-
when PRIVATE_CHANNEL_MATCHER
|
63
|
+
if name.start_with?(PRIVATE_CHANNEL_PREFIX)
|
63
64
|
Private
|
64
|
-
|
65
|
+
elsif name.start_with?(PRESENCE_CHANNEL_PREFIX)
|
65
66
|
Presence
|
66
67
|
else
|
67
68
|
Public
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module PusherFake
|
2
4
|
module Channel
|
3
5
|
# A private channel.
|
@@ -38,7 +40,7 @@ module PusherFake
|
|
38
40
|
configuration = PusherFake.configuration
|
39
41
|
|
40
42
|
data = [id, name, data].compact.map(&:to_s).join(":")
|
41
|
-
digest = OpenSSL::Digest
|
43
|
+
digest = OpenSSL::Digest.new("SHA256")
|
42
44
|
signature = OpenSSL::HMAC.hexdigest(digest, configuration.secret, data)
|
43
45
|
|
44
46
|
"#{configuration.key}:#{signature}"
|
@@ -1,8 +1,13 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module PusherFake
|
2
4
|
# Configuration class.
|
3
5
|
class Configuration
|
4
6
|
# @return [String] The Pusher Applicaiton ID. (Defaults to +PUSHER_APP_ID+.)
|
5
|
-
|
7
|
+
attr_reader :app_id
|
8
|
+
|
9
|
+
# @return [Boolean] Disable the client statistics. (Defaults to +true+.)
|
10
|
+
attr_accessor :disable_stats
|
6
11
|
|
7
12
|
# @return [String] The Pusher API key. (Defaults to +PUSHER_API_KEY+.)
|
8
13
|
attr_accessor :key
|
@@ -34,14 +39,22 @@ module PusherFake
|
|
34
39
|
reset!
|
35
40
|
end
|
36
41
|
|
42
|
+
# Assign the application ID, ensuring it's a string.
|
43
|
+
#
|
44
|
+
# @params [Integer|String] id The application ID.
|
45
|
+
def app_id=(id)
|
46
|
+
@app_id = id.to_s
|
47
|
+
end
|
48
|
+
|
37
49
|
def reset!
|
38
50
|
self.app_id = "PUSHER_APP_ID"
|
39
51
|
self.key = "PUSHER_API_KEY"
|
40
|
-
self.logger =
|
52
|
+
self.logger = $stdout.to_io
|
41
53
|
self.secret = "PUSHER_API_SECRET"
|
42
54
|
self.verbose = false
|
43
55
|
self.webhooks = []
|
44
56
|
|
57
|
+
self.disable_stats = true
|
45
58
|
self.socket_options = { host: "127.0.0.1", port: available_port }
|
46
59
|
self.web_options = { host: "127.0.0.1", port: available_port }
|
47
60
|
end
|
@@ -51,9 +64,11 @@ module PusherFake
|
|
51
64
|
# @param [Hash] options Custom options for Pusher client.
|
52
65
|
def to_options(options = {})
|
53
66
|
options.merge(
|
54
|
-
wsHost:
|
55
|
-
wsPort:
|
56
|
-
cluster:
|
67
|
+
wsHost: socket_options[:host],
|
68
|
+
wsPort: socket_options[:port],
|
69
|
+
cluster: "us-east-1",
|
70
|
+
forceTLS: false,
|
71
|
+
disableStats: disable_stats
|
57
72
|
)
|
58
73
|
end
|
59
74
|
|
@@ -1,8 +1,10 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module PusherFake
|
2
4
|
# A client connection.
|
3
5
|
class Connection
|
4
|
-
#
|
5
|
-
|
6
|
+
# Prefix for client events.
|
7
|
+
CLIENT_EVENT_PREFIX = "client-"
|
6
8
|
|
7
9
|
# @return [EventMachine::WebSocket::Connection] Socket for the connection.
|
8
10
|
attr_reader :socket
|
@@ -19,7 +21,7 @@ module PusherFake
|
|
19
21
|
# @return [Integer] The object ID of the socket.
|
20
22
|
def id
|
21
23
|
parts = socket.object_id.to_s.split("")
|
22
|
-
parts = parts.each_slice(parts.length / 2).to_a
|
24
|
+
parts = parts.each_slice((parts.length / 2.0).ceil).to_a
|
23
25
|
|
24
26
|
[parts.first.join(""), parts.last.join("")].join(".")
|
25
27
|
end
|
@@ -53,7 +55,7 @@ module PusherFake
|
|
53
55
|
|
54
56
|
PusherFake.log("RECV #{id}: #{message}")
|
55
57
|
|
56
|
-
if event
|
58
|
+
if event.start_with?(CLIENT_EVENT_PREFIX)
|
57
59
|
process_trigger(event, message)
|
58
60
|
else
|
59
61
|
process_event(event, message)
|
@@ -67,11 +69,12 @@ module PusherFake
|
|
67
69
|
end
|
68
70
|
|
69
71
|
def process_event(event, message)
|
70
|
-
|
72
|
+
case event
|
73
|
+
when "pusher:subscribe"
|
71
74
|
channel_for(message).add(self, message[:data])
|
72
|
-
|
75
|
+
when "pusher:unsubscribe"
|
73
76
|
channel_for(message).remove(self)
|
74
|
-
|
77
|
+
when "pusher:ping"
|
75
78
|
emit("pusher:pong")
|
76
79
|
end
|
77
80
|
end
|
data/lib/pusher-fake/server.rb
CHANGED
@@ -1,15 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module PusherFake
|
2
4
|
module Server
|
3
5
|
# The fake web application.
|
4
6
|
class Application
|
5
7
|
CHANNEL_FILTER_ERROR = "user_count may only be requested for presence " \
|
6
8
|
"channels - please supply filter_by_prefix " \
|
7
|
-
"begining with presence-"
|
9
|
+
"begining with presence-"
|
8
10
|
|
9
11
|
CHANNEL_USER_COUNT_ERROR = "Cannot retrieve the user count unless the " \
|
10
|
-
"channel is a presence channel"
|
11
|
-
|
12
|
-
PRESENCE_PREFIX_MATCHER = /\Apresence-/
|
12
|
+
"channel is a presence channel"
|
13
13
|
|
14
14
|
REQUEST_PATHS = {
|
15
15
|
%r{\A/apps/:id/batch_events\z} => :batch_events,
|
@@ -28,7 +28,7 @@ module PusherFake
|
|
28
28
|
response = response_for(request)
|
29
29
|
|
30
30
|
Rack::Response.new(MultiJson.dump(response)).finish
|
31
|
-
rescue => error
|
31
|
+
rescue StandardError => error
|
32
32
|
Rack::Response.new(error.message, 400).finish
|
33
33
|
end
|
34
34
|
|
@@ -68,11 +68,9 @@ module PusherFake
|
|
68
68
|
def self.channel(name, request)
|
69
69
|
count = request.params["info"].to_s.split(",").include?("user_count")
|
70
70
|
|
71
|
-
|
72
|
-
raise CHANNEL_USER_COUNT_ERROR
|
73
|
-
end
|
71
|
+
raise CHANNEL_USER_COUNT_ERROR if invalid_channel_to_count?(name, count)
|
74
72
|
|
75
|
-
channel =
|
73
|
+
channel = Channel.channels[name]
|
76
74
|
connections = channel ? channel.connections : []
|
77
75
|
|
78
76
|
result = { occupied: connections.any? }
|
@@ -92,7 +90,7 @@ module PusherFake
|
|
92
90
|
count = request.params["info"].to_s.split(",").include?("user_count")
|
93
91
|
prefix = request.params["filter_by_prefix"].to_s
|
94
92
|
|
95
|
-
raise CHANNEL_FILTER_ERROR if
|
93
|
+
raise CHANNEL_FILTER_ERROR if invalid_channel_to_count?(prefix, count)
|
96
94
|
|
97
95
|
PusherFake::Channel
|
98
96
|
.channels
|
@@ -143,7 +141,11 @@ module PusherFake
|
|
143
141
|
{ users: users || [] }
|
144
142
|
end
|
145
143
|
|
146
|
-
|
144
|
+
# @return [Boolean]
|
145
|
+
def self.invalid_channel_to_count?(name, includes_count)
|
146
|
+
includes_count && !name.start_with?(Channel::PRESENCE_CHANNEL_PREFIX)
|
147
|
+
end
|
148
|
+
private_class_method :invalid_channel_to_count?
|
147
149
|
|
148
150
|
# Emit an event with data to the requested channel(s).
|
149
151
|
#
|
@@ -158,6 +160,7 @@ module PusherFake
|
|
158
160
|
channel.emit(event["name"], data, socket_id: event["socket_id"])
|
159
161
|
end
|
160
162
|
end
|
163
|
+
private_class_method :send_event
|
161
164
|
# rubocop:enable Style/RescueModifier
|
162
165
|
end
|
163
166
|
end
|
data/lib/pusher-fake/webhook.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module PusherFake
|
2
4
|
# Webhook triggering.
|
3
5
|
class Webhook
|
@@ -26,7 +28,7 @@ module PusherFake
|
|
26
28
|
end
|
27
29
|
|
28
30
|
def signature_for(payload)
|
29
|
-
digest = OpenSSL::Digest
|
31
|
+
digest = OpenSSL::Digest.new("SHA256")
|
30
32
|
secret = PusherFake.configuration.secret
|
31
33
|
|
32
34
|
OpenSSL::HMAC.hexdigest(digest, secret, payload)
|
@@ -1,12 +1,10 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require "spec_helper"
|
2
4
|
|
3
5
|
feature "Client subscribing to a channel" do
|
4
6
|
before do
|
5
|
-
|
6
|
-
|
7
|
-
# rubocop:disable RSpec/ExpectInHook
|
8
|
-
expect(page).to have_content("Client connected.")
|
9
|
-
# rubocop:enable RSpec/ExpectInHook
|
7
|
+
connect
|
10
8
|
end
|
11
9
|
|
12
10
|
scenario "successfully subscribes to a channel" do
|
@@ -57,6 +55,12 @@ feature "Client subscribing to a channel" do
|
|
57
55
|
page.execute_script("Helpers.subscribe(#{MultiJson.dump(channel)})")
|
58
56
|
end
|
59
57
|
|
58
|
+
def connect
|
59
|
+
visit "/"
|
60
|
+
|
61
|
+
expect(page).to have_content("Client connected.")
|
62
|
+
end
|
63
|
+
|
60
64
|
def override_socket_id(value)
|
61
65
|
page.execute_script(
|
62
66
|
"Pusher.instance.connection.socket_id = #{MultiJson.dump(value)};"
|
@@ -1,4 +1,4 @@
|
|
1
|
-
#
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require "spec_helper"
|
4
4
|
|
@@ -8,6 +8,10 @@ feature "Receiving event webhooks" do
|
|
8
8
|
let(:presence_channel) { "presence-room-1" }
|
9
9
|
|
10
10
|
before do
|
11
|
+
events.clear
|
12
|
+
|
13
|
+
PusherFake.configuration.webhooks = ["http://127.0.0.1:8082"]
|
14
|
+
|
11
15
|
connect
|
12
16
|
connect_as(other_user)
|
13
17
|
end
|
@@ -15,11 +19,11 @@ feature "Receiving event webhooks" do
|
|
15
19
|
scenario "occupying a channel" do
|
16
20
|
subscribe_to(channel)
|
17
21
|
|
18
|
-
expect(
|
22
|
+
expect(events).to include_event("channel_occupied", "channel" => channel)
|
19
23
|
|
20
24
|
subscribe_to_as(channel, other_user)
|
21
25
|
|
22
|
-
expect(
|
26
|
+
expect(events.size).to eq(1)
|
23
27
|
end
|
24
28
|
|
25
29
|
scenario "vacating a channel" do
|
@@ -28,24 +32,24 @@ feature "Receiving event webhooks" do
|
|
28
32
|
|
29
33
|
unsubscribe_from(channel)
|
30
34
|
|
31
|
-
expect(
|
35
|
+
expect(events.size).to eq(1)
|
32
36
|
|
33
37
|
unsubscribe_from_as(channel, other_user)
|
34
38
|
|
35
|
-
expect(
|
39
|
+
expect(events).to include_event("channel_vacated", "channel" => channel)
|
36
40
|
end
|
37
41
|
|
38
42
|
scenario "subscribing to a presence channel" do
|
39
43
|
subscribe_to(presence_channel)
|
40
44
|
|
41
|
-
expect(
|
45
|
+
expect(events).to include_event(
|
42
46
|
"member_added",
|
43
47
|
"channel" => presence_channel, "user_id" => user_id
|
44
48
|
)
|
45
49
|
|
46
50
|
subscribe_to_as(presence_channel, other_user)
|
47
51
|
|
48
|
-
expect(
|
52
|
+
expect(events).to include_event(
|
49
53
|
"member_added",
|
50
54
|
"channel" => presence_channel, "user_id" => user_id(other_user)
|
51
55
|
)
|
@@ -57,19 +61,27 @@ feature "Receiving event webhooks" do
|
|
57
61
|
|
58
62
|
unsubscribe_from(presence_channel)
|
59
63
|
|
60
|
-
expect(
|
61
|
-
|
62
|
-
|
64
|
+
expect(events).to include_event("member_added",
|
65
|
+
"channel" => presence_channel,
|
66
|
+
"user_id" => user_id)
|
63
67
|
|
64
68
|
unsubscribe_from_as(presence_channel, other_user)
|
65
69
|
|
66
|
-
expect(
|
67
|
-
|
68
|
-
|
70
|
+
expect(events).to include_event("member_added",
|
71
|
+
"channel" => presence_channel,
|
72
|
+
"user_id" => user_id(other_user))
|
69
73
|
end
|
70
74
|
|
71
75
|
protected
|
72
76
|
|
77
|
+
def events
|
78
|
+
sleep(1)
|
79
|
+
|
80
|
+
WebhookHelper.mutex.synchronize do
|
81
|
+
WebhookHelper.events
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
73
85
|
def include_event(event, options = {})
|
74
86
|
include(options.merge("name" => event))
|
75
87
|
end
|