pusher-fake 0.4.0 → 0.5.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.
- data/features/channel.feature +109 -0
- data/features/step_definitions/api_steps.rb +38 -0
- data/features/step_definitions/channel_steps.rb +1 -1
- data/features/step_definitions/client_steps.rb +2 -2
- data/features/step_definitions/webhook_steps.rb +1 -1
- data/features/support/timing.rb +15 -0
- data/features/support/webhooks.rb +2 -2
- data/features/user.feature +14 -0
- data/lib/pusher-fake.rb +1 -1
- data/lib/pusher-fake/channel.rb +5 -4
- data/lib/pusher-fake/channel/presence.rb +5 -1
- data/lib/pusher-fake/channel/public.rb +5 -1
- data/lib/pusher-fake/connection.rb +5 -3
- data/lib/pusher-fake/server/application.rb +100 -5
- data/spec/lib/pusher-fake/server/application_spec.rb +325 -24
- metadata +16 -10
- data/features/support/wait.rb +0 -9
@@ -0,0 +1,109 @@
|
|
1
|
+
@javascript
|
2
|
+
Feature: Requesting channel information
|
3
|
+
|
4
|
+
Background:
|
5
|
+
Given I am connected
|
6
|
+
|
7
|
+
Scenario: Requesting all channels
|
8
|
+
When I request "/channels"
|
9
|
+
Then I should receive the following JSON:
|
10
|
+
"""
|
11
|
+
{ "channels" : {} }
|
12
|
+
"""
|
13
|
+
When I subscribe to the "chat-message" channel
|
14
|
+
And I request "/channels"
|
15
|
+
Then I should receive the following JSON:
|
16
|
+
"""
|
17
|
+
{ "channels" : {
|
18
|
+
"chat-message" : {}
|
19
|
+
}
|
20
|
+
}
|
21
|
+
"""
|
22
|
+
When I subscribe to the "presence-game-1" channel with presence events
|
23
|
+
And I request "/channels"
|
24
|
+
Then I should receive the following JSON:
|
25
|
+
"""
|
26
|
+
{ "channels" : {
|
27
|
+
"chat-message" : {},
|
28
|
+
"presence-game-1" : {}
|
29
|
+
}
|
30
|
+
}
|
31
|
+
"""
|
32
|
+
|
33
|
+
Scenario: Requesting all channels, with a filter
|
34
|
+
Given I subscribe to the "chat-message" channel
|
35
|
+
And I subscribe to the "presence-game-1" channel with presence events
|
36
|
+
When I request "/channels" with the following options:
|
37
|
+
| filter_by_prefix |
|
38
|
+
| chat |
|
39
|
+
Then I should receive the following JSON:
|
40
|
+
"""
|
41
|
+
{ "channels" : {
|
42
|
+
"chat-message" : {}
|
43
|
+
}
|
44
|
+
}
|
45
|
+
"""
|
46
|
+
|
47
|
+
Scenario: Requesting all channels, with a valid filter and info attributes
|
48
|
+
Given I subscribe to the "chat-message" channel
|
49
|
+
And I subscribe to the "presence-game-1" channel with presence events
|
50
|
+
When I request "/channels" with the following options:
|
51
|
+
| filter_by_prefix | info |
|
52
|
+
| presence- | user_count |
|
53
|
+
Then I should receive the following JSON:
|
54
|
+
"""
|
55
|
+
{ "channels" : {
|
56
|
+
"presence-game-1" : {
|
57
|
+
"user_count" : 1
|
58
|
+
}
|
59
|
+
}
|
60
|
+
}
|
61
|
+
"""
|
62
|
+
|
63
|
+
Scenario: Requesting all channels, with an invalid filter and info attributes
|
64
|
+
Given I subscribe to the "chat-message" channel
|
65
|
+
And I subscribe to the "presence-game-1" channel with presence events
|
66
|
+
When I request "/channels" with the following options:
|
67
|
+
| filter_by_prefix | info |
|
68
|
+
| chat- | user_count |
|
69
|
+
Then I should receive the following error:
|
70
|
+
"""
|
71
|
+
user_count may only be requested for presence channels - please supply filter_by_prefix begining with presence-
|
72
|
+
"""
|
73
|
+
|
74
|
+
Scenario: Requesting a channel, with no occupants
|
75
|
+
When I request "/channels/empty"
|
76
|
+
Then I should receive the following JSON:
|
77
|
+
"""
|
78
|
+
{ "occupied" : false }
|
79
|
+
"""
|
80
|
+
|
81
|
+
Scenario: Requesting a channel, with an occupant
|
82
|
+
Given I subscribe to the "non-empty" channel
|
83
|
+
When I request "/channels/non-empty"
|
84
|
+
Then I should receive the following JSON:
|
85
|
+
"""
|
86
|
+
{ "occupied" : true }
|
87
|
+
"""
|
88
|
+
|
89
|
+
Scenario: Requesting a channel, with valid info attributes
|
90
|
+
Given I subscribe to the "presence-1" channel
|
91
|
+
And Bob is connected
|
92
|
+
And Bob is subscribed to the "presence-1" channel
|
93
|
+
When I request "/channels/presence-1" with the following options:
|
94
|
+
| info |
|
95
|
+
| user_count |
|
96
|
+
Then I should receive the following JSON:
|
97
|
+
"""
|
98
|
+
{ "occupied" : true,
|
99
|
+
"user_count" : 2 }
|
100
|
+
"""
|
101
|
+
|
102
|
+
Scenario: Requesting a channel, with invalid info attributes
|
103
|
+
When I request "/channels/public-1" with the following options:
|
104
|
+
| info |
|
105
|
+
| user_count |
|
106
|
+
Then I should receive the following error:
|
107
|
+
"""
|
108
|
+
Cannot retrieve the user count unless the channel is a presence channel
|
109
|
+
"""
|
@@ -0,0 +1,38 @@
|
|
1
|
+
When %{I request "$path"} do |path|
|
2
|
+
wait do
|
3
|
+
@response = Pusher.get(path)
|
4
|
+
end
|
5
|
+
end
|
6
|
+
|
7
|
+
When %{I request "$path" with the following options:} do |path, table|
|
8
|
+
wait do
|
9
|
+
begin
|
10
|
+
@response = Pusher.get(path, table.hashes.first)
|
11
|
+
rescue => error
|
12
|
+
@error = error
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
Then %{I should receive the following JSON:} do |string|
|
18
|
+
expected = MultiJson.load(string)
|
19
|
+
expected = expected.inject({}) do |result, (key, value)|
|
20
|
+
result[key.to_sym] = value
|
21
|
+
result
|
22
|
+
end
|
23
|
+
|
24
|
+
@response.should == expected
|
25
|
+
end
|
26
|
+
|
27
|
+
Then %{I should receive the following error:} do |string|
|
28
|
+
@error.message.should include(string.strip)
|
29
|
+
end
|
30
|
+
|
31
|
+
Then /^I should receive JSON for (\d+) users?$/ do |count|
|
32
|
+
users = @response[:users]
|
33
|
+
users.length.should == count.to_i
|
34
|
+
users.each do |user|
|
35
|
+
object = ObjectSpace._id2ref(user["id"])
|
36
|
+
object.should be_a(PusherFake::Connection)
|
37
|
+
end
|
38
|
+
end
|
@@ -61,7 +61,7 @@ When %{$name unsubscribes from the "$channel" channel} do |name, channel|
|
|
61
61
|
end
|
62
62
|
|
63
63
|
Then %{I should be subscribed to the "$channel" channel} do |channel|
|
64
|
-
|
64
|
+
timeout_after(5) do
|
65
65
|
subscribed = page.evaluate_script(%{
|
66
66
|
var
|
67
67
|
channel = Pusher.instance.channel(#{MultiJson.dump(channel)});
|
@@ -14,14 +14,14 @@ Given "I change my socket ID" do
|
|
14
14
|
end
|
15
15
|
|
16
16
|
Then "I should be connected" do
|
17
|
-
|
17
|
+
timeout_after(5) do
|
18
18
|
state = page.evaluate_script("Pusher.instance.connection.state")
|
19
19
|
state == "connected"
|
20
20
|
end
|
21
21
|
end
|
22
22
|
|
23
23
|
Then "I should not be connected" do
|
24
|
-
|
24
|
+
timeout_after(5) do
|
25
25
|
state = page.evaluate_script("Pusher.instance.connection.state")
|
26
26
|
state == "unavailable"
|
27
27
|
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module Capybara
|
2
|
+
module Timing
|
3
|
+
def wait(seconds = 0.25, &block)
|
4
|
+
sleep(seconds) && yield
|
5
|
+
end
|
6
|
+
|
7
|
+
def timeout_after(seconds, &block)
|
8
|
+
Timeout::timeout(seconds) do
|
9
|
+
sleep(0.05) until yield
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
World(Capybara::Timing)
|
@@ -0,0 +1,14 @@
|
|
1
|
+
@javascript
|
2
|
+
Feature: Requesting user information
|
3
|
+
|
4
|
+
Scenario: Requesting users for a channel
|
5
|
+
Given I am connected
|
6
|
+
And Bob is connected
|
7
|
+
When I request "/channels/public-1/users"
|
8
|
+
Then I should receive JSON for 0 users
|
9
|
+
When I subscribe to the "public-1" channel
|
10
|
+
And I request "/channels/public-1/users"
|
11
|
+
Then I should receive JSON for 1 user
|
12
|
+
When Bob is subscribed to the "public-1" channel
|
13
|
+
And I request "/channels/public-1/users"
|
14
|
+
Then I should receive JSON for 2 users
|
data/lib/pusher-fake.rb
CHANGED
data/lib/pusher-fake/channel.rb
CHANGED
@@ -2,10 +2,10 @@ module PusherFake
|
|
2
2
|
module Channel
|
3
3
|
class << self
|
4
4
|
# Name matcher for private channels.
|
5
|
-
PRIVATE_CHANNEL_MATCHER =
|
5
|
+
PRIVATE_CHANNEL_MATCHER = /\Aprivate-/.freeze
|
6
6
|
|
7
7
|
# Name matcher for presence channels.
|
8
|
-
PRESENCE_CHANNEL_MATCHER =
|
8
|
+
PRESENCE_CHANNEL_MATCHER = /\Apresence-/.freeze
|
9
9
|
|
10
10
|
# @return [Hash] Cache of existing channels.
|
11
11
|
attr_accessor :channels
|
@@ -48,9 +48,10 @@ module PusherFake
|
|
48
48
|
# @param [String] name The name of the channel.
|
49
49
|
# @return [Class] The class to use for the channel.
|
50
50
|
def class_for(name)
|
51
|
-
|
51
|
+
case name
|
52
|
+
when PRIVATE_CHANNEL_MATCHER
|
52
53
|
Private
|
53
|
-
|
54
|
+
when PRESENCE_CHANNEL_MATCHER
|
54
55
|
Presence
|
55
56
|
else
|
56
57
|
Public
|
@@ -15,6 +15,8 @@ module PusherFake
|
|
15
15
|
|
16
16
|
# Remove the +connection+ from the channel and notify the channel.
|
17
17
|
#
|
18
|
+
# Also trigger the member_removed webhook.
|
19
|
+
#
|
18
20
|
# @param [Connection] connection The connection to remove.
|
19
21
|
def remove(connection)
|
20
22
|
super
|
@@ -39,9 +41,11 @@ module PusherFake
|
|
39
41
|
|
40
42
|
private
|
41
43
|
|
42
|
-
# Store the member data for the connection and notify
|
44
|
+
# Store the member data for the connection and notify the channel a
|
43
45
|
# member was added.
|
44
46
|
#
|
47
|
+
# Also trigger the member_added webhook.
|
48
|
+
#
|
45
49
|
# @param [Connection] connection The connection a subscription succeeded for.
|
46
50
|
# @param [Hash] options The options for the channel.
|
47
51
|
def subscription_succeeded(connection, options = {})
|
@@ -45,6 +45,8 @@ module PusherFake
|
|
45
45
|
|
46
46
|
# Remove the +connection+ from the channel.
|
47
47
|
#
|
48
|
+
# If it is the last connection, trigger the channel_vacated webhook.
|
49
|
+
#
|
48
50
|
# @param [Connection] connection The connection to remove.
|
49
51
|
def remove(connection)
|
50
52
|
connections.delete(connection)
|
@@ -64,9 +66,11 @@ module PusherFake
|
|
64
66
|
|
65
67
|
private
|
66
68
|
|
67
|
-
# Notify the
|
69
|
+
# Notify the +connection+ of the successful subscription and add the
|
68
70
|
# connection to the channel.
|
69
71
|
#
|
72
|
+
# If it is the first connection, trigger the channel_occupied webhook.
|
73
|
+
#
|
70
74
|
# @param [Connection] connection The connection a subscription succeeded for.
|
71
75
|
# @param [Hash] options The options for the channel.
|
72
76
|
def subscription_succeeded(connection, options = {})
|
@@ -1,5 +1,8 @@
|
|
1
1
|
module PusherFake
|
2
2
|
class Connection
|
3
|
+
# Name matcher for client events.
|
4
|
+
CLIENT_EVENT_MATCHER = /\Aclient-(.+)\Z/.freeze
|
5
|
+
|
3
6
|
# @return [EventMachine::WebSocket::Connection] The socket object for this connection.
|
4
7
|
attr_reader :socket
|
5
8
|
|
@@ -18,9 +21,8 @@ module PusherFake
|
|
18
21
|
def emit(event, data = {}, channel = nil)
|
19
22
|
message = { event: event, data: data }
|
20
23
|
message[:channel] = channel if channel
|
21
|
-
message = MultiJson.dump(message)
|
22
24
|
|
23
|
-
socket.send(message)
|
25
|
+
socket.send(MultiJson.dump(message))
|
24
26
|
end
|
25
27
|
|
26
28
|
# Notify the Pusher client that a connection has been established.
|
@@ -43,7 +45,7 @@ module PusherFake
|
|
43
45
|
channel.add(self, data)
|
44
46
|
when "pusher:unsubscribe"
|
45
47
|
channel.remove(self)
|
46
|
-
when
|
48
|
+
when CLIENT_EVENT_MATCHER
|
47
49
|
return unless channel.is_a?(Channel::Private)
|
48
50
|
return unless channel.includes?(self)
|
49
51
|
|
@@ -1,20 +1,115 @@
|
|
1
1
|
module PusherFake
|
2
2
|
module Server
|
3
3
|
class Application
|
4
|
-
|
5
|
-
|
4
|
+
CHANNEL_FILTER_ERROR = "user_count may only be requested for presence channels - " +
|
5
|
+
"please supply filter_by_prefix begining with presence-".freeze
|
6
|
+
|
7
|
+
CHANNEL_USER_COUNT_ERROR = "Cannot retrieve the user count unless the channel is a presence channel".freeze
|
8
|
+
|
9
|
+
PRESENCE_PREFIX_MATCHER = /\Apresence-/.freeze
|
10
|
+
|
11
|
+
# Process an API request.
|
6
12
|
#
|
7
13
|
# @param [Hash] environment The request environment.
|
8
14
|
# @return [Rack::Response] A successful response.
|
9
15
|
def self.call(environment)
|
10
|
-
|
11
|
-
|
16
|
+
id = PusherFake.configuration.app_id
|
17
|
+
request = Rack::Request.new(environment)
|
18
|
+
response = case request.path
|
19
|
+
when %r{\A/apps/#{id}/events\Z}
|
20
|
+
events(request)
|
21
|
+
when %r{\A/apps/#{id}/channels\Z}
|
22
|
+
channels(request)
|
23
|
+
when %r{\A/apps/#{id}/channels/([^/]+)\Z}
|
24
|
+
channel($1, request)
|
25
|
+
when %r{\A/apps/#{id}/channels/([^/]+)/users\Z}
|
26
|
+
users($1)
|
27
|
+
end
|
28
|
+
|
29
|
+
Rack::Response.new(MultiJson.dump(response)).finish
|
30
|
+
rescue => error
|
31
|
+
Rack::Response.new(error.message, 400).finish
|
32
|
+
end
|
12
33
|
|
34
|
+
# Emit an event with data to the requested channel(s).
|
35
|
+
#
|
36
|
+
# @param [Rack::Request] request The HTTP request.
|
37
|
+
# @return [Hash] An empty hash.
|
38
|
+
def self.events(request)
|
39
|
+
event = MultiJson.load(request.body.read)
|
13
40
|
event["channels"].each do |channel|
|
14
41
|
Channel.factory(channel).emit(event["name"], event["data"], socket_id: event["socket_id"])
|
15
42
|
end
|
16
43
|
|
17
|
-
|
44
|
+
{}
|
45
|
+
end
|
46
|
+
|
47
|
+
# Return a hash of channel information.
|
48
|
+
#
|
49
|
+
# Occupied status is always included. A user count may be requested for
|
50
|
+
# presence channels.
|
51
|
+
#
|
52
|
+
# @param [String] name The channel name.
|
53
|
+
# @params [Rack::Request] request The HTTP request.
|
54
|
+
# @return [Hash] A hash of channel information.
|
55
|
+
def self.channel(name, request)
|
56
|
+
info = request.params["info"].to_s.split(",")
|
57
|
+
|
58
|
+
if info.include?("user_count") && name !~ PRESENCE_PREFIX_MATCHER
|
59
|
+
raise CHANNEL_USER_COUNT_ERROR
|
60
|
+
end
|
61
|
+
|
62
|
+
channels = PusherFake::Channel.channels || {}
|
63
|
+
channel = channels[name]
|
64
|
+
|
65
|
+
{}.tap do |result|
|
66
|
+
result[:occupied] = !channel.nil? && channel.connections.length > 0
|
67
|
+
result[:user_count] = channel.connections.length if channel && info.include?("user_count")
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
# Returns a hash of occupied channels, optionally filtering with a prefix.
|
72
|
+
#
|
73
|
+
# When filtering to presence chanenls, the user count maybe also be requested.
|
74
|
+
#
|
75
|
+
# @param [Rack::Request] request The HTTP request.
|
76
|
+
# @return [Hash] A hash of occupied channels.
|
77
|
+
def self.channels(request)
|
78
|
+
info = request.params["info"].to_s.split(",")
|
79
|
+
prefix = request.params["filter_by_prefix"].to_s
|
80
|
+
|
81
|
+
if info.include?("user_count") && prefix !~ PRESENCE_PREFIX_MATCHER
|
82
|
+
raise CHANNEL_FILTER_ERROR
|
83
|
+
end
|
84
|
+
|
85
|
+
filter = Regexp.new(%r{\A#{prefix}})
|
86
|
+
channels = PusherFake::Channel.channels || {}
|
87
|
+
channels.inject({ channels: {} }) do |result, (name, channel)|
|
88
|
+
unless filter && name !~ filter
|
89
|
+
channels = result[:channels]
|
90
|
+
channels[name] = {}
|
91
|
+
channels[name][:user_count] = channel.connections.length if info.include?("user_count")
|
92
|
+
end
|
93
|
+
|
94
|
+
result
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
# Returns a hash of the IDs for the users in the channel.
|
99
|
+
#
|
100
|
+
# @param [String] name The channel name.
|
101
|
+
# @return [Hash] A hash of user IDs.
|
102
|
+
def self.users(name)
|
103
|
+
channels = PusherFake::Channel.channels || {}
|
104
|
+
channel = channels[name]
|
105
|
+
|
106
|
+
if channel
|
107
|
+
users = channel.connections.map do |connection|
|
108
|
+
{ id: connection.object_id }
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
{ users: users || [] }
|
18
113
|
end
|
19
114
|
end
|
20
115
|
end
|
@@ -1,17 +1,10 @@
|
|
1
1
|
require "spec_helper"
|
2
2
|
|
3
|
-
|
4
|
-
let(:
|
5
|
-
let(:
|
6
|
-
let(:
|
7
|
-
let(:name) { "event-name" }
|
8
|
-
let(:event) { { "channels" => channels, "name" => name, "data" => data, "socket_id" => socket_id } }
|
9
|
-
let(:request) { stub(body: body) }
|
10
|
-
let(:channels) { ["channel-1", "channel-2"] }
|
3
|
+
shared_examples_for "an API request" do
|
4
|
+
let(:hash) { mock }
|
5
|
+
let(:string) { mock }
|
6
|
+
let(:request) { stub(path: path) }
|
11
7
|
let(:response) { mock }
|
12
|
-
let(:channel_1) { stub(emit: true) }
|
13
|
-
let(:channel_2) { stub(emit: true) }
|
14
|
-
let(:socket_id) { stub }
|
15
8
|
let(:environment) { mock }
|
16
9
|
|
17
10
|
subject { PusherFake::Server::Application }
|
@@ -19,11 +12,9 @@ describe PusherFake::Server::Application, ".call" do
|
|
19
12
|
before do
|
20
13
|
response.stubs(finish: response)
|
21
14
|
|
22
|
-
MultiJson.stubs(
|
15
|
+
MultiJson.stubs(dump: string)
|
23
16
|
Rack::Request.stubs(new: request)
|
24
17
|
Rack::Response.stubs(new: response)
|
25
|
-
PusherFake::Channel.stubs(:factory).with(channels[0]).returns(channel_1)
|
26
|
-
PusherFake::Channel.stubs(:factory).with(channels[1]).returns(channel_2)
|
27
18
|
end
|
28
19
|
|
29
20
|
it "creates a request" do
|
@@ -31,28 +22,85 @@ describe PusherFake::Server::Application, ".call" do
|
|
31
22
|
Rack::Request.should have_received(:new).with(environment)
|
32
23
|
end
|
33
24
|
|
34
|
-
it "
|
25
|
+
it "dumps the response hash to JSON" do
|
35
26
|
subject.call(environment)
|
36
|
-
MultiJson.should have_received(:
|
27
|
+
MultiJson.should have_received(:dump).with(hash)
|
37
28
|
end
|
38
29
|
|
39
|
-
it "creates
|
30
|
+
it "creates a Rack response with the response JSON" do
|
40
31
|
subject.call(environment)
|
32
|
+
Rack::Response.should have_received(:new).with(string)
|
33
|
+
end
|
41
34
|
|
42
|
-
|
43
|
-
|
35
|
+
it "finishes the response" do
|
36
|
+
subject.call(environment)
|
37
|
+
response.should have_received(:finish).with()
|
38
|
+
end
|
39
|
+
|
40
|
+
it "returns the response" do
|
41
|
+
subject.call(environment).should == response
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
describe PusherFake::Server::Application, ".call, for triggering events" do
|
46
|
+
it_should_behave_like "an API request" do
|
47
|
+
let(:id) { PusherFake.configuration.app_id }
|
48
|
+
let(:path) { "/apps/#{id}/events" }
|
49
|
+
|
50
|
+
before do
|
51
|
+
subject.stubs(events: hash)
|
52
|
+
end
|
53
|
+
|
54
|
+
it "emits events" do
|
55
|
+
subject.call(environment)
|
56
|
+
subject.should have_received(:events).with(request)
|
44
57
|
end
|
45
58
|
end
|
59
|
+
end
|
46
60
|
|
47
|
-
|
61
|
+
describe PusherFake::Server::Application, ".call, for retrieving occupied channels" do
|
62
|
+
it_should_behave_like "an API request" do
|
63
|
+
let(:id) { PusherFake.configuration.app_id }
|
64
|
+
let(:path) { "/apps/#{id}/channels" }
|
65
|
+
|
66
|
+
before do
|
67
|
+
subject.stubs(channels: hash)
|
68
|
+
end
|
69
|
+
|
70
|
+
it "filters the occupied channels" do
|
71
|
+
subject.call(environment)
|
72
|
+
subject.should have_received(:channels).with(request)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
describe PusherFake::Server::Application, ".call, raising an error" do
|
78
|
+
let(:id) { PusherFake.configuration.app_id }
|
79
|
+
let(:path) { "/apps/#{id}/channels" }
|
80
|
+
let(:message) { "Example error message." }
|
81
|
+
let(:request) { stub(path: path) }
|
82
|
+
let(:response) { mock }
|
83
|
+
let(:environment) { mock }
|
84
|
+
|
85
|
+
subject { PusherFake::Server::Application }
|
86
|
+
|
87
|
+
before do
|
88
|
+
subject.stubs(:channels).raises(message)
|
89
|
+
|
90
|
+
response.stubs(finish: response)
|
91
|
+
|
92
|
+
Rack::Request.stubs(new: request)
|
93
|
+
Rack::Response.stubs(new: response)
|
94
|
+
end
|
95
|
+
|
96
|
+
it "creates a request" do
|
48
97
|
subject.call(environment)
|
49
|
-
|
50
|
-
channel_2.should have_received(:emit).with(name, data, socket_id: socket_id)
|
98
|
+
Rack::Request.should have_received(:new).with(environment)
|
51
99
|
end
|
52
100
|
|
53
|
-
it "creates a Rack response with
|
101
|
+
it "creates a Rack response with the error message" do
|
54
102
|
subject.call(environment)
|
55
|
-
Rack::Response.should have_received(:new).with(
|
103
|
+
Rack::Response.should have_received(:new).with(message, 400)
|
56
104
|
end
|
57
105
|
|
58
106
|
it "finishes the response" do
|
@@ -64,3 +112,256 @@ describe PusherFake::Server::Application, ".call" do
|
|
64
112
|
subject.call(environment).should == response
|
65
113
|
end
|
66
114
|
end
|
115
|
+
|
116
|
+
describe PusherFake::Server::Application, ".events" do
|
117
|
+
let(:body) { stub(read: json) }
|
118
|
+
let(:data) { mock }
|
119
|
+
let(:json) { mock }
|
120
|
+
let(:name) { "event-name" }
|
121
|
+
let(:event) { { "channels" => channels, "name" => name, "data" => data, "socket_id" => socket_id } }
|
122
|
+
let(:request) { stub(body: body) }
|
123
|
+
let(:channels) { ["channel-1", "channel-2"] }
|
124
|
+
let(:channel_1) { stub(emit: true) }
|
125
|
+
let(:channel_2) { stub(emit: true) }
|
126
|
+
let(:socket_id) { stub }
|
127
|
+
|
128
|
+
subject { PusherFake::Server::Application }
|
129
|
+
|
130
|
+
before do
|
131
|
+
MultiJson.stubs(load: event)
|
132
|
+
PusherFake::Channel.stubs(:factory).with(channels[0]).returns(channel_1)
|
133
|
+
PusherFake::Channel.stubs(:factory).with(channels[1]).returns(channel_2)
|
134
|
+
end
|
135
|
+
|
136
|
+
it "parses the request body as JSON" do
|
137
|
+
subject.events(request)
|
138
|
+
MultiJson.should have_received(:load).with(json)
|
139
|
+
end
|
140
|
+
|
141
|
+
it "creates channels by name" do
|
142
|
+
subject.events(request)
|
143
|
+
|
144
|
+
channels.each do |channel|
|
145
|
+
PusherFake::Channel.should have_received(:factory).with(channel)
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
it "emits the event to the channels" do
|
150
|
+
subject.events(request)
|
151
|
+
|
152
|
+
channel_1.should have_received(:emit).with(name, data, socket_id: socket_id)
|
153
|
+
channel_2.should have_received(:emit).with(name, data, socket_id: socket_id)
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
describe PusherFake::Server::Application, ".channels, requesting all channels" do
|
158
|
+
let(:request) { stub(params: {}) }
|
159
|
+
let(:channels) { { "channel-1" => mock, "channel-2" => mock } }
|
160
|
+
|
161
|
+
subject { PusherFake::Server::Application }
|
162
|
+
|
163
|
+
before do
|
164
|
+
PusherFake::Channel.stubs(channels: channels)
|
165
|
+
end
|
166
|
+
|
167
|
+
it "returns a hash of all the channels" do
|
168
|
+
subject.channels(request).should == {
|
169
|
+
channels: {
|
170
|
+
"channel-1" => {},
|
171
|
+
"channel-2" => {}
|
172
|
+
}
|
173
|
+
}
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
describe PusherFake::Server::Application, ".channels, requesting channels with a filter" do
|
178
|
+
let(:params) { { "filter_by_prefix" => "public-" } }
|
179
|
+
let(:request) { stub(params: params) }
|
180
|
+
let(:channels) { { "public-1" => mock, "presence-1" => mock } }
|
181
|
+
|
182
|
+
subject { PusherFake::Server::Application }
|
183
|
+
|
184
|
+
before do
|
185
|
+
PusherFake::Channel.stubs(channels: channels)
|
186
|
+
end
|
187
|
+
|
188
|
+
it "returns a hash of the channels matching the filter" do
|
189
|
+
subject.channels(request).should == { channels: { "public-1" => {} } }
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
describe PusherFake::Server::Application, ".channels, requesting user count for channels with a filter" do
|
194
|
+
let(:params) { { "filter_by_prefix" => "presence-", "info" => "user_count" } }
|
195
|
+
let(:request) { stub(params: params) }
|
196
|
+
let(:channel) { stub(connections: [mock, mock]) }
|
197
|
+
let(:channels) { { "public-1" => mock, "presence-1" => channel } }
|
198
|
+
|
199
|
+
subject { PusherFake::Server::Application }
|
200
|
+
|
201
|
+
before do
|
202
|
+
PusherFake::Channel.stubs(channels: channels)
|
203
|
+
end
|
204
|
+
|
205
|
+
it "returns a hash of the channels matching the filter and include the user count" do
|
206
|
+
subject.channels(request).should == { channels: { "presence-1" => { user_count: 2 } } }
|
207
|
+
end
|
208
|
+
end
|
209
|
+
|
210
|
+
describe PusherFake::Server::Application, ".channels, requesting all channels with no channels occupied" do
|
211
|
+
let(:request) { stub(params: {}) }
|
212
|
+
let(:channels) { nil }
|
213
|
+
|
214
|
+
subject { PusherFake::Server::Application }
|
215
|
+
|
216
|
+
before do
|
217
|
+
PusherFake::Channel.stubs(channels: channels)
|
218
|
+
end
|
219
|
+
|
220
|
+
it "returns a hash of no channels" do
|
221
|
+
subject.channels(request).should == { channels: {} }
|
222
|
+
end
|
223
|
+
end
|
224
|
+
|
225
|
+
describe PusherFake::Server::Application, ".channels, requesting a user count on a non-presence channel" do
|
226
|
+
let(:params) { { "filter_by_prefix" => "public-", "info" => "user_count" } }
|
227
|
+
let(:request) { stub(params: params) }
|
228
|
+
|
229
|
+
subject { PusherFake::Server::Application }
|
230
|
+
|
231
|
+
it "raises an error" do
|
232
|
+
lambda {
|
233
|
+
subject.channels(request)
|
234
|
+
}.should raise_error(subject::CHANNEL_FILTER_ERROR)
|
235
|
+
end
|
236
|
+
end
|
237
|
+
|
238
|
+
describe PusherFake::Server::Application, ".channel, for an occupied channel" do
|
239
|
+
let(:name) { "public-1" }
|
240
|
+
let(:request) { stub(params: {}) }
|
241
|
+
let(:channel) { stub(connections: [mock]) }
|
242
|
+
let(:channels) { { name => channel } }
|
243
|
+
|
244
|
+
subject { PusherFake::Server::Application }
|
245
|
+
|
246
|
+
before do
|
247
|
+
PusherFake::Channel.stubs(channels: channels)
|
248
|
+
end
|
249
|
+
|
250
|
+
it "returns a hash with the occupied status" do
|
251
|
+
subject.channel(name, request).should == { occupied: true }
|
252
|
+
end
|
253
|
+
end
|
254
|
+
|
255
|
+
describe PusherFake::Server::Application, ".channel, for an unoccupied channel" do
|
256
|
+
let(:name) { "public-1" }
|
257
|
+
let(:request) { stub(params: {}) }
|
258
|
+
let(:channel) { stub(connections: []) }
|
259
|
+
let(:channels) { { name => channel } }
|
260
|
+
|
261
|
+
subject { PusherFake::Server::Application }
|
262
|
+
|
263
|
+
before do
|
264
|
+
PusherFake::Channel.stubs(channels: channels)
|
265
|
+
end
|
266
|
+
|
267
|
+
it "returns a hash with the occupied status" do
|
268
|
+
subject.channel(name, request).should == { occupied: false }
|
269
|
+
end
|
270
|
+
end
|
271
|
+
|
272
|
+
describe PusherFake::Server::Application, ".channel, for an unknown channel" do
|
273
|
+
let(:request) { stub(params: {}) }
|
274
|
+
let(:channels) { {} }
|
275
|
+
|
276
|
+
subject { PusherFake::Server::Application }
|
277
|
+
|
278
|
+
before do
|
279
|
+
PusherFake::Channel.stubs(channels: channels)
|
280
|
+
end
|
281
|
+
|
282
|
+
it "returns a hash with the occupied status" do
|
283
|
+
subject.channel("fake", request).should == { occupied: false }
|
284
|
+
end
|
285
|
+
end
|
286
|
+
|
287
|
+
describe PusherFake::Server::Application, ".channel, request user count for a presence channel" do
|
288
|
+
let(:name) { "presence-1" }
|
289
|
+
let(:params) { { "info" => "user_count" } }
|
290
|
+
let(:request) { stub(params: params) }
|
291
|
+
let(:channel) { stub(connections: [mock, mock]) }
|
292
|
+
let(:channels) { { name => channel } }
|
293
|
+
|
294
|
+
subject { PusherFake::Server::Application }
|
295
|
+
|
296
|
+
before do
|
297
|
+
PusherFake::Channel.stubs(channels: channels)
|
298
|
+
end
|
299
|
+
|
300
|
+
it "returns a hash with the occupied status" do
|
301
|
+
subject.channel(name, request).should == { occupied: true, user_count: 2 }
|
302
|
+
end
|
303
|
+
end
|
304
|
+
|
305
|
+
describe PusherFake::Server::Application, ".channel, requesting a user count on a non-presence channel" do
|
306
|
+
let(:params) { { "info" => "user_count" } }
|
307
|
+
let(:request) { stub(params: params) }
|
308
|
+
|
309
|
+
subject { PusherFake::Server::Application }
|
310
|
+
|
311
|
+
it "raises an error" do
|
312
|
+
lambda {
|
313
|
+
subject.channel("public-1", request)
|
314
|
+
}.should raise_error(subject::CHANNEL_USER_COUNT_ERROR)
|
315
|
+
end
|
316
|
+
end
|
317
|
+
|
318
|
+
describe PusherFake::Server::Application, ".users, for an occupied channel" do
|
319
|
+
let(:name) { "public-1" }
|
320
|
+
let(:user_1) { mock }
|
321
|
+
let(:user_2) { mock }
|
322
|
+
let(:channel) { stub(connections: [user_1, user_2]) }
|
323
|
+
let(:channels) { { name => channel } }
|
324
|
+
|
325
|
+
subject { PusherFake::Server::Application }
|
326
|
+
|
327
|
+
before do
|
328
|
+
PusherFake::Channel.stubs(channels: channels)
|
329
|
+
end
|
330
|
+
|
331
|
+
it "returns a hash with the occupied status" do
|
332
|
+
subject.users(name).should == { users: [
|
333
|
+
{ id: user_1.object_id },
|
334
|
+
{ id: user_2.object_id }
|
335
|
+
] }
|
336
|
+
end
|
337
|
+
end
|
338
|
+
|
339
|
+
describe PusherFake::Server::Application, ".users, for an empty channel" do
|
340
|
+
let(:name) { "public-1" }
|
341
|
+
let(:channel) { stub(connections: []) }
|
342
|
+
let(:channels) { { name => channel } }
|
343
|
+
|
344
|
+
subject { PusherFake::Server::Application }
|
345
|
+
|
346
|
+
before do
|
347
|
+
PusherFake::Channel.stubs(channels: channels)
|
348
|
+
end
|
349
|
+
|
350
|
+
it "returns a hash with the occupied status" do
|
351
|
+
subject.users(name).should == { users: [] }
|
352
|
+
end
|
353
|
+
end
|
354
|
+
|
355
|
+
describe PusherFake::Server::Application, ".users, for an unknown channel" do
|
356
|
+
let(:channels) { {} }
|
357
|
+
|
358
|
+
subject { PusherFake::Server::Application }
|
359
|
+
|
360
|
+
before do
|
361
|
+
PusherFake::Channel.stubs(channels: channels)
|
362
|
+
end
|
363
|
+
|
364
|
+
it "returns a hash with the occupied status" do
|
365
|
+
subject.users("fake").should == { users: [] }
|
366
|
+
end
|
367
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: pusher-fake
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.5.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date:
|
12
|
+
date: 2013-01-21 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: em-http-request
|
@@ -98,7 +98,7 @@ dependencies:
|
|
98
98
|
requirements:
|
99
99
|
- - '='
|
100
100
|
- !ruby/object:Gem::Version
|
101
|
-
version: 0.
|
101
|
+
version: 0.14.1
|
102
102
|
type: :development
|
103
103
|
prerelease: false
|
104
104
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -106,7 +106,7 @@ dependencies:
|
|
106
106
|
requirements:
|
107
107
|
- - '='
|
108
108
|
- !ruby/object:Gem::Version
|
109
|
-
version: 0.
|
109
|
+
version: 0.14.1
|
110
110
|
- !ruby/object:Gem::Dependency
|
111
111
|
name: cucumber
|
112
112
|
requirement: !ruby/object:Gem::Requirement
|
@@ -130,7 +130,7 @@ dependencies:
|
|
130
130
|
requirements:
|
131
131
|
- - '='
|
132
132
|
- !ruby/object:Gem::Version
|
133
|
-
version: 0.11.
|
133
|
+
version: 0.11.2
|
134
134
|
type: :development
|
135
135
|
prerelease: false
|
136
136
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -138,7 +138,7 @@ dependencies:
|
|
138
138
|
requirements:
|
139
139
|
- - '='
|
140
140
|
- !ruby/object:Gem::Version
|
141
|
-
version: 0.11.
|
141
|
+
version: 0.11.2
|
142
142
|
- !ruby/object:Gem::Dependency
|
143
143
|
name: rake
|
144
144
|
requirement: !ruby/object:Gem::Requirement
|
@@ -235,11 +235,13 @@ files:
|
|
235
235
|
- lib/pusher-fake/server.rb
|
236
236
|
- lib/pusher-fake/webhook.rb
|
237
237
|
- lib/pusher-fake.rb
|
238
|
+
- features/channel.feature
|
238
239
|
- features/channel_presence.feature
|
239
240
|
- features/channel_subscribe.feature
|
240
241
|
- features/channel_trigger.feature
|
241
242
|
- features/channel_webhooks.feature
|
242
243
|
- features/client_connect.feature
|
244
|
+
- features/step_definitions/api_steps.rb
|
243
245
|
- features/step_definitions/channel_steps.rb
|
244
246
|
- features/step_definitions/client_steps.rb
|
245
247
|
- features/step_definitions/event_steps.rb
|
@@ -251,8 +253,9 @@ files:
|
|
251
253
|
- features/support/application.rb
|
252
254
|
- features/support/environment.rb
|
253
255
|
- features/support/pusher-fake.rb
|
254
|
-
- features/support/
|
256
|
+
- features/support/timing.rb
|
255
257
|
- features/support/webhooks.rb
|
258
|
+
- features/user.feature
|
256
259
|
- spec/lib/pusher-fake/channel/presence_spec.rb
|
257
260
|
- spec/lib/pusher-fake/channel/private_spec.rb
|
258
261
|
- spec/lib/pusher-fake/channel/public_spec.rb
|
@@ -279,7 +282,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
279
282
|
version: '0'
|
280
283
|
segments:
|
281
284
|
- 0
|
282
|
-
hash:
|
285
|
+
hash: 399560288532115736
|
283
286
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
284
287
|
none: false
|
285
288
|
requirements:
|
@@ -288,7 +291,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
288
291
|
version: '0'
|
289
292
|
segments:
|
290
293
|
- 0
|
291
|
-
hash:
|
294
|
+
hash: 399560288532115736
|
292
295
|
requirements: []
|
293
296
|
rubyforge_project:
|
294
297
|
rubygems_version: 1.8.23
|
@@ -296,11 +299,13 @@ signing_key:
|
|
296
299
|
specification_version: 3
|
297
300
|
summary: A fake Pusher server for development and testing.
|
298
301
|
test_files:
|
302
|
+
- features/channel.feature
|
299
303
|
- features/channel_presence.feature
|
300
304
|
- features/channel_subscribe.feature
|
301
305
|
- features/channel_trigger.feature
|
302
306
|
- features/channel_webhooks.feature
|
303
307
|
- features/client_connect.feature
|
308
|
+
- features/step_definitions/api_steps.rb
|
304
309
|
- features/step_definitions/channel_steps.rb
|
305
310
|
- features/step_definitions/client_steps.rb
|
306
311
|
- features/step_definitions/event_steps.rb
|
@@ -312,8 +317,9 @@ test_files:
|
|
312
317
|
- features/support/application.rb
|
313
318
|
- features/support/environment.rb
|
314
319
|
- features/support/pusher-fake.rb
|
315
|
-
- features/support/
|
320
|
+
- features/support/timing.rb
|
316
321
|
- features/support/webhooks.rb
|
322
|
+
- features/user.feature
|
317
323
|
- spec/lib/pusher-fake/channel/presence_spec.rb
|
318
324
|
- spec/lib/pusher-fake/channel/private_spec.rb
|
319
325
|
- spec/lib/pusher-fake/channel/public_spec.rb
|