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.
- checksums.yaml +7 -0
- data/MIT-LICENSE +20 -0
- data/README.md +126 -0
- data/Rakefile +12 -0
- data/lib/rspec/cramp.rb +7 -0
- data/lib/rspec/cramp/extensions/cramp/action.rb +17 -0
- data/lib/rspec/cramp/matchers/respond_with.rb +18 -0
- data/lib/rspec/cramp/mock_response.rb +151 -0
- data/lib/rspec/cramp/shared_context.rb +126 -0
- data/rspec-cramp.gemspec +22 -0
- data/spec/examples/basic_spec.rb +60 -0
- data/spec/examples/errors_spec.rb +26 -0
- data/spec/examples/headers_spec.rb +25 -0
- data/spec/examples/low_level_spec.rb +26 -0
- data/spec/examples/sample_actions.rb +42 -0
- data/spec/rspec_cramp_spec.rb +513 -0
- data/spec/spec_helper.rb +12 -0
- data/spec/tests.log +1417 -0
- metadata +129 -0
data/rspec-cramp.gemspec
ADDED
@@ -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
|