faye 0.5.5 → 0.6.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of faye might be problematic. Click here for more details.
- data/History.txt +14 -0
- data/README.rdoc +98 -0
- data/Rakefile +17 -15
- data/lib/faye-browser-min.js +1 -1
- data/lib/faye.rb +14 -5
- data/lib/faye/adapters/rack_adapter.rb +12 -5
- data/lib/faye/engines/base.rb +62 -0
- data/lib/faye/engines/connection.rb +63 -0
- data/lib/faye/engines/memory.rb +89 -0
- data/lib/faye/engines/redis.rb +141 -0
- data/lib/faye/error.rb +16 -4
- data/lib/faye/mixins/publisher.rb +6 -0
- data/lib/faye/protocol/channel.rb +34 -86
- data/lib/faye/protocol/client.rb +36 -52
- data/lib/faye/protocol/extensible.rb +3 -0
- data/lib/faye/protocol/server.rb +119 -169
- data/lib/faye/transport/http.rb +45 -0
- data/lib/faye/transport/local.rb +15 -0
- data/lib/faye/{network → transport}/transport.rb +36 -49
- data/spec/browser.html +35 -0
- data/spec/install.sh +48 -0
- data/spec/javascript/channel_spec.js +15 -0
- data/spec/javascript/client_spec.js +610 -0
- data/spec/javascript/engine_spec.js +319 -0
- data/spec/javascript/faye_spec.js +15 -0
- data/spec/javascript/grammar_spec.js +66 -0
- data/spec/javascript/node_adapter_spec.js +276 -0
- data/spec/javascript/server/connect_spec.js +168 -0
- data/spec/javascript/server/disconnect_spec.js +121 -0
- data/spec/javascript/server/extensions_spec.js +60 -0
- data/spec/javascript/server/handshake_spec.js +153 -0
- data/spec/javascript/server/subscribe_spec.js +245 -0
- data/spec/javascript/server/unsubscribe_spec.js +245 -0
- data/spec/javascript/server_spec.js +146 -0
- data/spec/javascript/transport_spec.js +130 -0
- data/spec/node.js +34 -0
- data/spec/ruby/channel_spec.rb +17 -0
- data/spec/ruby/client_spec.rb +615 -0
- data/spec/ruby/engine_spec.rb +312 -0
- data/spec/ruby/faye_spec.rb +14 -0
- data/spec/ruby/grammar_spec.rb +68 -0
- data/spec/ruby/rack_adapter_spec.rb +209 -0
- data/spec/ruby/server/connect_spec.rb +170 -0
- data/spec/ruby/server/disconnect_spec.rb +120 -0
- data/spec/ruby/server/extensions_spec.rb +69 -0
- data/spec/ruby/server/handshake_spec.rb +151 -0
- data/spec/ruby/server/subscribe_spec.rb +247 -0
- data/spec/ruby/server/unsubscribe_spec.rb +247 -0
- data/spec/ruby/server_spec.rb +138 -0
- data/spec/ruby/transport_spec.rb +128 -0
- data/spec/spec_helper.rb +5 -0
- data/spec/testswarm.pl +200 -0
- data/spec/thin_proxy.rb +36 -0
- metadata +119 -84
- data/Manifest.txt +0 -27
- data/README.txt +0 -98
- data/lib/faye/protocol/connection.rb +0 -111
- data/test/scenario.rb +0 -172
- data/test/test_channel.rb +0 -54
- data/test/test_clients.rb +0 -381
- data/test/test_grammar.rb +0 -86
- data/test/test_server.rb +0 -488
@@ -0,0 +1,312 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
EngineSteps = EM::RSpec.async_steps do
|
4
|
+
def create_client(name, &resume)
|
5
|
+
@inboxes ||= {}
|
6
|
+
@clients ||= {}
|
7
|
+
engine.create_client do |client_id|
|
8
|
+
@clients[name] = client_id
|
9
|
+
@inboxes[name] ||= []
|
10
|
+
resume.call
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def connect(name, engine, &resume)
|
15
|
+
engine.connect(@clients[name]) do |m|
|
16
|
+
m.each do |message|
|
17
|
+
message.delete("id")
|
18
|
+
@inboxes[name] << message
|
19
|
+
end
|
20
|
+
end
|
21
|
+
EM.add_timer(0.01, &resume)
|
22
|
+
end
|
23
|
+
|
24
|
+
def destroy_client(name, &resume)
|
25
|
+
engine.destroy_client(@clients[name], &resume)
|
26
|
+
end
|
27
|
+
|
28
|
+
def check_client_id(name, pattern, &resume)
|
29
|
+
@clients[name].should =~ pattern
|
30
|
+
resume.call
|
31
|
+
end
|
32
|
+
|
33
|
+
def check_num_clients(n, &resume)
|
34
|
+
ids = Set.new
|
35
|
+
@clients.each { |name,id| ids.add(id) }
|
36
|
+
ids.size.should == n
|
37
|
+
resume.call
|
38
|
+
end
|
39
|
+
|
40
|
+
def check_client_exists(name, exists, &resume)
|
41
|
+
engine.client_exists(@clients[name]) do |actual|
|
42
|
+
actual.should == exists
|
43
|
+
resume.call
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def subscribe(name, channel, &resume)
|
48
|
+
engine.subscribe(@clients[name], channel, &resume)
|
49
|
+
end
|
50
|
+
|
51
|
+
def unsubscribe(name, channel, &resume)
|
52
|
+
engine.unsubscribe(@clients[name], channel, &resume)
|
53
|
+
end
|
54
|
+
|
55
|
+
def publish(messages, &resume)
|
56
|
+
messages = [messages].flatten
|
57
|
+
messages.each do |message|
|
58
|
+
message = {"id" => Faye.random}.merge(message)
|
59
|
+
engine.publish(message)
|
60
|
+
end
|
61
|
+
EM.add_timer(0.01, &resume)
|
62
|
+
end
|
63
|
+
|
64
|
+
def ping(name, &resume)
|
65
|
+
engine.ping(@clients[name])
|
66
|
+
resume.call
|
67
|
+
end
|
68
|
+
|
69
|
+
def clock_tick(time, &resume)
|
70
|
+
clock.tick(time)
|
71
|
+
resume.call
|
72
|
+
end
|
73
|
+
|
74
|
+
def expect_message(name, messages, &resume)
|
75
|
+
@inboxes[name].should == messages
|
76
|
+
resume.call
|
77
|
+
end
|
78
|
+
|
79
|
+
def expect_no_message(name, &resume)
|
80
|
+
@inboxes[name].should == []
|
81
|
+
resume.call
|
82
|
+
end
|
83
|
+
|
84
|
+
def clean_redis_db(&resume)
|
85
|
+
engine.disconnect
|
86
|
+
redis = EM::Hiredis::Client.connect
|
87
|
+
redis.flushall(&resume)
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
describe "Pub/sub engines" do
|
92
|
+
shared_examples_for "faye engine" do
|
93
|
+
include EngineSteps
|
94
|
+
|
95
|
+
let(:options) { {:timeout => 1} }
|
96
|
+
let(:engine) { engine_klass.new options }
|
97
|
+
|
98
|
+
before do
|
99
|
+
Faye.ensure_reactor_running!
|
100
|
+
create_client :alice
|
101
|
+
create_client :bob
|
102
|
+
create_client :carol
|
103
|
+
end
|
104
|
+
|
105
|
+
describe :create_client do
|
106
|
+
it "returns a client id" do
|
107
|
+
create_client :dave
|
108
|
+
check_client_id :dave, /^[a-z0-9]+$/
|
109
|
+
end
|
110
|
+
|
111
|
+
it "returns a different id every time" do
|
112
|
+
1.upto(7) { |i| create_client "client#{i}" }
|
113
|
+
check_num_clients 10
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
describe :client_exists do
|
118
|
+
it "returns true if the client id exists" do
|
119
|
+
check_client_exists :alice, true
|
120
|
+
end
|
121
|
+
|
122
|
+
it "returns false if the client id does not exist" do
|
123
|
+
check_client_exists :anything, false
|
124
|
+
end
|
125
|
+
end
|
126
|
+
=begin
|
127
|
+
describe :ping do
|
128
|
+
it "removes a client if it does not ping often enough" do
|
129
|
+
clock_tick 2
|
130
|
+
check_client_exists :alice, false
|
131
|
+
end
|
132
|
+
|
133
|
+
it "prolongs the life of a client" do
|
134
|
+
clock_tick 1
|
135
|
+
ping :alice
|
136
|
+
clock_tick 1
|
137
|
+
check_client_exists :alice, true
|
138
|
+
clock_tick 1
|
139
|
+
check_client_exists :alice, false
|
140
|
+
end
|
141
|
+
end
|
142
|
+
=end
|
143
|
+
describe :destroy_client do
|
144
|
+
it "removes the given client" do
|
145
|
+
destroy_client :alice
|
146
|
+
check_client_exists :alice, false
|
147
|
+
end
|
148
|
+
|
149
|
+
describe "when the client has subscriptions" do
|
150
|
+
before do
|
151
|
+
@message = {"channel" => "/messages/foo", "data" => "ok"}
|
152
|
+
subscribe :alice, "/messages/foo"
|
153
|
+
end
|
154
|
+
|
155
|
+
it "stops the client receiving messages" do
|
156
|
+
connect :alice, engine
|
157
|
+
destroy_client :alice
|
158
|
+
publish @message
|
159
|
+
expect_no_message :alice
|
160
|
+
end
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
describe :publish do
|
165
|
+
before do
|
166
|
+
@message = {"channel" => "/messages/foo", "data" => "ok"}
|
167
|
+
connect :alice, engine
|
168
|
+
connect :bob, engine
|
169
|
+
connect :carol, engine
|
170
|
+
end
|
171
|
+
|
172
|
+
describe "with no subscriptions" do
|
173
|
+
it "delivers no messages" do
|
174
|
+
publish @message
|
175
|
+
expect_no_message :alice
|
176
|
+
expect_no_message :bob
|
177
|
+
expect_no_message :carol
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
describe "with a subscriber" do
|
182
|
+
before { subscribe :alice, "/messages/foo" }
|
183
|
+
|
184
|
+
it "delivers messages to the subscribed client" do
|
185
|
+
publish @message
|
186
|
+
expect_message :alice, [@message]
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
describe "with a subscriber that is removed" do
|
191
|
+
before do
|
192
|
+
subscribe :alice, "/messages/foo"
|
193
|
+
unsubscribe :alice, "/messages/foo"
|
194
|
+
end
|
195
|
+
|
196
|
+
it "does not deliver messages to unsubscribed clients" do
|
197
|
+
publish @message
|
198
|
+
expect_no_message :alice
|
199
|
+
expect_no_message :bob
|
200
|
+
expect_no_message :carol
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
describe "with multiple subscribers" do
|
205
|
+
before do
|
206
|
+
subscribe :alice, "/messages/foo"
|
207
|
+
subscribe :bob, "/messages/bar"
|
208
|
+
subscribe :carol, "/messages/foo"
|
209
|
+
end
|
210
|
+
|
211
|
+
it "delivers messages to the subscribed clients" do
|
212
|
+
publish @message
|
213
|
+
expect_message :alice, [@message]
|
214
|
+
expect_no_message :bob
|
215
|
+
expect_message :carol, [@message]
|
216
|
+
end
|
217
|
+
end
|
218
|
+
|
219
|
+
describe "with a single wildcard" do
|
220
|
+
before do
|
221
|
+
subscribe :alice, "/messages/*"
|
222
|
+
subscribe :bob, "/messages/bar"
|
223
|
+
subscribe :carol, "/*"
|
224
|
+
end
|
225
|
+
|
226
|
+
it "delivers messages to matching subscriptions" do
|
227
|
+
publish @message
|
228
|
+
expect_message :alice, [@message]
|
229
|
+
expect_no_message :bob
|
230
|
+
expect_no_message :carol
|
231
|
+
end
|
232
|
+
end
|
233
|
+
|
234
|
+
describe "with a double wildcard" do
|
235
|
+
before do
|
236
|
+
subscribe :alice, "/messages/**"
|
237
|
+
subscribe :bob, "/messages/bar"
|
238
|
+
subscribe :carol, "/**"
|
239
|
+
end
|
240
|
+
|
241
|
+
it "delivers messages to matching subscriptions" do
|
242
|
+
publish @message
|
243
|
+
expect_message :alice, [@message]
|
244
|
+
expect_no_message :bob
|
245
|
+
expect_message :carol, [@message]
|
246
|
+
end
|
247
|
+
end
|
248
|
+
|
249
|
+
describe "with multiple matching subscriptions for the same client" do
|
250
|
+
before do
|
251
|
+
subscribe :alice, "/messages/foo"
|
252
|
+
subscribe :alice, "/messages/*"
|
253
|
+
end
|
254
|
+
|
255
|
+
it "delivers each message once to each client" do
|
256
|
+
publish @message
|
257
|
+
expect_message :alice, [@message]
|
258
|
+
end
|
259
|
+
|
260
|
+
it "delivers the message as many times as it is published" do
|
261
|
+
publish [@message, @message]
|
262
|
+
expect_message :alice, [@message, @message]
|
263
|
+
end
|
264
|
+
end
|
265
|
+
end
|
266
|
+
end
|
267
|
+
|
268
|
+
shared_examples_for "distributed engine" do
|
269
|
+
include EngineSteps
|
270
|
+
|
271
|
+
let(:options) { {} }
|
272
|
+
let(:left) { engine_klass.new options }
|
273
|
+
let(:right) { engine_klass.new options }
|
274
|
+
|
275
|
+
alias :engine :left
|
276
|
+
|
277
|
+
before do
|
278
|
+
Faye.ensure_reactor_running!
|
279
|
+
create_client :alice
|
280
|
+
create_client :bob
|
281
|
+
|
282
|
+
connect :alice, left
|
283
|
+
end
|
284
|
+
|
285
|
+
describe :publish do
|
286
|
+
before do
|
287
|
+
subscribe :alice, "/foo"
|
288
|
+
publish "channel" => "/foo", "data" => "first"
|
289
|
+
end
|
290
|
+
|
291
|
+
it "only delivers each message once" do
|
292
|
+
expect_message :alice, ["channel" => "/foo", "data" => "first"]
|
293
|
+
publish "channel" => "/foo", "data" => "second"
|
294
|
+
connect :alice, right
|
295
|
+
expect_message :alice, [{"channel" => "/foo", "data" => "first"}, {"channel" => "/foo", "data" => "second"}]
|
296
|
+
end
|
297
|
+
end
|
298
|
+
end
|
299
|
+
|
300
|
+
describe Faye::Engine::Memory do
|
301
|
+
let(:engine_klass) { Faye::Engine::Memory }
|
302
|
+
it_should_behave_like "faye engine"
|
303
|
+
end
|
304
|
+
|
305
|
+
describe Faye::Engine::Redis do
|
306
|
+
let(:engine_klass) { Faye::Engine::Redis }
|
307
|
+
after { clean_redis_db }
|
308
|
+
it_should_behave_like "faye engine"
|
309
|
+
it_should_behave_like "distributed engine"
|
310
|
+
end
|
311
|
+
end
|
312
|
+
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe Faye do
|
4
|
+
describe :random do
|
5
|
+
it "returns a 128-bit random number in base 36" do
|
6
|
+
Faye.random.should =~ /^[a-z0-9]+$/
|
7
|
+
end
|
8
|
+
|
9
|
+
it "always produces the same length of string" do
|
10
|
+
ids = (1..100).map { Faye.random }
|
11
|
+
ids.should be_all { |id| id.size == 25 }
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe Faye::Grammar do
|
4
|
+
describe :CHANNEL_NAME do
|
5
|
+
it "matches valid channel names" do
|
6
|
+
Faye::Grammar::CHANNEL_NAME.should =~ "/fo_o/$@()bar"
|
7
|
+
end
|
8
|
+
|
9
|
+
it "does not match channel patterns" do
|
10
|
+
Faye::Grammar::CHANNEL_NAME.should_not =~ "/foo/**"
|
11
|
+
end
|
12
|
+
|
13
|
+
it "does not match invalid channel names" do
|
14
|
+
Faye::Grammar::CHANNEL_NAME.should_not =~ "foo/$@()bar"
|
15
|
+
Faye::Grammar::CHANNEL_NAME.should_not =~ "/foo/$@()bar/"
|
16
|
+
Faye::Grammar::CHANNEL_NAME.should_not =~ "/fo o/$@()bar"
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
describe :CHANNEL_PATTERN do
|
21
|
+
it "does not match channel names" do
|
22
|
+
Faye::Grammar::CHANNEL_PATTERN.should_not =~ "/fo_o/$@()bar"
|
23
|
+
end
|
24
|
+
|
25
|
+
it "matches valid channel patterns" do
|
26
|
+
Faye::Grammar::CHANNEL_PATTERN.should =~ "/foo/**"
|
27
|
+
Faye::Grammar::CHANNEL_PATTERN.should =~ "/foo/*"
|
28
|
+
end
|
29
|
+
|
30
|
+
it "does not match invalid channel patterns" do
|
31
|
+
Faye::Grammar::CHANNEL_PATTERN.should_not =~ "/foo/**/*"
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
describe :ERROR do
|
36
|
+
it "matches an error with an argument" do
|
37
|
+
Faye::Grammar::ERROR.should =~ "402:xj3sjdsjdsjad:Unknown Client ID"
|
38
|
+
end
|
39
|
+
|
40
|
+
it "matches an error with many arguments" do
|
41
|
+
Faye::Grammar::ERROR.should =~ "403:xj3sjdsjdsjad,/foo/bar:Subscription denied"
|
42
|
+
end
|
43
|
+
|
44
|
+
it "matches an error with no arguments" do
|
45
|
+
Faye::Grammar::ERROR.should =~ "402::Unknown Client ID"
|
46
|
+
end
|
47
|
+
|
48
|
+
it "does not match an error with no code" do
|
49
|
+
Faye::Grammar::ERROR.should_not =~ ":xj3sjdsjdsjad:Unknown Client ID"
|
50
|
+
end
|
51
|
+
|
52
|
+
it "does not match an error with an invalid code" do
|
53
|
+
Faye::Grammar::ERROR.should_not =~ "40:xj3sjdsjdsjad:Unknown Client ID"
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
describe :VERSION do
|
58
|
+
it "matches a version number" do
|
59
|
+
Faye::Grammar::VERSION.should =~ "9"
|
60
|
+
Faye::Grammar::VERSION.should =~ "9.0.a-delta1"
|
61
|
+
end
|
62
|
+
|
63
|
+
it "does not match invalid version numbers" do
|
64
|
+
Faye::Grammar::VERSION.should_not =~ "9.0.a-delta1."
|
65
|
+
Faye::Grammar::VERSION.should_not =~ ""
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,209 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
require "thin_proxy"
|
3
|
+
|
4
|
+
describe Faye::RackAdapter do
|
5
|
+
include Rack::Test::Methods
|
6
|
+
let(:app) { ThinProxy.new(Faye::RackAdapter.new options) }
|
7
|
+
let(:options) { {:mount => "/bayeux", :timeout => 30} }
|
8
|
+
let(:server) { mock "server" }
|
9
|
+
|
10
|
+
after { app.stop }
|
11
|
+
|
12
|
+
let(:content_type) { last_response["Content-Type"] }
|
13
|
+
let(:access_control_origin) { last_response["Access-Control-Allow-Origin"] }
|
14
|
+
let(:json) { JSON.parse(body) }
|
15
|
+
let(:body) { last_response.body }
|
16
|
+
let(:status) { last_response.status.to_i }
|
17
|
+
|
18
|
+
before do
|
19
|
+
Faye::Server.should_receive(:new).with(options).and_return server
|
20
|
+
end
|
21
|
+
|
22
|
+
describe "POST requests" do
|
23
|
+
describe "with cross-origin access control" do
|
24
|
+
before do
|
25
|
+
header "Origin", "http://example.com"
|
26
|
+
end
|
27
|
+
|
28
|
+
it "returns a matching cross-origin access control header" do
|
29
|
+
server.stub(:process).and_yield []
|
30
|
+
post "/bayeux", :message => '[]'
|
31
|
+
access_control_origin.should == "http://example.com"
|
32
|
+
end
|
33
|
+
|
34
|
+
it "forwards the message param onto the server" do
|
35
|
+
server.should_receive(:process).with({"channel" => "/foo"}, false).and_yield []
|
36
|
+
post "/bayeux", :message => '{"channel":"/foo"}'
|
37
|
+
end
|
38
|
+
|
39
|
+
it "returns the server's response as JSON" do
|
40
|
+
server.stub(:process).and_yield ["channel" => "/meta/handshake"]
|
41
|
+
post "/bayeux", :message => '[]'
|
42
|
+
status.should == 200
|
43
|
+
content_type.should == "application/json"
|
44
|
+
json.should == ["channel" => "/meta/handshake"]
|
45
|
+
end
|
46
|
+
|
47
|
+
it "returns a 400 response if malformed JSON is given" do
|
48
|
+
server.should_not_receive(:process)
|
49
|
+
post "/bayeux", :message => "[}"
|
50
|
+
status.should == 400
|
51
|
+
content_type.should == "text/plain"
|
52
|
+
end
|
53
|
+
|
54
|
+
it "returns a 404 if the path is not matched" do
|
55
|
+
server.should_not_receive(:process)
|
56
|
+
post "/blaf", :message => "[]"
|
57
|
+
status.should == 404
|
58
|
+
content_type.should == "text/plain"
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
describe "with application/json" do
|
63
|
+
before do
|
64
|
+
header "Content-Type", "application/json"
|
65
|
+
end
|
66
|
+
|
67
|
+
it "does not return an access control header" do
|
68
|
+
server.stub(:process).and_yield []
|
69
|
+
post "/bayeux", :message => '[]'
|
70
|
+
access_control_origin.should be_nil
|
71
|
+
end
|
72
|
+
|
73
|
+
it "forwards the POST body onto the server" do
|
74
|
+
server.should_receive(:process).with({"channel" => "/foo"}, false).and_yield []
|
75
|
+
post "/bayeux", '{"channel":"/foo"}'
|
76
|
+
end
|
77
|
+
|
78
|
+
it "returns the server's response as JSON" do
|
79
|
+
server.stub(:process).and_yield ["channel" => "/meta/handshake"]
|
80
|
+
post "/bayeux", '[]'
|
81
|
+
status.should == 200
|
82
|
+
content_type.should == "application/json"
|
83
|
+
json.should == ["channel" => "/meta/handshake"]
|
84
|
+
end
|
85
|
+
|
86
|
+
it "returns a 400 response if malformed JSON is given" do
|
87
|
+
server.should_not_receive(:process)
|
88
|
+
post "/bayeux", "[}"
|
89
|
+
status.should == 400
|
90
|
+
content_type.should == "text/plain"
|
91
|
+
end
|
92
|
+
|
93
|
+
it "returns a 404 if the path is not matched" do
|
94
|
+
server.should_not_receive(:process)
|
95
|
+
post "/blaf", "[]"
|
96
|
+
status.should == 404
|
97
|
+
content_type.should == "text/plain"
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
describe "with no content type" do
|
102
|
+
it "forwards the message param onto the server" do
|
103
|
+
server.should_receive(:process).with({"channel" => "/foo"}, false).and_yield []
|
104
|
+
post "/bayeux", :message => '{"channel":"/foo"}'
|
105
|
+
end
|
106
|
+
|
107
|
+
it "returns the server's response as JSON" do
|
108
|
+
server.stub(:process).and_yield ["channel" => "/meta/handshake"]
|
109
|
+
post "/bayeux", :message => '[]'
|
110
|
+
status.should == 200
|
111
|
+
content_type.should == "application/json"
|
112
|
+
json.should == ["channel" => "/meta/handshake"]
|
113
|
+
end
|
114
|
+
|
115
|
+
it "returns a 400 response if malformed JSON is given" do
|
116
|
+
server.should_not_receive(:process)
|
117
|
+
post "/bayeux", :message => "[}"
|
118
|
+
status.should == 400
|
119
|
+
content_type.should == "text/plain"
|
120
|
+
end
|
121
|
+
|
122
|
+
it "returns a 404 if the path is not matched" do
|
123
|
+
server.should_not_receive(:process)
|
124
|
+
post "/blaf", :message => "[]"
|
125
|
+
status.should == 404
|
126
|
+
content_type.should == "text/plain"
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
describe "GET requests" do
|
132
|
+
let(:params) {{:message => '{"channel":"/foo"}', :jsonp => "callback"}}
|
133
|
+
|
134
|
+
describe "with valid params" do
|
135
|
+
before do
|
136
|
+
server.should_receive(:flush_connection).with("channel" => "/foo")
|
137
|
+
end
|
138
|
+
|
139
|
+
it "forwards the message param onto the server" do
|
140
|
+
server.should_receive(:process).with({"channel" => "/foo"}, false).and_yield []
|
141
|
+
get "/bayeux", params
|
142
|
+
end
|
143
|
+
|
144
|
+
it "returns the server's response as JavaScript" do
|
145
|
+
server.stub(:process).and_yield ["channel" => "/meta/handshake"]
|
146
|
+
get "/bayeux", params
|
147
|
+
status.should == 200
|
148
|
+
content_type.should == "text/javascript"
|
149
|
+
body.should == 'callback([{"channel":"/meta/handshake"}]);'
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
describe "with an unknown path" do
|
154
|
+
it "returns a 404" do
|
155
|
+
server.should_not_receive(:process)
|
156
|
+
get "/blah", params
|
157
|
+
status.should == 404
|
158
|
+
content_type.should == "text/plain"
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
describe "missing jsonp" do
|
163
|
+
before do
|
164
|
+
params.delete(:jsonp)
|
165
|
+
server.should_receive(:flush_connection)
|
166
|
+
end
|
167
|
+
|
168
|
+
it "returns the server's response using the default callback" do
|
169
|
+
server.stub(:process).and_yield ["channel" => "/meta/handshake"]
|
170
|
+
get "/bayeux", params
|
171
|
+
status.should == 200
|
172
|
+
content_type.should == "text/javascript"
|
173
|
+
body.should == 'jsonpcallback([{"channel":"/meta/handshake"}]);'
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
shared_examples_for "bad GET request" do
|
178
|
+
it "does not call the server" do
|
179
|
+
server.should_not_receive(:process)
|
180
|
+
get "/bayeux", params
|
181
|
+
end
|
182
|
+
|
183
|
+
it "returns a 400 response" do
|
184
|
+
get "/bayeux", params
|
185
|
+
status.should == 400
|
186
|
+
content_type.should == "text/plain"
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
describe "with malformed JSON" do
|
191
|
+
before { params[:message] = "[}" }
|
192
|
+
it_should_behave_like "bad GET request"
|
193
|
+
end
|
194
|
+
|
195
|
+
describe "missing message" do
|
196
|
+
before { params.delete(:message) }
|
197
|
+
it_should_behave_like "bad GET request"
|
198
|
+
end
|
199
|
+
|
200
|
+
describe "for the client script" do
|
201
|
+
it "returns the client script" do
|
202
|
+
get "/bayeux.js"
|
203
|
+
status.should == 200
|
204
|
+
content_type.should == "text/javascript"
|
205
|
+
body.should =~ /function\(\)\{/
|
206
|
+
end
|
207
|
+
end
|
208
|
+
end
|
209
|
+
end
|