pusher-fake 0.14.0 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -13,7 +13,7 @@
13
13
  <ul></ul>
14
14
  </section>
15
15
 
16
- <script src="/javascripts/vendor/pusher-2.1.6.js"></script>
16
+ <script src="/javascripts/vendor/pusher-2.2.1.js"></script>
17
17
  <script>
18
18
  window.addEventListener("DOMContentLoaded", function() {
19
19
  // Create the client instance using the PusherFake server.
@@ -22,15 +22,9 @@ Thread.new do
22
22
  EventMachine.run do
23
23
  Thin::Logging.silent = true
24
24
  Thin::Server.start("0.0.0.0", 8082, WebhookEndpoint)
25
- Thread.current[:ready] = true
26
25
  end
27
26
  end.tap do |thread|
28
27
  at_exit { thread.exit }
29
-
30
- # Wait for the webhook endpoint server to start.
31
- Timeout::timeout(5) do
32
- sleep(0.05) until thread[:ready]
33
- end
34
28
  end
35
29
 
36
- PusherFake.configuration.webhooks = ["http://localhost:8082"]
30
+ PusherFake.configuration.webhooks = ["http://127.0.0.1:8082"]
@@ -6,7 +6,7 @@ require "thin"
6
6
 
7
7
  module PusherFake
8
8
  # The current version string.
9
- VERSION = "0.14.0"
9
+ VERSION = "1.0.0"
10
10
 
11
11
  autoload :Channel, "pusher-fake/channel"
12
12
  autoload :Configuration, "pusher-fake/configuration"
@@ -22,7 +22,7 @@ module PusherFake
22
22
  # @option options [String] :auth The authentication string.
23
23
  # @return [Boolean] +true+ if authorized, +false+ otherwise.
24
24
  def authorized?(connection, options)
25
- authentication_for(connection.socket.object_id, options[:channel_data]) == options[:auth]
25
+ authentication_for(connection.id, options[:channel_data]) == options[:auth]
26
26
  end
27
27
 
28
28
  # Generate an authentication string from the channel based on the
@@ -29,7 +29,7 @@ module PusherFake
29
29
  # @param [Hash] data The event data.
30
30
  def emit(event, data, options = {})
31
31
  connections.each do |connection|
32
- unless connection.socket.object_id == options[:socket_id]
32
+ unless connection.id == options[:socket_id]
33
33
  connection.emit(event, data, name)
34
34
  end
35
35
  end
@@ -13,21 +13,30 @@ module PusherFake
13
13
  @socket = socket
14
14
  end
15
15
 
16
+ # The ID of the connection.
17
+ #
18
+ # @return [Integer] The object ID of the socket.
19
+ def id
20
+ socket.object_id.to_s
21
+ end
22
+
16
23
  # Emit an event to the connection.
17
24
  #
18
25
  # @param [String] event The event name.
19
26
  # @param [Hash] data The event data.
20
27
  # @param [String] channel The channel name.
21
28
  def emit(event, data = {}, channel = nil)
22
- message = { event: event, data: data }
29
+ message = { event: event, data: MultiJson.dump(data) }
23
30
  message[:channel] = channel if channel
24
31
 
32
+ PusherFake.log("SEND #{id}: #{message}")
33
+
25
34
  socket.send(MultiJson.dump(message))
26
35
  end
27
36
 
28
37
  # Notify the Pusher client that a connection has been established.
29
38
  def establish
30
- emit("pusher:connection_established", socket_id: socket.object_id, activity_timeout: 120)
39
+ emit("pusher:connection_established", socket_id: id, activity_timeout: 120)
31
40
  end
32
41
 
33
42
  # Process an event.
@@ -35,9 +44,12 @@ module PusherFake
35
44
  # @param [String] data The event data as JSON.
36
45
  def process(data)
37
46
  message = MultiJson.load(data, symbolize_keys: true)
47
+
48
+ PusherFake.log("RECV #{id}: #{message}")
49
+
38
50
  data = message[:data]
39
51
  event = message[:event]
40
- name = message[:channel] || data.delete(:channel)
52
+ name = message[:channel] || data[:channel]
41
53
  channel = Channel.factory(name) if name
42
54
 
43
55
  case event
@@ -49,9 +61,27 @@ module PusherFake
49
61
  emit("pusher:pong")
50
62
  when CLIENT_EVENT_MATCHER
51
63
  if channel.is_a?(Channel::Private) && channel.includes?(self)
52
- channel.emit(event, data, socket_id: socket.object_id)
64
+ channel.emit(event, data, socket_id: id)
65
+
66
+ trigger(channel, id, event, data)
53
67
  end
54
68
  end
55
69
  end
70
+
71
+ private
72
+
73
+ def trigger(channel, id, event, data)
74
+ Thread.new do
75
+ hook = {
76
+ event: event,
77
+ channel: channel.name,
78
+ socket_id: id
79
+ }
80
+ hook[:data] = MultiJson.dump(data) if data
81
+ hook[:user_id] = channel.members[self][:user_id] if channel.is_a?(Channel::Presence)
82
+
83
+ channel.trigger("client_event", hook)
84
+ end
85
+ end
56
86
  end
57
87
  end
@@ -39,8 +39,15 @@ module PusherFake
39
39
  # @return [Hash] An empty hash.
40
40
  def self.events(request)
41
41
  event = MultiJson.load(request.body.read)
42
- event["channels"].each do |channel|
43
- Channel.factory(channel).emit(event["name"], event["data"], socket_id: event["socket_id"])
42
+ data = begin
43
+ MultiJson.load(event["data"])
44
+ rescue MultiJson::LoadError
45
+ event["data"]
46
+ end
47
+
48
+ event["channels"].each do |channel_name|
49
+ channel = Channel.factory(channel_name)
50
+ channel.emit(event["name"], data, socket_id: event["socket_id"])
44
51
  end
45
52
 
46
53
  {}
@@ -107,7 +114,7 @@ module PusherFake
107
114
 
108
115
  if channel
109
116
  users = channel.connections.map do |connection|
110
- { id: connection.object_id }
117
+ { id: connection.id }
111
118
  end
112
119
  end
113
120
 
@@ -7,6 +7,7 @@ module PusherFake
7
7
  time_ms: Time.now.to_i
8
8
  )
9
9
 
10
+ PusherFake.log("HOOK: #{payload}")
10
11
  PusherFake.configuration.webhooks.each do |url|
11
12
  http = EventMachine::HttpRequest.new(url)
12
13
  http.post(body: payload, head: headers_for(payload))
@@ -133,8 +133,7 @@ end
133
133
  describe PusherFake::Channel::Private, "#authorized?" do
134
134
  let(:data) { { auth: authentication, channel_data: channel_data } }
135
135
  let(:name) { "private-channel" }
136
- let(:socket) { stub }
137
- let(:connection) { stub(socket: socket) }
136
+ let(:connection) { stub(id: "1") }
138
137
  let(:channel_data) { "{}" }
139
138
  let(:authentication) { "authentication" }
140
139
 
@@ -144,10 +143,10 @@ describe PusherFake::Channel::Private, "#authorized?" do
144
143
  subject.stubs(:authentication_for)
145
144
  end
146
145
 
147
- it "generates authentication for the connection socket ID" do
146
+ it "generates authentication for the connection ID" do
148
147
  subject.authorized?(connection, data)
149
148
 
150
- expect(subject).to have_received(:authentication_for).with(socket.object_id, channel_data)
149
+ expect(subject).to have_received(:authentication_for).with(connection.id, channel_data)
151
150
  end
152
151
 
153
152
  it "returns true if the authentication matches" do
@@ -55,11 +55,9 @@ describe PusherFake::Channel, "#emit" do
55
55
  let(:data) { stub }
56
56
  let(:name) { "name" }
57
57
  let(:event) { "event" }
58
- let(:socket_1) { stub }
59
- let(:socket_2) { stub }
60
58
  let(:connections) { [connection_1, connection_2] }
61
- let(:connection_1) { stub(emit: nil, socket: socket_1) }
62
- let(:connection_2) { stub(emit: nil, socket: socket_2) }
59
+ let(:connection_1) { stub(emit: nil, id: "1") }
60
+ let(:connection_2) { stub(emit: nil, id: "2") }
63
61
 
64
62
  subject { PusherFake::Channel::Public.new(name) }
65
63
 
@@ -74,8 +72,8 @@ describe PusherFake::Channel, "#emit" do
74
72
  expect(connection_2).to have_received(:emit).with(event, data, name)
75
73
  end
76
74
 
77
- it "ignores connection if socket_id matches the connections socket object_id" do
78
- subject.emit(event, data, socket_id: socket_2.object_id)
75
+ it "ignores connection if socket_id matches the connections ID" do
76
+ subject.emit(event, data, socket_id: connection_2.id)
79
77
 
80
78
  expect(connection_1).to have_received(:emit).with(event, data, name)
81
79
  expect(connection_2).to have_received(:emit).never
@@ -1,5 +1,28 @@
1
1
  require "spec_helper"
2
2
 
3
+ shared_examples_for "#process" do
4
+ let(:json) { stub }
5
+
6
+ subject { PusherFake::Connection.new(stub) }
7
+
8
+ before do
9
+ PusherFake.stubs(:log)
10
+ MultiJson.stubs(load: message)
11
+ end
12
+
13
+ it "parses the JSON data" do
14
+ subject.process(json)
15
+
16
+ expect(MultiJson).to have_received(:load).with(json, symbolize_keys: true)
17
+ end
18
+
19
+ it "logs receiving the event" do
20
+ subject.process(json)
21
+
22
+ expect(PusherFake).to have_received(:log).with("RECV #{subject.id}: #{message}")
23
+ end
24
+ end
25
+
3
26
  describe PusherFake::Connection do
4
27
  let(:socket) { stub }
5
28
 
@@ -18,11 +41,15 @@ describe PusherFake::Connection, "#emit" do
18
41
  let(:event) { "name" }
19
42
  let(:socket) { stub(:send) }
20
43
  let(:channel) { "channel" }
21
- let(:message) { { event: event, data: data } }
44
+ let(:message) { { event: event, data: MultiJson.dump(data) } }
22
45
  let(:channel_json) { MultiJson.dump(message.merge(channel: channel)) }
23
46
 
24
47
  subject { PusherFake::Connection.new(socket) }
25
48
 
49
+ before do
50
+ PusherFake.stubs(:log)
51
+ end
52
+
26
53
  it "sends the event to the socket as JSON" do
27
54
  subject.emit(event, data)
28
55
 
@@ -34,6 +61,12 @@ describe PusherFake::Connection, "#emit" do
34
61
 
35
62
  expect(socket).to have_received(:send).with(channel_json)
36
63
  end
64
+
65
+ it "logs sending the event" do
66
+ subject.emit(event, data)
67
+
68
+ expect(PusherFake).to have_received(:log).with("SEND #{subject.id}: #{message}")
69
+ end
37
70
  end
38
71
 
39
72
  describe PusherFake::Connection, "#establish" do
@@ -45,204 +78,225 @@ describe PusherFake::Connection, "#establish" do
45
78
  subject.stubs(:emit)
46
79
  end
47
80
 
48
- it "emits the connection established event with the socket ID" do
81
+ it "emits the connection established event with the connection ID" do
49
82
  subject.establish
50
83
 
51
84
  expect(subject).to have_received(:emit)
52
- .with("pusher:connection_established", socket_id: socket.object_id, activity_timeout: 120)
85
+ .with("pusher:connection_established", socket_id: subject.id, activity_timeout: 120)
53
86
  end
54
87
  end
55
88
 
56
- describe PusherFake::Connection, "#process, with a subscribe event" do
57
- let(:data) { { channel: name, auth: "auth" } }
58
- let(:json) { stub }
59
- let(:name) { "channel" }
60
- let(:channel) { stub(add: nil) }
61
- let(:message) { { event: "pusher:subscribe", data: data } }
89
+ describe PusherFake::Connection, "#id" do
90
+ let(:id) { socket.object_id.to_s }
91
+ let(:socket) { stub }
62
92
 
63
- subject { PusherFake::Connection.new(stub) }
93
+ subject { PusherFake::Connection.new(socket) }
64
94
 
65
- before do
66
- MultiJson.stubs(load: message)
67
- PusherFake::Channel.stubs(factory: channel)
95
+ it "returns the object ID of the socket" do
96
+ expect(subject.id).to eq(id)
68
97
  end
98
+ end
69
99
 
70
- it "parses the JSON data" do
71
- subject.process(json)
100
+ describe PusherFake::Connection, "#process, with a subscribe event" do
101
+ it_should_behave_like "#process" do
102
+ let(:data) { { channel: name, auth: "auth" } }
103
+ let(:name) { "channel" }
104
+ let(:channel) { stub(add: nil) }
105
+ let(:message) { { event: "pusher:subscribe", data: data } }
72
106
 
73
- expect(MultiJson).to have_received(:load).with(json, symbolize_keys: true)
74
- end
107
+ before do
108
+ PusherFake::Channel.stubs(factory: channel)
109
+ end
75
110
 
76
- it "creates a channel from the event data" do
77
- subject.process(json)
111
+ it "creates a channel from the event data" do
112
+ subject.process(json)
78
113
 
79
- expect(PusherFake::Channel).to have_received(:factory).with(name)
80
- end
114
+ expect(PusherFake::Channel).to have_received(:factory).with(name)
115
+ end
81
116
 
82
- it "attempts to add the connection to the channel" do
83
- subject.process(json)
117
+ it "attempts to add the connection to the channel" do
118
+ subject.process(json)
84
119
 
85
- expect(channel).to have_received(:add).with(subject, data)
120
+ expect(channel).to have_received(:add).with(subject, data)
121
+ end
86
122
  end
87
123
  end
88
124
 
89
125
  describe PusherFake::Connection, "#process, with an unsubscribe event" do
90
- let(:json) { stub }
91
- let(:name) { "channel" }
92
- let(:channel) { stub(remove: nil) }
93
- let(:message) { { event: "pusher:unsubscribe", channel: name } }
126
+ it_should_behave_like "#process" do
127
+ let(:name) { "channel" }
128
+ let(:channel) { stub(remove: nil) }
129
+ let(:message) { { event: "pusher:unsubscribe", channel: name } }
94
130
 
95
- subject { PusherFake::Connection.new(stub) }
131
+ before do
132
+ PusherFake::Channel.stubs(factory: channel)
133
+ end
96
134
 
97
- before do
98
- MultiJson.stubs(load: message)
99
- PusherFake::Channel.stubs(factory: channel)
100
- end
135
+ it "creates a channel from the event data" do
136
+ subject.process(json)
101
137
 
102
- it "parses the JSON data" do
103
- subject.process(json)
138
+ expect(PusherFake::Channel).to have_received(:factory).with(name)
139
+ end
104
140
 
105
- expect(MultiJson).to have_received(:load).with(json, symbolize_keys: true)
106
- end
107
-
108
- it "creates a channel from the event data" do
109
- subject.process(json)
110
-
111
- expect(PusherFake::Channel).to have_received(:factory).with(name)
112
- end
113
-
114
- it "removes the connection from the channel" do
115
- subject.process(json)
141
+ it "removes the connection from the channel" do
142
+ subject.process(json)
116
143
 
117
- expect(channel).to have_received(:remove).with(subject)
144
+ expect(channel).to have_received(:remove).with(subject)
145
+ end
118
146
  end
119
147
  end
120
148
 
121
149
  describe PusherFake::Connection, "#process, with a ping event" do
122
- let(:json) { stub }
123
- let(:message) { { event: "pusher:ping", data: {} } }
150
+ it_should_behave_like "#process" do
151
+ let(:message) { { event: "pusher:ping", data: {} } }
124
152
 
125
- subject { PusherFake::Connection.new(stub) }
153
+ before do
154
+ subject.stubs(:emit)
155
+ end
126
156
 
127
- before do
128
- MultiJson.stubs(load: message)
129
- PusherFake::Channel.stubs(:factory)
130
- subject.stubs(:emit)
131
- end
157
+ it "does not create a channel" do
158
+ subject.process(json)
132
159
 
133
- it "parses the JSON data" do
134
- subject.process(json)
160
+ expect(PusherFake::Channel).to have_received(:factory).never
161
+ end
135
162
 
136
- expect(MultiJson).to have_received(:load).with(json, symbolize_keys: true)
163
+ it "emits a pong event" do
164
+ subject.process(json)
165
+
166
+ expect(subject).to have_received(:emit).with("pusher:pong")
167
+ end
137
168
  end
169
+ end
138
170
 
139
- it "does not create a channel" do
140
- subject.process(json)
171
+ describe PusherFake::Connection, "#process, with a client event" do
172
+ it_should_behave_like "#process" do
173
+ let(:data) { {} }
174
+ let(:name) { "channel" }
175
+ let(:event) { "client-hello-world" }
176
+ let(:channel) { stub(emit: nil, includes?: nil, is_a?: true) }
177
+ let(:message) { { event: event, data: data, channel: name } }
141
178
 
142
- expect(PusherFake::Channel).to have_received(:factory).never
143
- end
179
+ before do
180
+ subject.stubs(:trigger)
181
+ PusherFake::Channel.stubs(factory: channel)
182
+ end
144
183
 
145
- it "emits a pong event" do
146
- subject.process(json)
184
+ it "creates a channel from the event data" do
185
+ subject.process(json)
147
186
 
148
- expect(subject).to have_received(:emit).with("pusher:pong")
149
- end
150
- end
187
+ expect(PusherFake::Channel).to have_received(:factory).with(name)
188
+ end
151
189
 
152
- describe PusherFake::Connection, "#process, with a client event" do
153
- let(:data) { {} }
154
- let(:json) { stub }
155
- let(:name) { "channel" }
156
- let(:event) { "client-hello-world" }
157
- let(:channel) { stub(emit: nil, includes?: nil, is_a?: true) }
158
- let(:message) { { event: event, data: data, channel: name } }
190
+ it "ensures the channel is private" do
191
+ subject.process(json)
159
192
 
160
- subject { PusherFake::Connection.new(stub) }
193
+ expect(channel).to have_received(:is_a?).with(PusherFake::Channel::Private)
194
+ end
161
195
 
162
- before do
163
- MultiJson.stubs(load: message)
164
- PusherFake::Channel.stubs(factory: channel)
165
- end
196
+ it "checks if the connection is in the channel" do
197
+ subject.process(json)
166
198
 
167
- it "parses the JSON data" do
168
- subject.process(json)
199
+ expect(channel).to have_received(:includes?).with(subject)
200
+ end
169
201
 
170
- expect(MultiJson).to have_received(:load).with(json, symbolize_keys: true)
171
- end
202
+ it "emits the event to the channel when the connection is in the channel" do
203
+ channel.stubs(includes?: true)
172
204
 
173
- it "creates a channel from the event data" do
174
- subject.process(json)
205
+ subject.process(json)
175
206
 
176
- expect(PusherFake::Channel).to have_received(:factory).with(name)
177
- end
207
+ expect(channel).to have_received(:emit).with(event, data, socket_id: subject.id)
208
+ end
178
209
 
179
- it "ensures the channel is private" do
180
- subject.process(json)
210
+ it "does not emit the event to the channel when the channel is not private" do
211
+ channel.stubs(includes?: true, is_a?: false)
181
212
 
182
- expect(channel).to have_received(:is_a?).with(PusherFake::Channel::Private)
183
- end
213
+ subject.process(json)
184
214
 
185
- it "checks if the connection is in the channel" do
186
- subject.process(json)
215
+ expect(channel).to have_received(:emit).never
216
+ end
217
+
218
+ it "does not emit the event to the channel when the connection is not in the channel" do
219
+ channel.stubs(includes?: false)
187
220
 
188
- expect(channel).to have_received(:includes?).with(subject)
221
+ subject.process(json)
222
+
223
+ expect(channel).to have_received(:emit).never
224
+ end
189
225
  end
226
+ end
190
227
 
191
- it "emits the event to the channel when the connection is in the channel" do
192
- channel.stubs(includes?: true)
228
+ describe PusherFake::Connection, "#process, with a client event trigger a webhook" do
229
+ it_should_behave_like "#process" do
230
+ let(:data) { { example: "data" } }
231
+ let(:name) { "channel" }
232
+ let(:event) { "client-hello-world" }
233
+ let(:user_id) { 1 }
234
+ let(:channel) { stub(name: name, emit: nil, includes?: nil) }
235
+ let(:message) { { event: event, channel: name } }
236
+ let(:options) { { channel: name, event: event, socket_id: subject.id } }
193
237
 
194
- subject.process(json)
238
+ before do
239
+ channel.stubs(:trigger)
240
+ channel.stubs(:includes?).with(subject).returns(true)
241
+ channel.stubs(:is_a?).with(PusherFake::Channel::Private).returns(true)
242
+ channel.stubs(:is_a?).with(PusherFake::Channel::Presence).returns(false)
195
243
 
196
- expect(channel).to have_received(:emit).with(event, data, socket_id: subject.socket.object_id)
197
- end
244
+ # NOTE: Hack to avoid race condition in unit tests.
245
+ Thread.stubs(:new).yields
198
246
 
199
- it "does not emit the event to the channel when the channel is not private" do
200
- channel.stubs(includes?: true, is_a?: false)
247
+ PusherFake::Channel.stubs(factory: channel)
248
+ end
201
249
 
202
- subject.process(json)
250
+ it "triggers the client event webhook" do
251
+ subject.process(json)
203
252
 
204
- expect(channel).to have_received(:emit).never
205
- end
253
+ expect(channel).to have_received(:trigger)
254
+ .with("client_event", options).once
255
+ end
206
256
 
207
- it "does not emit the event to the channel when the connection is not in the channel" do
208
- channel.stubs(includes?: false)
257
+ it "includes data in event when present" do
258
+ message[:data] = data
209
259
 
210
- subject.process(json)
260
+ subject.process(json)
211
261
 
212
- expect(channel).to have_received(:emit).never
213
- end
214
- end
262
+ expect(channel).to have_received(:trigger).
263
+ with("client_event", options.merge(data: MultiJson.dump(data))).once
264
+ end
215
265
 
216
- describe PusherFake::Connection, "#process, with an unknown event" do
217
- let(:data) { {} }
218
- let(:json) { stub }
219
- let(:name) { "channel" }
220
- let(:event) { "hello-world" }
221
- let(:channel) { stub(emit: nil) }
222
- let(:message) { { event: event, data: data, channel: name } }
266
+ it "includes user ID in event when on a presence channel" do
267
+ channel.stubs(:is_a?).with(PusherFake::Channel::Presence).returns(true)
268
+ channel.stubs(members: { subject => { user_id: user_id } })
223
269
 
224
- subject { PusherFake::Connection.new(stub) }
270
+ subject.process(json)
225
271
 
226
- before do
227
- MultiJson.stubs(load: message)
228
- PusherFake::Channel.stubs(factory: channel)
272
+ expect(channel).to have_received(:trigger).
273
+ with("client_event", options.merge(user_id: user_id)).once
274
+ end
229
275
  end
276
+ end
230
277
 
231
- it "parses the JSON data" do
232
- subject.process(json)
278
+ describe PusherFake::Connection, "#process, with an unknown event" do
279
+ it_should_behave_like "#process" do
280
+ let(:data) { {} }
281
+ let(:name) { "channel" }
282
+ let(:event) { "hello-world" }
283
+ let(:channel) { stub(emit: nil) }
284
+ let(:message) { { event: event, data: data, channel: name } }
233
285
 
234
- expect(MultiJson).to have_received(:load).with(json, symbolize_keys: true)
235
- end
286
+ before do
287
+ PusherFake::Channel.stubs(factory: channel)
288
+ end
236
289
 
237
- it "creates a channel from the event data" do
238
- subject.process(json)
290
+ it "creates a channel from the event data" do
291
+ subject.process(json)
239
292
 
240
- expect(PusherFake::Channel).to have_received(:factory).with(name)
241
- end
293
+ expect(PusherFake::Channel).to have_received(:factory).with(name)
294
+ end
242
295
 
243
- it "does not emit the event" do
244
- subject.process(json)
296
+ it "does not emit the event" do
297
+ subject.process(json)
245
298
 
246
- expect(channel).to have_received(:emit).never
299
+ expect(channel).to have_received(:emit).never
300
+ end
247
301
  end
248
302
  end