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.
Files changed (50) hide show
  1. checksums.yaml +5 -5
  2. data/bin/pusher-fake +5 -1
  3. data/lib/pusher-fake.rb +2 -2
  4. data/lib/pusher-fake/channel.rb +9 -8
  5. data/lib/pusher-fake/channel/presence.rb +2 -0
  6. data/lib/pusher-fake/channel/private.rb +3 -1
  7. data/lib/pusher-fake/channel/public.rb +2 -0
  8. data/lib/pusher-fake/configuration.rb +20 -5
  9. data/lib/pusher-fake/connection.rb +10 -7
  10. data/lib/pusher-fake/server.rb +2 -0
  11. data/lib/pusher-fake/server/application.rb +14 -11
  12. data/lib/pusher-fake/support/base.rb +2 -0
  13. data/lib/pusher-fake/support/cucumber.rb +2 -0
  14. data/lib/pusher-fake/support/rspec.rb +2 -0
  15. data/lib/pusher-fake/webhook.rb +3 -1
  16. data/spec/features/api/channels_spec.rb +2 -0
  17. data/spec/features/api/users_spec.rb +2 -0
  18. data/spec/features/client/connect_spec.rb +2 -0
  19. data/spec/features/client/event_spec.rb +2 -0
  20. data/spec/features/client/presence_spec.rb +2 -0
  21. data/spec/features/client/subscribe_spec.rb +9 -5
  22. data/spec/features/server/event_spec.rb +2 -0
  23. data/spec/features/server/webhooks_spec.rb +25 -13
  24. data/spec/lib/pusher-fake/channel/presence_spec.rb +2 -0
  25. data/spec/lib/pusher-fake/channel/private_spec.rb +4 -2
  26. data/spec/lib/pusher-fake/channel/public_spec.rb +2 -0
  27. data/spec/lib/pusher-fake/channel_spec.rb +5 -3
  28. data/spec/lib/pusher-fake/configuration_spec.rb +36 -7
  29. data/spec/lib/pusher-fake/connection_spec.rb +4 -2
  30. data/spec/lib/pusher-fake/server/application_spec.rb +21 -2
  31. data/spec/lib/pusher-fake/server_spec.rb +2 -0
  32. data/spec/lib/pusher-fake/webhook_spec.rb +3 -1
  33. data/spec/lib/pusher_fake_spec.rb +2 -0
  34. data/spec/spec_helper.rb +3 -1
  35. data/spec/support/application.rb +3 -1
  36. data/spec/support/application/public/javascripts/vendor/polyfill.min.js +1 -0
  37. data/spec/support/application/public/javascripts/vendor/pusher-7.0.0.js +4565 -0
  38. data/spec/support/application/views/index.erb +2 -1
  39. data/spec/support/capybara.rb +3 -0
  40. data/spec/support/coveralls.rb +2 -0
  41. data/spec/support/helpers/connect.rb +2 -0
  42. data/spec/support/helpers/event.rb +2 -0
  43. data/spec/support/helpers/subscription.rb +2 -0
  44. data/spec/support/helpers/user.rb +2 -0
  45. data/spec/support/matchers/have_configuration_option.rb +2 -0
  46. data/spec/support/pusher_fake.rb +2 -0
  47. data/spec/support/webhooks.rb +14 -10
  48. metadata +41 -27
  49. data/lib/pusher-fake/cucumber.rb +0 -4
  50. data/spec/support/application/public/javascripts/vendor/pusher-4.2.1.js +0 -4179
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 6f3eacabe00d510d5ad31f3796c821a0b34de364
4
- data.tar.gz: c144fc964da7577d3244f08f3c36f648302c8e4f
2
+ SHA256:
3
+ metadata.gz: c43e78151b832a81f5a36093a7d0433f5b7d1a9206f9a8afd746c160385ac6e3
4
+ data.tar.gz: 2b2138bcbebf26177a05291ac932a180f29c56c56743f766bfad2e0d593ad9a5
5
5
  SHA512:
6
- metadata.gz: 4dda368dd7c7c488399ce316e7eac7c8e90e0da213c8b96009aef2fc01e997ebfebddd8fd7fe8acae6413953427b31a3d0ee5106e5f0f491f4f4031dc80f7e81
7
- data.tar.gz: 535020b68c34f77dac67828bef623d3ca76f9bb72139bd4290cc644d31e6e380b2248074c502c8f89c46b737e1df74b3b4cc6d29ba24c2667f8366d144c81aa6
6
+ metadata.gz: 012ae6c1cc4ad2a3efdd24031ed3aeaa66df0870811f86493fa3898f03be9ea068d0fe8a3651d0f842d39af704bc3e9ef5013ba28e0ac6499f06f787f965628c
7
+ data.tar.gz: 4dcb8d470769855aedf13d803b428332387679feb9bc9b630023e0de25388db07138f5e9068febdbb2cece782396e7f2bf40b895d077f98042e634a64be0a818
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
- # rubocop:disable Metrics/BlockLength, Metrics/LineLength
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
 
@@ -1,4 +1,4 @@
1
- # rubocop:disable Naming/FileName
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.9.0".freeze
12
+ VERSION = "2.1.0"
13
13
 
14
14
  autoload :Channel, "pusher-fake/channel"
15
15
  autoload :Configuration, "pusher-fake/configuration"
@@ -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
- class << self
9
- # Name matcher for private channels.
10
- PRIVATE_CHANNEL_MATCHER = /\Aprivate-/
10
+ # Prefix for private channels.
11
+ PRIVATE_CHANNEL_PREFIX = "private-"
11
12
 
12
- # Name matcher for presence channels.
13
- PRESENCE_CHANNEL_MATCHER = /\Apresence-/
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
- case name
62
- when PRIVATE_CHANNEL_MATCHER
63
+ if name.start_with?(PRIVATE_CHANNEL_PREFIX)
63
64
  Private
64
- when PRESENCE_CHANNEL_MATCHER
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 presence channel.
@@ -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::SHA256.new
43
+ digest = OpenSSL::Digest.new("SHA256")
42
44
  signature = OpenSSL::HMAC.hexdigest(digest, configuration.secret, data)
43
45
 
44
46
  "#{configuration.key}:#{signature}"
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module PusherFake
2
4
  module Channel
3
5
  # A public channel.
@@ -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
- attr_accessor :app_id
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 = STDOUT.to_io
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: socket_options[:host],
55
- wsPort: socket_options[:port],
56
- cluster: "us-east-1"
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
- # Name matcher for client events.
5
- CLIENT_EVENT_MATCHER = /\Aclient-(.+)\z/
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 =~ CLIENT_EVENT_MATCHER
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
- if event == "pusher:subscribe"
72
+ case event
73
+ when "pusher:subscribe"
71
74
  channel_for(message).add(self, message[:data])
72
- elsif event == "pusher:unsubscribe"
75
+ when "pusher:unsubscribe"
73
76
  channel_for(message).remove(self)
74
- elsif event == "pusher:ping"
77
+ when "pusher:ping"
75
78
  emit("pusher:pong")
76
79
  end
77
80
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module PusherFake
2
4
  # Socket and web server manager.
3
5
  module Server
@@ -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-".freeze
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".freeze
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 # rubocop:disable Lint/RescueWithoutErrorClass
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
- if count && name !~ PRESENCE_PREFIX_MATCHER
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 = PusherFake::Channel.channels[name]
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 count && prefix !~ PRESENCE_PREFIX_MATCHER
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
- private_class_method
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
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  %w(app_id key secret).each do |setting|
2
4
  next unless Pusher.public_send(setting).nil?
3
5
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "pusher-fake/support/base"
2
4
 
3
5
  # Reset channels between scenarios.
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "pusher-fake/support/base"
2
4
 
3
5
  # Reset channels between examples
@@ -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::SHA256.new
31
+ digest = OpenSSL::Digest.new("SHA256")
30
32
  secret = PusherFake.configuration.secret
31
33
 
32
34
  OpenSSL::HMAC.hexdigest(digest, secret, payload)
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "spec_helper"
2
4
 
3
5
  feature "Requesting channel API endpoint" do
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "spec_helper"
2
4
 
3
5
  feature "Requesting user API endpoint" do
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "spec_helper"
2
4
 
3
5
  feature "Client connecting to the server" do
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "spec_helper"
2
4
 
3
5
  feature "Client triggers a client event" do
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "spec_helper"
2
4
 
3
5
  feature "Client on a presence channel" do
@@ -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
- visit "/"
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,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "spec_helper"
2
4
 
3
5
  feature "Server triggers event" do
@@ -1,4 +1,4 @@
1
- # rubocop:disable Style/GlobalVars
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($events).to include_event("channel_occupied", "channel" => channel)
22
+ expect(events).to include_event("channel_occupied", "channel" => channel)
19
23
 
20
24
  subscribe_to_as(channel, other_user)
21
25
 
22
- expect($events.size).to eq(1)
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($events.size).to eq(1)
35
+ expect(events.size).to eq(1)
32
36
 
33
37
  unsubscribe_from_as(channel, other_user)
34
38
 
35
- expect($events).to include_event("channel_vacated", "channel" => channel)
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($events).to include_event(
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($events).to include_event(
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($events).to include_event("member_added",
61
- "channel" => presence_channel,
62
- "user_id" => user_id)
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($events).to include_event("member_added",
67
- "channel" => presence_channel,
68
- "user_id" => user_id(other_user))
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