rspec-crampy 0.1.3

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.
@@ -0,0 +1,22 @@
1
+ Gem::Specification.new do |s|
2
+ s.platform = Gem::Platform::RUBY
3
+ s.name = 'rspec-crampy'
4
+ s.version = '0.1.3'
5
+ s.summary = 'RSpec helpers for Crampy.'
6
+ s.description = 'RSpec extension library for Cramp.'
7
+
8
+ s.author = 'Martin Bilski'
9
+ s.email = 'gyamtso@gmail.com'
10
+ s.homepage = 'https://github.com/Vasfed/rspec-cramp'
11
+
12
+ s.add_dependency('rack', '~> 1.3')
13
+ s.add_dependency('eventmachine', '~> 1.0.0')
14
+ s.add_dependency('http_router', '>= 0.11')
15
+ s.add_dependency('rspec', '~> 2.6')
16
+ s.files = Dir['README.md', 'MIT-LICENSE', '*.gemspec', 'Rakefile', 'lib/**/*', 'spec/**/*']
17
+ s.has_rdoc = false
18
+
19
+ s.add_development_dependency "rake"
20
+
21
+ s.require_path = 'lib'
22
+ end
@@ -0,0 +1,60 @@
1
+ require File.join(File.dirname(__FILE__), "../spec_helper")
2
+ require File.join(File.dirname(__FILE__), "sample_actions")
3
+
4
+ # Decorate your spec with :cramp => true
5
+ describe HelloWorld, :cramp => true do
6
+
7
+ # You need to define this method
8
+ def app
9
+ HelloWorld # Here goes your cramp application, action or http routes.
10
+ end
11
+
12
+ # Matching on status code.
13
+ it "should respond to a GET request" do
14
+ get("/").should respond_with :status => :ok # Matches responses from 200 to 299.
15
+ get("/").should respond_with :status => 200 # Matches only 200.
16
+ get("/").should respond_with :status => "200" # Same as above.
17
+ get("/").should respond_with :status => /^2.*/ # Matches response codes starting with 2.
18
+ get("/").should_not respond_with :status => :error # Matches any HTTP error.
19
+ end
20
+
21
+ # Matching on response body.
22
+ it "should respond with text starting with 'Hello'" do
23
+ get("/").should respond_with :body => /^Hello.*/
24
+ end
25
+ it "should respond with 'Hello, world!'" do
26
+ get("/").should respond_with :body => "Hello, world!"
27
+ end
28
+
29
+ # Matching on response headers.
30
+ it "should respond with html" do
31
+ get("/").should respond_with :headers => {"Content-Type" => "text/html"}
32
+ get("/").should_not respond_with :headers => {"Content-Type" => "text/plain"}
33
+ get("/").should_not respond_with :headers => {"Unexpected-Header" => /.*/}
34
+ end
35
+
36
+ # Matching using lambdas.
37
+ it "should match my sophisticated custom matchers" do
38
+ # Entire headers.
39
+ status_check = lambda {|status| status.between?(200, 299)}
40
+ body_check = lambda {|body| body =~ /.*el.*/}
41
+ headers_check = lambda {|headers| true} # Any headers will do.
42
+ get("/").should respond_with :status => status_check, :body => body_check, :headers => headers_check
43
+ # Header value.
44
+ get("/").should respond_with :headers => {"Content-Type" => lambda {|value| value == "text/html"}}
45
+ get("/").should_not respond_with :headers => {"Content-Type" => lambda {|value| value == "text/plain"}}
46
+ end
47
+
48
+ # Supports POST/GET/PUT/DELETE and you don't have to use the matcher.
49
+ it "should work without a matcher" do
50
+ get "/"
51
+ post "/"
52
+ put "/"
53
+ delete "/"
54
+ end
55
+
56
+ # Request params & custom headers.
57
+ it "should accept my params" do
58
+ post("/", :params => {:text => "whatever"}, :headers => {"Custom-Header" => "blah"})
59
+ end
60
+ end
@@ -0,0 +1,26 @@
1
+ require File.join(File.dirname(__FILE__), "../spec_helper")
2
+ require File.join(File.dirname(__FILE__), "sample_actions")
3
+
4
+ describe "Error handling", :cramp => true do
5
+ def app
6
+ HttpRouter.new do
7
+ add('/error_before_start').to ErrorBeforeStart
8
+ add('/error_on_start').to ErrorOnStart
9
+ add('/error_on_finish').to ErrorOnFinish
10
+ end
11
+ end
12
+
13
+ it "should handle error in before_start handler" do
14
+ get("/error_before_start").should respond_with :status => 500
15
+ end
16
+
17
+ it "should handle error in on_start handler" do
18
+ # Headers were already sent by the time the exception was raised.
19
+ get("/error_on_start").should respond_with :body => /.*Error in on_start.*/, :status => 200
20
+ end
21
+
22
+ it "should handle error in on_finish handler" do
23
+ # Headers were already sent by the time the exception was raised.
24
+ get("/error_on_finish").should respond_with :body => /.*Error in on_finish.*/, :status => 200
25
+ end
26
+ end
@@ -0,0 +1,25 @@
1
+ require File.join(File.dirname(__FILE__), "../spec_helper")
2
+ require File.join(File.dirname(__FILE__), "sample_actions")
3
+
4
+ describe CustomHeader, :cramp => true do
5
+ def app
6
+ CustomHeader
7
+ end
8
+
9
+ it "should render the value of the custom header" do
10
+ get("/", :headers => {"Custom-Header" => "SAMPLE VALUE"}).should respond_with :body => /^SAMPLE VALUE$/
11
+ get("/", :headers => {"Custom-Header" => "SAMPLE VALUE"}).should respond_with :body => "SAMPLE VALUE"
12
+ end
13
+
14
+ it "should include the custom header in response headers" do
15
+ # Exact match using string & regex.
16
+ get("/", :headers => {"Custom-Header" => "SAMPLE VALUE"}).should respond_with :headers => {"Custom-Header" => "SAMPLE VALUE"}
17
+ get("/", :headers => {"Custom-Header" => "SAMPLE VALUE"}).should respond_with :headers => {"Custom-Header" => /^SAMPLE VALUE$/}
18
+
19
+ # Header field names are case insensitive - use regex match.
20
+ get("/", :headers => {"Custom-Header" => "SAMPLE VALUE"}).should respond_with :headers => {/Custom\-Header/i => "SAMPLE VALUE"}
21
+
22
+ # Negative match.
23
+ get("/", :headers => {"Custom-Header" => "SAMPLE VALUE"}).should_not respond_with :headers => {"Custom-Header" => "ANOTHER VALUE"}
24
+ end
25
+ end
@@ -0,0 +1,26 @@
1
+ require File.join(File.dirname(__FILE__), "../spec_helper")
2
+ require File.join(File.dirname(__FILE__), "sample_actions")
3
+
4
+ describe HelloWorld, :cramp => true do
5
+ def app
6
+ HelloWorld
7
+ end
8
+
9
+ it "should respond to a GET request" do
10
+ get("/") do |response|
11
+ response.status.should == 200
12
+ response.headers.should have_key "Content-Type"
13
+ response.should be_matching :status => :ok
14
+ stop # This is important.
15
+ end
16
+ end
17
+ it "should match the body" do
18
+ get("/") do |response|
19
+ response.read_body do
20
+ response.body.should include "Hello, world!" # MockResponse::body returns an Array.
21
+ response.should be_matching :body => "Hello, world!"
22
+ # Note: no call to stop here.
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,42 @@
1
+ class HelloWorld < Cramp::Action
2
+ def start
3
+ render "Hello, world!"
4
+ finish
5
+ end
6
+ end
7
+
8
+ class ErrorOnStart < Cramp::Action
9
+ on_start :raise_error
10
+ def raise_error
11
+ raise "Error in on_start"
12
+ end
13
+ end
14
+
15
+ class ErrorBeforeStart < Cramp::Action
16
+ before_start :raise_error
17
+ def raise_error
18
+ raise "Error in before_start"
19
+ end
20
+ end
21
+
22
+ class ErrorOnFinish < Cramp::Action
23
+ on_start :just_finish
24
+ on_finish :raise_error
25
+ def just_finish
26
+ finish
27
+ end
28
+ def raise_error
29
+ raise "Error in on_finish"
30
+ end
31
+ end
32
+
33
+ class CustomHeader < Cramp::Action
34
+ on_start :render_custom_header
35
+ def respond_with
36
+ [200, {'Content-Type' => 'text/html', 'Custom-Header' => @env["HTTP_CUSTOM_HEADER"]}]
37
+ end
38
+ def render_custom_header
39
+ render @env["HTTP_CUSTOM_HEADER"]
40
+ finish
41
+ end
42
+ end
@@ -0,0 +1,513 @@
1
+ require File.join(File.dirname(__FILE__), "spec_helper")
2
+
3
+ module Cramp
4
+
5
+ describe "rspec-cramp" do
6
+ class SuccessfulResponse < Cramp::Action
7
+ on_start :render_and_finish
8
+ def render_and_finish
9
+ render "ok"
10
+ finish
11
+ end
12
+ end
13
+
14
+ class HelloWorldResponse < Cramp::Action
15
+ on_start :render_and_finish
16
+ def render_and_finish
17
+ render "Hello, world"
18
+ finish
19
+ end
20
+ end
21
+
22
+ class MultipartResponse < Cramp::Action
23
+ on_start :render_and_finish
24
+ def render_and_finish
25
+ render "part1"
26
+ render "part2"
27
+ finish
28
+ end
29
+ end
30
+
31
+ class ErrorResponse < Cramp::Action
32
+ on_start :just_finish
33
+ def respond_with
34
+ [500, {'Content-Type' => 'text/html'}]
35
+ end
36
+ def just_finish
37
+ finish
38
+ end
39
+ end
40
+
41
+
42
+ class RaiseBeforeStart < Cramp::Action
43
+ before_start :raise_error
44
+ def raise_error
45
+ raise "Error in before_start"
46
+ end
47
+ end
48
+
49
+ class RaiseOnStart < Cramp::Action
50
+ on_start :raise_error
51
+ def raise_error
52
+ raise "Error in on_start"
53
+ end
54
+ end
55
+
56
+ class RaiseOnFinish < Cramp::Action
57
+ on_start :just_finish
58
+ on_finish :raise_error
59
+ def just_finish
60
+ finish
61
+ end
62
+ def raise_error
63
+ raise "Error in on_finish"
64
+ end
65
+ end
66
+
67
+ class NoResponse < Cramp::Action
68
+ on_start :noop
69
+ def noop
70
+ end
71
+ end
72
+
73
+ class CustomHeaders < Cramp::Action
74
+ on_start :ok_and_finish
75
+ def respond_with
76
+ [200, {'Extra-Header' => 'ABCD', 'Another-One' => 'QWERTY'}]
77
+ end
78
+ def ok_and_finish
79
+ render "ok"
80
+ finish
81
+ end
82
+ end
83
+
84
+ class SseAction < Cramp::Action
85
+ self.transport = :sse
86
+ periodic_timer :write_hello, :every => 0.5
87
+ def write_hello
88
+ @num ||= 1
89
+ render "Hello #{@num}"
90
+ @num += 1
91
+ end
92
+ end
93
+
94
+ class SseNoRenderAction < Cramp::Action
95
+ self.transport = :sse
96
+ end
97
+
98
+ class RequestHeaders < Cramp::Action
99
+ on_start :render_request_headers
100
+ def render_request_headers
101
+ response = http_headers.inject("Request headers:\n") {|acc, (k,v)| acc << "#{k}: #{v}\n"; acc}
102
+ render response
103
+ finish
104
+ end
105
+
106
+ private
107
+
108
+ def http_headers
109
+ @env.inject({}){|acc, (k,v)| acc[$1.upcase] = v if k =~ /^http_(.*)/i; acc}
110
+ end
111
+ end
112
+
113
+ class RequestParams < Cramp::Action
114
+ on_start :render_and_finish
115
+ def render_and_finish
116
+ render params[:text]
117
+ finish
118
+ end
119
+ end
120
+
121
+
122
+ def routes
123
+ HttpRouter.new do
124
+ add('/200').to SuccessfulResponse
125
+ add('/hello_world').to HelloWorldResponse
126
+ add('/multipart').to MultipartResponse
127
+ add('/500').to ErrorResponse
128
+ add('/no_response').to NoResponse
129
+ add('/custom_header').to CustomHeaders
130
+ add('/raise_before_start').to RaiseBeforeStart
131
+ add('/raise_on_start').to RaiseOnStart
132
+ add('/raise_on_finish').to RaiseOnFinish
133
+ add('/sse').to SseAction
134
+ add('/sse_no_render').to SseNoRenderAction
135
+ get('/get_only').to SuccessfulResponse
136
+ post('/post_only').to SuccessfulResponse
137
+ put('/put_only').to SuccessfulResponse
138
+ delete('/delete_only').to SuccessfulResponse
139
+ add('/request_headers').to RequestHeaders
140
+ add('/request_params').to RequestParams
141
+ end
142
+ end
143
+
144
+ describe "'respond_with' matcher", :cramp => true do
145
+ def app
146
+ routes
147
+ end
148
+
149
+ it "should raise error for unsupported match options" do
150
+ lambda { get("/200").should respond_with :whatever => "ABC" }.should raise_error
151
+ end
152
+
153
+ shared_examples_for "async_request" do |method|
154
+
155
+ describe "timeout" do
156
+ it "- timeout when no response" do
157
+ lambda { send(method, "/no_response") }.should raise_error Timeout::Error
158
+ end
159
+ it "- allow the timeout to be defined by the user" do
160
+ lambda do
161
+ timeout(2) do
162
+ lambda {send(method, "/no_response", {:timeout => 1}) }.should raise_error Timeout::Error
163
+ end
164
+ end.should_not raise_error Timeout::Error
165
+ end
166
+ end
167
+
168
+ describe "exact match on response status" do
169
+ it "should match successful response" do
170
+ send(method, "/200").should respond_with :status => 200
171
+ send(method, "/200").should respond_with :status => "200"
172
+ send(method, "/200").should respond_with :status => :ok
173
+ end
174
+ it "should match error response" do
175
+ send(method, "/500").should respond_with :status => 500
176
+ send(method, "/500").should respond_with :status => "500"
177
+ send(method, "/500").should respond_with :status => :error
178
+ send(method, "/500").should_not respond_with :status => 200
179
+ send(method, "/500").should_not respond_with :status => "200"
180
+ send(method, "/500").should_not respond_with :status => :ok
181
+ end
182
+ it "should match non-async errors from http router" do
183
+ send(method, "/404").should respond_with :status => 404
184
+ send(method, "/404").should respond_with :status => "404"
185
+ end
186
+ end
187
+
188
+ describe "regex match on response status" do
189
+ it "should match successful response" do
190
+ send(method, "/200").should respond_with :status => /^2.*/
191
+ end
192
+ it "should match error response" do
193
+ send(method, "/500").should respond_with :status => /^5.*/
194
+ send(method, "/500").should_not respond_with :status => /^2.*/
195
+ end
196
+ it "should match non-sync errors from http router" do
197
+ send(method, "/404").should respond_with :status => /^4.*/
198
+ end
199
+ end
200
+
201
+ describe "lambda match on response status" do
202
+ it "should match when true" do
203
+ send(method, "/200").should respond_with :status => lambda {|status| status == 200}
204
+ end
205
+ it "should not match when false" do
206
+ send(method, "/200").should_not respond_with :status => lambda {|status| status == 500}
207
+ end
208
+ end
209
+
210
+ describe "exact match on response header values" do
211
+ it "should match with one expected header" do
212
+ send(method, "/custom_header").should respond_with :headers => {"Extra-Header" => "ABCD"}
213
+ end
214
+ it "should match all with two expected headers" do
215
+ send(method, "/custom_header").should respond_with :headers => {"Extra-Header" => "ABCD", "Another-One" => "QWERTY"}
216
+ end
217
+ it "should not match if value does not match" do
218
+ send(method, "/custom_header").should_not respond_with :headers => {"Extra-Header" => "1234"}
219
+ end
220
+ it "should not match iff the header isn't there" do
221
+ send(method, "/custom_header").should_not respond_with :headers => {"Non-Existent-One" => "QWERTY"}
222
+ end
223
+ end
224
+
225
+ describe "regex match on response header values" do
226
+ it "should match with one expected header" do
227
+ send(method, "/custom_header").should respond_with :headers => {"Extra-Header" => /^ABCD$/}
228
+ end
229
+ it "should match all with two expected headers" do
230
+ send(method, "/custom_header").should respond_with :headers => {"Extra-Header" => /^ABCD$/, "Another-One" => /^QWERTY$/}
231
+ end
232
+ it "should not match if value does not match" do
233
+ send(method, "/custom_header").should_not respond_with :headers => {"Extra-Header" => /^1234$/}
234
+ end
235
+ it "should not match iff the header isn't there" do
236
+ send(method, "/custom_header").should_not respond_with :headers => {"Non-Existent-One" => /^QWERTY$/}
237
+ end
238
+ end
239
+
240
+ describe "lambda match on response header values" do
241
+ it "should match when true" do
242
+ send(method, "/custom_header").should respond_with :headers => {"Extra-Header" => lambda {|value| value == "ABCD"}}
243
+ end
244
+ it "should not match when false" do
245
+ send(method, "/custom_header").should_not respond_with :headers => {"Extra-Header" => lambda {|value| value == "WRONG"}}
246
+ end
247
+ end
248
+
249
+ describe "regex match on response header fields" do
250
+ it "should match with one expected header" do
251
+ send(method, "/custom_header").should respond_with :headers => {/Extra\-Header/i => /^ABCD$/}
252
+ end
253
+ it "should match all with two expected headers" do
254
+ send(method, "/custom_header").should respond_with :headers => {/Extra\-Header/i => /^ABCD$/, "Another-One" => /^QWERTY$/}
255
+ end
256
+ it "should not match if value does not match" do
257
+ send(method, "/custom_header").should_not respond_with :headers => {/Extra\-Header/i => /^1234$/}
258
+ end
259
+ it "should not match iff the header isn't there" do
260
+ send(method, "/custom_header").should_not respond_with :headers => {/Non\-Existent\-One/i => /^QWERTY$/}
261
+ end
262
+ end
263
+
264
+ describe "lambda match on entire header " do
265
+ it "should match when true" do
266
+ match_headers = lambda do |headers|
267
+ headers.find {|(k, v)| k == "Extra-Header" && v == "ABCD"}
268
+ end
269
+ send(method, "/custom_header").should respond_with :headers => match_headers
270
+ end
271
+ it "should not match when false" do
272
+ match_headers = lambda do |headers|
273
+ headers.find {|(k, v)| k == "Non-Existent-One" && v == "QWERTY"}
274
+ end
275
+ send(method, "/custom_header").should_not respond_with :headers => match_headers
276
+ end
277
+ end
278
+
279
+ # FIXME How to handle a situation where nothing is rendered? get reads the body...
280
+
281
+ describe "exact match on response body" do
282
+ it "should match with successful response" do
283
+ send(method, "/200").should respond_with :body => "ok"
284
+ send(method, "/200").should_not respond_with :body => "wrong"
285
+ end
286
+ it "should not load body on error response" do
287
+ # TODO Not sure about this behaviour. What do you think? Use "Something went wrong"?
288
+ send(method, "/500").should respond_with :body => /.*Cramp::Body.*/
289
+ end
290
+ it "should match non-async response from http router" do
291
+ send(method, "/404").should respond_with :body => "Something went wrong"
292
+ end
293
+ end
294
+ describe "regex match on response body" do
295
+ it "should match the body" do
296
+ send(method, "/hello_world").should respond_with :body => /.*Hello.*/
297
+ send(method, "/hello_world").should_not respond_with :body => /.*incorrect.*/
298
+ end
299
+ end
300
+
301
+ describe "lambda match on response body" do
302
+ it "should match when true" do
303
+ send(method, "/hello_world").should respond_with :body => lambda {|body| body =~ /.*Hello.*/}
304
+ end
305
+ it "should not match when false" do
306
+ send(method, "/hello_world").should_not respond_with :body => lambda {|body| body =~ /.*incorrect.*/}
307
+ end
308
+ end
309
+
310
+ describe "exact match on multipart response body" do
311
+ it "should match with successful response" do
312
+ send(method, "/multipart", :max_chunks => 2).should respond_with :body => "part1part2"
313
+ send(method, "/multipart", :max_chunks => 2).should_not respond_with :body => "whatever"
314
+ end
315
+ end
316
+ describe "regex match on multipart response body" do
317
+ it "should match the body" do
318
+ send(method, "/multipart", :max_chunks => 2).should respond_with :body => /.*part.*/
319
+ send(method, "/multipart", :max_chunks => 2).should_not respond_with :body => /.*incorrect.*/
320
+ end
321
+ end
322
+
323
+ describe "exact match on response body chunks" do
324
+ it "should match with successful response" do
325
+ send(method, "/multipart", :max_chunks => 2).should respond_with :chunks => ["part1", "part2"]
326
+ send(method, "/multipart", :max_chunks => 2).should_not respond_with :chunks => ["whatever1", "whatever2"]
327
+ end
328
+ end
329
+ describe "regex match on response body chunks" do
330
+ # Note: In theory, an exact match would also work but because the content also contains an event-id,
331
+ # it is not practical.
332
+ it "should match with successful response" do
333
+ send(method, "/multipart", :max_chunks => 2).should respond_with :chunks => [/part1/, /part2/]
334
+ send(method, "/multipart", :max_chunks => 2).should_not respond_with :chunks => [/whatever1/, /whatever2/]
335
+ end
336
+ end
337
+
338
+ describe "lambda match on response body chunks" do
339
+ it "should match when true" do
340
+ send(method, "/multipart", :max_chunks => 2).should respond_with(:chunks => lambda do |chunks|
341
+ chunks[0] =~ /part1/ && chunks[1] =~ /part2/
342
+ end)
343
+ end
344
+ it "should not match when false" do
345
+ send(method, "/multipart", :max_chunks => 2).should_not respond_with(:chunks => lambda do |chunks|
346
+ chunks[0] =~ /whatever1/ || chunks[1] =~ /whatever2/
347
+ end)
348
+ end
349
+ end
350
+
351
+
352
+ describe "multiple conditions" do
353
+ it "should match on status and body" do
354
+ send(method, "/200").should respond_with :status => :ok, :body => "ok"
355
+ send(method, "/200").should_not respond_with :status => :ok, :body => "incorrect"
356
+ send(method, "/200").should_not respond_with :status => :error, :body => "ok"
357
+ end
358
+ end
359
+
360
+ describe "sse support" do
361
+ # Note: In theory, xact match would also work but because the content also contains an event-id,
362
+ # it is not practical.
363
+ it "should match with successful response" do
364
+ send(method, "/sse", :max_chunks => 2).should respond_with :chunks => [/^data: Hello 1.*/, /^data: Hello 2.*/]
365
+ send(method, "/sse", :max_chunks => 2).should_not respond_with :chunks => [/.*Incorrect 1.*/, /.*Incorrect 2.*/]
366
+ end
367
+
368
+ it "should not wait for body if it is skipped explicitly" do
369
+ lambda { send(method, "/sse_no_render", :max_chunks => 0) }.should_not raise_error Timeout::Error
370
+ send(method, "/sse_no_render", :max_chunks => 0).should respond_with :status => 200, :body => ""
371
+ end
372
+ end
373
+
374
+ it "should correctly handle exception in the callbacks" do
375
+ send(method, "/raise_on_start").should respond_with :status => 200 # Unfortunately, the headers have been already sent.
376
+ end
377
+
378
+ describe "when an action raises an exception" do
379
+ it "should handle error in before_start handler" do
380
+ get("/raise_before_start").should respond_with :status => 500
381
+ end
382
+
383
+ it "should handle error in on_start handler" do
384
+ # Headers were already sent by the time the exception was raised.
385
+ get("/raise_on_start").should respond_with :body => /.*Error in on_start.*/, :status => 200
386
+ end
387
+
388
+ it "should handle error in on_finish handler" do
389
+ # Headers were already sent by the time the exception was raised.
390
+ get("/raise_on_finish").should respond_with :body => /.*Error in on_finish.*/, :status => 200
391
+ end
392
+ end
393
+
394
+ it "should support custom request headers" do
395
+ get("/request_headers", :headers => {"Custom1" => "ABC", "Custom2" => "DEF"}).should respond_with :body => /.*^Custom1: ABC$.*/i
396
+ get("/request_headers", :headers => {"Custom1" => "ABC", "Custom2" => "DEF"}).should respond_with :body => /.*^Custom2: DEF$.*/i
397
+ end
398
+
399
+ it "should support request params" do
400
+ get("/request_params", :params => {:text => "Hello, world!"}).should respond_with :body => "Hello, world!"
401
+ end
402
+
403
+ it "should pass body chunks to the block" do
404
+ actual_chunks = []
405
+ get("/sse", :max_chunks => 2).should respond_with(:chunks => lambda {|chunks| actual_chunks = chunks; true})
406
+ actual_chunks.should have(2).elements
407
+ actual_chunks[0].should include "Hello 1"
408
+ actual_chunks[1].should include "Hello 2"
409
+ end
410
+ end
411
+
412
+ describe "GET request" do
413
+ it_should_behave_like "async_request", :get
414
+
415
+ it "should be able to access paths accessible only with GET" do
416
+ get("/get_only").should respond_with :status => :ok
417
+ end
418
+ it "should not be able to access paths non-accessible with GET" do
419
+ get("/post_only").should respond_with :status => 405
420
+ end
421
+ end
422
+
423
+ describe "POST request" do
424
+ it_should_behave_like "async_request", :post
425
+
426
+ it "should be able to access paths accessible only with POST" do
427
+ post("/post_only").should respond_with :status => :ok
428
+ end
429
+ it "should not be able to access paths non-accessible with POST" do
430
+ post("/get_only").should respond_with :status => 405
431
+ end
432
+ end
433
+
434
+ describe "DELETE request" do
435
+ it_should_behave_like "async_request", :delete
436
+
437
+ it "should be able to access paths accessible only with DELETE" do
438
+ delete("/delete_only").should respond_with :status => :ok
439
+ end
440
+ it "should not be able to access paths non-accessible with DELETE" do
441
+ delete("/post_only").should respond_with :status => 405
442
+ end
443
+ end
444
+
445
+ describe "PUT request" do
446
+ it_should_behave_like "async_request", :put
447
+
448
+ it "should be able to access paths accessible only with PUT" do
449
+ put("/put_only").should respond_with :status => :ok
450
+ end
451
+ it "should not be able to access paths non-accessible with PUT" do
452
+ put("/post_only").should respond_with :status => 405
453
+ end
454
+ end
455
+ end
456
+
457
+ # FIXME Better failure message for response matcher.
458
+ describe "'be_matching' matcher", :cramp => true do
459
+ def app
460
+ routes
461
+ end
462
+
463
+ # Note: Only basic specs here because respond_with matcher uses the same code and is extensively tested.
464
+
465
+ it "should support expectations on response status" do
466
+ get("/200") do |response|
467
+ response.should be_matching :status => 200
468
+ response.should be_matching :status => :ok
469
+ response.should_not be_matching :status => 500
470
+ stop
471
+ end
472
+ end
473
+ it "should support expectations on response body" do
474
+ get("/200") do |response|
475
+ response.read_body do
476
+ response.body.should == ["ok"]
477
+ response.body.should_not == ["whatever"]
478
+ end
479
+ end
480
+ end
481
+ it "should support array access" do
482
+ get("/200") do |response|
483
+ response[0].should == 200
484
+ response[1].should be_a Hash
485
+ response[2].should be_a Cramp::Body
486
+ response[-1].should be_a Cramp::Body
487
+ stop
488
+ end
489
+ end
490
+ it "should handle exceptions in block" do
491
+ lambda { get("/404") do |response|
492
+ raise "this is an error"
493
+ end }.should raise_error
494
+ end
495
+
496
+ describe "improper body access" do
497
+ it "should warn user if body is accessed via an attribute without read_body" do
498
+ get("/200") do |response|
499
+ lambda { response.body }.should raise_error
500
+ stop
501
+ end
502
+ end
503
+ it "should warn user if body is accessed via an attribute outside read_body" do
504
+ get("/200") do |response|
505
+ response.read_body
506
+ lambda { response.body }.should raise_error
507
+ stop
508
+ end
509
+ end
510
+ end
511
+ end
512
+ end
513
+ end