grape 0.7.0 → 0.8.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.
Potentially problematic release.
This version of grape might be problematic. Click here for more details.
- checksums.yaml +5 -13
- data/.rubocop.yml +6 -6
- data/.travis.yml +11 -2
- data/CHANGELOG.md +23 -1
- data/Gemfile +11 -10
- data/Guardfile +5 -6
- data/README.md +194 -13
- data/UPGRADING.md +3 -3
- data/grape.gemspec +1 -1
- data/grape.png +0 -0
- data/lib/grape/api.rb +21 -13
- data/lib/grape/endpoint.rb +31 -13
- data/lib/grape/exceptions/validation.rb +2 -2
- data/lib/grape/locale/en.yml +2 -0
- data/lib/grape/middleware/auth/oauth2.rb +69 -65
- data/lib/grape/middleware/error.rb +4 -2
- data/lib/grape/middleware/formatter.rb +2 -2
- data/lib/grape/middleware/versioner/accept_version_header.rb +1 -1
- data/lib/grape/middleware/versioner/header.rb +3 -3
- data/lib/grape/util/hash_stack.rb +1 -1
- data/lib/grape/validations.rb +22 -8
- data/lib/grape/validations/default.rb +1 -1
- data/lib/grape/validations/exactly_one_of.rb +26 -0
- data/lib/grape/validations/mutual_exclusion.rb +25 -0
- data/lib/grape/validations/presence.rb +1 -1
- data/lib/grape/validations/regexp.rb +2 -1
- data/lib/grape/validations/values.rb +7 -1
- data/lib/grape/version.rb +1 -1
- data/spec/grape/api_spec.rb +390 -333
- data/spec/grape/endpoint_spec.rb +129 -99
- data/spec/grape/entity_spec.rb +47 -27
- data/spec/grape/exceptions/invalid_formatter_spec.rb +1 -1
- data/spec/grape/exceptions/invalid_versioner_option_spec.rb +1 -1
- data/spec/grape/exceptions/missing_mime_type_spec.rb +2 -2
- data/spec/grape/exceptions/missing_option_spec.rb +1 -1
- data/spec/grape/exceptions/unknown_options_spec.rb +1 -1
- data/spec/grape/exceptions/unknown_validator_spec.rb +1 -1
- data/spec/grape/middleware/auth/basic_spec.rb +3 -3
- data/spec/grape/middleware/auth/digest_spec.rb +4 -4
- data/spec/grape/middleware/auth/oauth2_spec.rb +11 -11
- data/spec/grape/middleware/base_spec.rb +9 -9
- data/spec/grape/middleware/error_spec.rb +4 -4
- data/spec/grape/middleware/exception_spec.rb +17 -17
- data/spec/grape/middleware/formatter_spec.rb +38 -38
- data/spec/grape/middleware/versioner/accept_version_header_spec.rb +11 -11
- data/spec/grape/middleware/versioner/header_spec.rb +39 -39
- data/spec/grape/middleware/versioner/param_spec.rb +10 -10
- data/spec/grape/middleware/versioner/path_spec.rb +7 -7
- data/spec/grape/middleware/versioner_spec.rb +4 -4
- data/spec/grape/path_spec.rb +6 -6
- data/spec/grape/util/hash_stack_spec.rb +22 -22
- data/spec/grape/validations/coerce_spec.rb +34 -34
- data/spec/grape/validations/default_spec.rb +16 -16
- data/spec/grape/validations/exactly_one_of_spec.rb +71 -0
- data/spec/grape/validations/mutual_exclusion_spec.rb +61 -0
- data/spec/grape/validations/presence_spec.rb +34 -34
- data/spec/grape/validations/regexp_spec.rb +11 -4
- data/spec/grape/validations/values_spec.rb +34 -20
- data/spec/grape/validations_spec.rb +300 -147
- data/spec/shared/versioning_examples.rb +18 -18
- data/spec/spec_helper.rb +2 -1
- metadata +81 -38
data/lib/grape/validations.rb
CHANGED
@@ -19,7 +19,7 @@ module Grape
|
|
19
19
|
def validate!(params)
|
20
20
|
attributes = AttributesIterator.new(self, @scope, params)
|
21
21
|
attributes.each do |resource_params, attr_name|
|
22
|
-
if @required || resource_params.
|
22
|
+
if @required || resource_params.key?(attr_name)
|
23
23
|
validate_param!(attr_name, resource_params)
|
24
24
|
end
|
25
25
|
end
|
@@ -43,8 +43,6 @@ module Grape
|
|
43
43
|
end
|
44
44
|
end
|
45
45
|
|
46
|
-
private
|
47
|
-
|
48
46
|
def self.convert_to_short_name(klass)
|
49
47
|
ret = klass.name.gsub(/::/, '/')
|
50
48
|
.gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2')
|
@@ -132,6 +130,14 @@ module Grape
|
|
132
130
|
push_declared_params(attrs)
|
133
131
|
end
|
134
132
|
|
133
|
+
def mutually_exclusive(*attrs)
|
134
|
+
validates(attrs, mutual_exclusion: true)
|
135
|
+
end
|
136
|
+
|
137
|
+
def exactly_one_of(*attrs)
|
138
|
+
validates(attrs, exactly_one_of: true)
|
139
|
+
end
|
140
|
+
|
135
141
|
def group(*attrs, &block)
|
136
142
|
requires(*attrs, &block)
|
137
143
|
end
|
@@ -152,11 +158,12 @@ module Grape
|
|
152
158
|
|
153
159
|
def use(*names)
|
154
160
|
named_params = @api.settings[:named_params] || {}
|
161
|
+
options = names.last.is_a?(Hash) ? names.pop : {}
|
155
162
|
names.each do |name|
|
156
163
|
params_block = named_params.fetch(name) do
|
157
164
|
raise "Params :#{name} not found!"
|
158
165
|
end
|
159
|
-
|
166
|
+
instance_exec(options, ¶ms_block)
|
160
167
|
end
|
161
168
|
end
|
162
169
|
alias_method :use_scope, :use
|
@@ -167,6 +174,10 @@ module Grape
|
|
167
174
|
name.to_s
|
168
175
|
end
|
169
176
|
|
177
|
+
def root?
|
178
|
+
!@parent
|
179
|
+
end
|
180
|
+
|
170
181
|
protected
|
171
182
|
|
172
183
|
def push_declared_params(attrs)
|
@@ -184,10 +195,13 @@ module Grape
|
|
184
195
|
optional_fields = opts[:using].keys - required_fields
|
185
196
|
end
|
186
197
|
required_fields.each do |field|
|
187
|
-
|
198
|
+
field_opts = opts[:using][field]
|
199
|
+
raise ArgumentError, "required field not exist: #{field}" unless field_opts
|
200
|
+
requires(field, field_opts)
|
188
201
|
end
|
189
202
|
optional_fields.each do |field|
|
190
|
-
|
203
|
+
field_opts = opts[:using][field]
|
204
|
+
optional(field, field_opts) if field_opts
|
191
205
|
end
|
192
206
|
end
|
193
207
|
|
@@ -250,7 +264,7 @@ module Grape
|
|
250
264
|
@api.document_attribute(full_attrs, doc_attrs)
|
251
265
|
|
252
266
|
# Validate for presence before any other validators
|
253
|
-
if validations.
|
267
|
+
if validations.key?(:presence) && validations[:presence]
|
254
268
|
validate('presence', validations[:presence], attrs, doc_attrs)
|
255
269
|
validations.delete(:presence)
|
256
270
|
end
|
@@ -258,7 +272,7 @@ module Grape
|
|
258
272
|
# Before we run the rest of the validators, lets handle
|
259
273
|
# whatever coercion so that we are working with correctly
|
260
274
|
# type casted values
|
261
|
-
if validations.
|
275
|
+
if validations.key? :coerce
|
262
276
|
validate('coerce', validations[:coerce], attrs, doc_attrs)
|
263
277
|
validations.delete(:coerce)
|
264
278
|
end
|
@@ -7,7 +7,7 @@ module Grape
|
|
7
7
|
end
|
8
8
|
|
9
9
|
def validate_param!(attr_name, params)
|
10
|
-
params[attr_name] = @default.is_a?(Proc) ? @default.call : @default unless params.
|
10
|
+
params[attr_name] = @default.is_a?(Proc) ? @default.call : @default unless params.key?(attr_name)
|
11
11
|
end
|
12
12
|
|
13
13
|
def validate!(params)
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Grape
|
2
|
+
module Validations
|
3
|
+
require 'grape/validations/mutual_exclusion'
|
4
|
+
class ExactlyOneOfValidator < MutualExclusionValidator
|
5
|
+
attr_reader :params
|
6
|
+
|
7
|
+
def validate!(params)
|
8
|
+
super
|
9
|
+
if none_of_restricted_params_is_present
|
10
|
+
raise Grape::Exceptions::Validation, param: "#{all_keys}", message_key: :exactly_one
|
11
|
+
end
|
12
|
+
params
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def none_of_restricted_params_is_present
|
18
|
+
keys_in_common.length < 1
|
19
|
+
end
|
20
|
+
|
21
|
+
def all_keys
|
22
|
+
attrs.map(&:to_sym)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Grape
|
2
|
+
module Validations
|
3
|
+
class MutualExclusionValidator < Validator
|
4
|
+
attr_reader :params
|
5
|
+
|
6
|
+
def validate!(params)
|
7
|
+
@params = params
|
8
|
+
if two_or_more_exclusive_params_are_present
|
9
|
+
raise Grape::Exceptions::Validation, param: "#{keys_in_common.map(&:to_sym)}", message_key: :mutual_exclusion
|
10
|
+
end
|
11
|
+
params
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def two_or_more_exclusive_params_are_present
|
17
|
+
keys_in_common.length > 1
|
18
|
+
end
|
19
|
+
|
20
|
+
def keys_in_common
|
21
|
+
attrs.map(&:to_s) & params.stringify_keys.keys
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -7,7 +7,7 @@ module Grape
|
|
7
7
|
end
|
8
8
|
|
9
9
|
def validate_param!(attr_name, params)
|
10
|
-
unless params.respond_to?(:
|
10
|
+
unless params.respond_to?(:key?) && params.key?(attr_name)
|
11
11
|
raise Grape::Exceptions::Validation, param: @scope.full_name(attr_name), message_key: :presence
|
12
12
|
end
|
13
13
|
end
|
@@ -2,7 +2,8 @@ module Grape
|
|
2
2
|
module Validations
|
3
3
|
class RegexpValidator < SingleOptionValidator
|
4
4
|
def validate_param!(attr_name, params)
|
5
|
-
if params
|
5
|
+
if params.key?(attr_name) &&
|
6
|
+
(params[attr_name].nil? || !(params[attr_name].to_s =~ @option))
|
6
7
|
raise Grape::Exceptions::Validation, param: @scope.full_name(attr_name), message_key: :regexp
|
7
8
|
end
|
8
9
|
end
|
@@ -8,10 +8,16 @@ module Grape
|
|
8
8
|
end
|
9
9
|
|
10
10
|
def validate_param!(attr_name, params)
|
11
|
-
if (params[attr_name] ||
|
11
|
+
if (params[attr_name] || required_for_root_scope?) && !(@values.is_a?(Proc) ? @values.call : @values).include?(params[attr_name])
|
12
12
|
raise Grape::Exceptions::Validation, param: @scope.full_name(attr_name), message_key: :values
|
13
13
|
end
|
14
14
|
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def required_for_root_scope?
|
19
|
+
@required && @scope.root?
|
20
|
+
end
|
15
21
|
end
|
16
22
|
end
|
17
23
|
end
|
data/lib/grape/version.rb
CHANGED
data/spec/grape/api_spec.rb
CHANGED
@@ -9,7 +9,6 @@ describe Grape::API do
|
|
9
9
|
end
|
10
10
|
|
11
11
|
describe '.prefix' do
|
12
|
-
|
13
12
|
it 'routes root through with the prefix' do
|
14
13
|
subject.prefix 'awesome/sauce'
|
15
14
|
subject.get do
|
@@ -17,7 +16,7 @@ describe Grape::API do
|
|
17
16
|
end
|
18
17
|
|
19
18
|
get 'awesome/sauce/'
|
20
|
-
last_response.body.
|
19
|
+
expect(last_response.body).to eql "Hello there."
|
21
20
|
end
|
22
21
|
|
23
22
|
it 'routes through with the prefix' do
|
@@ -27,25 +26,24 @@ describe Grape::API do
|
|
27
26
|
end
|
28
27
|
|
29
28
|
get 'awesome/sauce/hello'
|
30
|
-
last_response.body.
|
29
|
+
expect(last_response.body).to eql "Hello there."
|
31
30
|
|
32
31
|
get '/hello'
|
33
|
-
last_response.status.
|
32
|
+
expect(last_response.status).to eql 404
|
34
33
|
end
|
35
|
-
|
36
34
|
end
|
37
35
|
|
38
36
|
describe '.version' do
|
39
37
|
context 'when defined' do
|
40
38
|
it 'returns version value' do
|
41
39
|
subject.version 'v1'
|
42
|
-
subject.version.
|
40
|
+
expect(subject.version).to eq('v1')
|
43
41
|
end
|
44
42
|
end
|
45
43
|
|
46
44
|
context 'when not defined' do
|
47
45
|
it 'returns nil' do
|
48
|
-
subject.version.
|
46
|
+
expect(subject.version).to be_nil
|
49
47
|
end
|
50
48
|
end
|
51
49
|
end
|
@@ -117,7 +115,7 @@ describe Grape::API do
|
|
117
115
|
it 'adds the association to the :representations setting' do
|
118
116
|
klass = Class.new
|
119
117
|
subject.represent Object, with: klass
|
120
|
-
subject.settings[:representations][Object].
|
118
|
+
expect(subject.settings[:representations][Object]).to eq(klass)
|
121
119
|
end
|
122
120
|
end
|
123
121
|
|
@@ -137,7 +135,7 @@ describe Grape::API do
|
|
137
135
|
end
|
138
136
|
|
139
137
|
get "/rad/v1/awesome/hello"
|
140
|
-
last_response.body.
|
138
|
+
expect(last_response.body).to eq("worked")
|
141
139
|
end
|
142
140
|
|
143
141
|
it 'cancels itself after the block is over' do
|
@@ -145,7 +143,7 @@ describe Grape::API do
|
|
145
143
|
namespace.should == '/awesome'
|
146
144
|
end
|
147
145
|
|
148
|
-
subject.namespace.
|
146
|
+
expect(subject.namespace).to eq('/')
|
149
147
|
end
|
150
148
|
|
151
149
|
it 'is stackable' do
|
@@ -155,7 +153,7 @@ describe Grape::API do
|
|
155
153
|
end
|
156
154
|
namespace.should == '/awesome'
|
157
155
|
end
|
158
|
-
subject.namespace.
|
156
|
+
expect(subject.namespace).to eq('/')
|
159
157
|
end
|
160
158
|
|
161
159
|
it 'accepts path segments correctly' do
|
@@ -168,7 +166,7 @@ describe Grape::API do
|
|
168
166
|
end
|
169
167
|
end
|
170
168
|
get '/members/23'
|
171
|
-
last_response.body.
|
169
|
+
expect(last_response.body).to eq("23")
|
172
170
|
end
|
173
171
|
|
174
172
|
it 'is callable with nil just to push onto the stack' do
|
@@ -179,9 +177,9 @@ describe Grape::API do
|
|
179
177
|
subject.get('/hello') { "outer" }
|
180
178
|
|
181
179
|
get '/v2/hello'
|
182
|
-
last_response.body.
|
180
|
+
expect(last_response.body).to eq("inner")
|
183
181
|
get '/hello'
|
184
|
-
last_response.body.
|
182
|
+
expect(last_response.body).to eq("outer")
|
185
183
|
end
|
186
184
|
|
187
185
|
%w(group resource resources segment).each do |als|
|
@@ -204,7 +202,7 @@ describe Grape::API do
|
|
204
202
|
end
|
205
203
|
|
206
204
|
get '/users/23'
|
207
|
-
last_response.body.
|
205
|
+
expect(last_response.body).to eq('23')
|
208
206
|
end
|
209
207
|
|
210
208
|
it 'should be able to define requirements with a single hash' do
|
@@ -217,9 +215,9 @@ describe Grape::API do
|
|
217
215
|
end
|
218
216
|
|
219
217
|
get '/users/michael'
|
220
|
-
last_response.status.
|
218
|
+
expect(last_response.status).to eq(404)
|
221
219
|
get '/users/23'
|
222
|
-
last_response.status.
|
220
|
+
expect(last_response.status).to eq(200)
|
223
221
|
end
|
224
222
|
end
|
225
223
|
|
@@ -235,15 +233,15 @@ describe Grape::API do
|
|
235
233
|
end
|
236
234
|
|
237
235
|
get '/votes'
|
238
|
-
last_response.body.
|
236
|
+
expect(last_response.body).to eql 'Votes'
|
239
237
|
post '/votes'
|
240
|
-
last_response.body.
|
238
|
+
expect(last_response.body).to eql 'Created a Vote'
|
241
239
|
end
|
242
240
|
|
243
241
|
it 'handles empty calls' do
|
244
242
|
subject.get "/"
|
245
243
|
get "/"
|
246
|
-
last_response.body.
|
244
|
+
expect(last_response.body).to eql ""
|
247
245
|
end
|
248
246
|
|
249
247
|
describe 'root routes should work with' do
|
@@ -255,7 +253,7 @@ describe Grape::API do
|
|
255
253
|
end
|
256
254
|
|
257
255
|
after do
|
258
|
-
last_response.body.
|
256
|
+
expect(last_response.body).to eql "root"
|
259
257
|
end
|
260
258
|
|
261
259
|
describe 'path versioned APIs' do
|
@@ -315,9 +313,9 @@ describe Grape::API do
|
|
315
313
|
end
|
316
314
|
|
317
315
|
get '/abc'
|
318
|
-
last_response.body.
|
316
|
+
expect(last_response.body).to eql 'foo'
|
319
317
|
get '/def'
|
320
|
-
last_response.body.
|
318
|
+
expect(last_response.body).to eql 'foo'
|
321
319
|
end
|
322
320
|
|
323
321
|
context 'format' do
|
@@ -329,14 +327,14 @@ describe Grape::API do
|
|
329
327
|
|
330
328
|
it 'allows .json' do
|
331
329
|
get '/abc.json'
|
332
|
-
last_response.status.
|
333
|
-
last_response.body.
|
330
|
+
expect(last_response.status).to eq(200)
|
331
|
+
expect(last_response.body).to eql 'abc' # json-encoded symbol
|
334
332
|
end
|
335
333
|
|
336
334
|
it 'allows .txt' do
|
337
335
|
get '/abc.txt'
|
338
|
-
last_response.status.
|
339
|
-
last_response.body.
|
336
|
+
expect(last_response.status).to eq(200)
|
337
|
+
expect(last_response.body).to eql 'def' # raw text
|
340
338
|
end
|
341
339
|
end
|
342
340
|
|
@@ -346,7 +344,7 @@ describe Grape::API do
|
|
346
344
|
end
|
347
345
|
|
348
346
|
get '/awesome.json'
|
349
|
-
last_response.body.
|
347
|
+
expect(last_response.body).to eql '{"id":"awesome"}'
|
350
348
|
end
|
351
349
|
|
352
350
|
it 'allows for format in namespace with no path' do
|
@@ -357,7 +355,7 @@ describe Grape::API do
|
|
357
355
|
end
|
358
356
|
|
359
357
|
get '/abc.json'
|
360
|
-
last_response.body.
|
358
|
+
expect(last_response.body).to eql '["json"]'
|
361
359
|
end
|
362
360
|
|
363
361
|
it 'allows for multiple verbs' do
|
@@ -366,13 +364,13 @@ describe Grape::API do
|
|
366
364
|
end
|
367
365
|
|
368
366
|
subject.endpoints.first.routes.each do |route|
|
369
|
-
route.route_path.
|
367
|
+
expect(route.route_path).to eql '/abc(.:format)'
|
370
368
|
end
|
371
369
|
|
372
370
|
get '/abc'
|
373
|
-
last_response.body.
|
371
|
+
expect(last_response.body).to eql 'hiya'
|
374
372
|
post '/abc'
|
375
|
-
last_response.body.
|
373
|
+
expect(last_response.body).to eql 'hiya'
|
376
374
|
end
|
377
375
|
|
378
376
|
[:put, :post].each do |verb|
|
@@ -384,9 +382,9 @@ describe Grape::API do
|
|
384
382
|
env['api.request.body']
|
385
383
|
end
|
386
384
|
send verb, '/', MultiJson.dump(object), 'CONTENT_TYPE' => 'application/json'
|
387
|
-
last_response.status.
|
388
|
-
last_response.body.
|
389
|
-
last_request.params.
|
385
|
+
expect(last_response.status).to eq(verb == :post ? 201 : 200)
|
386
|
+
expect(last_response.body).to eql MultiJson.dump(object)
|
387
|
+
expect(last_request.params).to eql Hash.new
|
390
388
|
end
|
391
389
|
it "stores input in api.request.input" do
|
392
390
|
subject.format :json
|
@@ -394,8 +392,8 @@ describe Grape::API do
|
|
394
392
|
env['api.request.input']
|
395
393
|
end
|
396
394
|
send verb, '/', MultiJson.dump(object), 'CONTENT_TYPE' => 'application/json'
|
397
|
-
last_response.status.
|
398
|
-
last_response.body.
|
395
|
+
expect(last_response.status).to eq(verb == :post ? 201 : 200)
|
396
|
+
expect(last_response.body).to eql MultiJson.dump(object).to_json
|
399
397
|
end
|
400
398
|
context "chunked transfer encoding" do
|
401
399
|
it "stores input in api.request.input" do
|
@@ -404,8 +402,8 @@ describe Grape::API do
|
|
404
402
|
env['api.request.input']
|
405
403
|
end
|
406
404
|
send verb, '/', MultiJson.dump(object), 'CONTENT_TYPE' => 'application/json', 'HTTP_TRANSFER_ENCODING' => 'chunked', 'CONTENT_LENGTH' => nil
|
407
|
-
last_response.status.
|
408
|
-
last_response.body.
|
405
|
+
expect(last_response.status).to eq(verb == :post ? 201 : 200)
|
406
|
+
expect(last_response.body).to eql MultiJson.dump(object).to_json
|
409
407
|
end
|
410
408
|
end
|
411
409
|
end
|
@@ -426,15 +424,15 @@ describe Grape::API do
|
|
426
424
|
end
|
427
425
|
|
428
426
|
get '/1'
|
429
|
-
last_response.body.
|
427
|
+
expect(last_response.body).to eql 'ola'
|
430
428
|
post '/1'
|
431
|
-
last_response.body.
|
429
|
+
expect(last_response.body).to eql 'ola'
|
432
430
|
get '/1/first'
|
433
|
-
last_response.body.
|
431
|
+
expect(last_response.body).to eql 'first'
|
434
432
|
post '/1/first'
|
435
|
-
last_response.body.
|
433
|
+
expect(last_response.body).to eql 'first'
|
436
434
|
get '/1/first/second'
|
437
|
-
last_response.body.
|
435
|
+
expect(last_response.body).to eql 'second'
|
438
436
|
|
439
437
|
end
|
440
438
|
|
@@ -445,7 +443,7 @@ describe Grape::API do
|
|
445
443
|
|
446
444
|
%w(get post put delete options patch).each do |m|
|
447
445
|
send(m, '/abc')
|
448
|
-
last_response.body.
|
446
|
+
expect(last_response.body).to eql 'lol'
|
449
447
|
end
|
450
448
|
end
|
451
449
|
|
@@ -456,10 +454,10 @@ describe Grape::API do
|
|
456
454
|
verb
|
457
455
|
end
|
458
456
|
send(verb, '/example')
|
459
|
-
last_response.body.
|
457
|
+
expect(last_response.body).to eql verb == 'head' ? '' : verb
|
460
458
|
# Call it with a method other than the properly constrained one.
|
461
459
|
send(used_verb = verbs[(verbs.index(verb) + 2) % verbs.size], '/example')
|
462
|
-
last_response.status.
|
460
|
+
expect(last_response.status).to eql used_verb == 'options' ? 204 : 405
|
463
461
|
end
|
464
462
|
end
|
465
463
|
|
@@ -469,8 +467,8 @@ describe Grape::API do
|
|
469
467
|
end
|
470
468
|
|
471
469
|
post '/example'
|
472
|
-
last_response.status.
|
473
|
-
last_response.body.
|
470
|
+
expect(last_response.status).to eql 201
|
471
|
+
expect(last_response.body).to eql 'Created'
|
474
472
|
end
|
475
473
|
|
476
474
|
it 'returns a 405 for an unsupported method with an X-Custom-Header' do
|
@@ -479,9 +477,9 @@ describe Grape::API do
|
|
479
477
|
"example"
|
480
478
|
end
|
481
479
|
put '/example'
|
482
|
-
last_response.status.
|
483
|
-
last_response.body.
|
484
|
-
last_response.headers['X-Custom-Header'].
|
480
|
+
expect(last_response.status).to eql 405
|
481
|
+
expect(last_response.body).to eql ''
|
482
|
+
expect(last_response.headers['X-Custom-Header']).to eql 'foo'
|
485
483
|
end
|
486
484
|
|
487
485
|
specify '405 responses includes an Allow header specifying supported methods' do
|
@@ -492,7 +490,7 @@ describe Grape::API do
|
|
492
490
|
"example"
|
493
491
|
end
|
494
492
|
put '/example'
|
495
|
-
last_response.headers['Allow'].
|
493
|
+
expect(last_response.headers['Allow']).to eql 'OPTIONS, GET, POST, HEAD'
|
496
494
|
end
|
497
495
|
|
498
496
|
specify '405 responses includes an Content-Type header' do
|
@@ -503,7 +501,7 @@ describe Grape::API do
|
|
503
501
|
"example"
|
504
502
|
end
|
505
503
|
put '/example'
|
506
|
-
last_response.headers['Content-Type'].
|
504
|
+
expect(last_response.headers['Content-Type']).to eql 'text/plain'
|
507
505
|
end
|
508
506
|
|
509
507
|
it 'adds an OPTIONS route that returns a 204, an Allow header and a X-Custom-Header' do
|
@@ -512,10 +510,10 @@ describe Grape::API do
|
|
512
510
|
"example"
|
513
511
|
end
|
514
512
|
options '/example'
|
515
|
-
last_response.status.
|
516
|
-
last_response.body.
|
517
|
-
last_response.headers['Allow'].
|
518
|
-
last_response.headers['X-Custom-Header'].
|
513
|
+
expect(last_response.status).to eql 204
|
514
|
+
expect(last_response.body).to eql ''
|
515
|
+
expect(last_response.headers['Allow']).to eql 'OPTIONS, GET, HEAD'
|
516
|
+
expect(last_response.headers['X-Custom-Header']).to eql 'foo'
|
519
517
|
end
|
520
518
|
|
521
519
|
it 'allows HEAD on a GET request' do
|
@@ -523,8 +521,8 @@ describe Grape::API do
|
|
523
521
|
"example"
|
524
522
|
end
|
525
523
|
head '/example'
|
526
|
-
last_response.status.
|
527
|
-
last_response.body.
|
524
|
+
expect(last_response.status).to eql 200
|
525
|
+
expect(last_response.body).to eql ''
|
528
526
|
end
|
529
527
|
|
530
528
|
it 'overwrites the default HEAD request' do
|
@@ -535,7 +533,7 @@ describe Grape::API do
|
|
535
533
|
"example"
|
536
534
|
end
|
537
535
|
head '/example'
|
538
|
-
last_response.status.
|
536
|
+
expect(last_response.status).to eql 400
|
539
537
|
end
|
540
538
|
end
|
541
539
|
|
@@ -548,13 +546,13 @@ describe Grape::API do
|
|
548
546
|
end
|
549
547
|
it 'options does not contain HEAD' do
|
550
548
|
options '/example'
|
551
|
-
last_response.status.
|
552
|
-
last_response.body.
|
553
|
-
last_response.headers['Allow'].
|
549
|
+
expect(last_response.status).to eql 204
|
550
|
+
expect(last_response.body).to eql ''
|
551
|
+
expect(last_response.headers['Allow']).to eql 'OPTIONS, GET'
|
554
552
|
end
|
555
553
|
it 'does not allow HEAD on a GET request' do
|
556
554
|
head '/example'
|
557
|
-
last_response.status.
|
555
|
+
expect(last_response.status).to eql 405
|
558
556
|
end
|
559
557
|
end
|
560
558
|
|
@@ -567,7 +565,7 @@ describe Grape::API do
|
|
567
565
|
end
|
568
566
|
it 'options does not exist' do
|
569
567
|
options '/example'
|
570
|
-
last_response.status.
|
568
|
+
expect(last_response.status).to eql 405
|
571
569
|
end
|
572
570
|
end
|
573
571
|
|
@@ -580,7 +578,7 @@ describe Grape::API do
|
|
580
578
|
end
|
581
579
|
|
582
580
|
get '/'
|
583
|
-
last_response.body.
|
581
|
+
expect(last_response.body).to eql 'first second'
|
584
582
|
end
|
585
583
|
|
586
584
|
it 'adds a before filter to current and child namespaces only' do
|
@@ -601,11 +599,11 @@ describe Grape::API do
|
|
601
599
|
end
|
602
600
|
|
603
601
|
get '/'
|
604
|
-
last_response.body.
|
602
|
+
expect(last_response.body).to eql 'root - '
|
605
603
|
get '/blah'
|
606
|
-
last_response.body.
|
604
|
+
expect(last_response.body).to eql 'blah - foo'
|
607
605
|
get '/blah/bar'
|
608
|
-
last_response.body.
|
606
|
+
expect(last_response.body).to eql 'blah - bar - foo'
|
609
607
|
end
|
610
608
|
|
611
609
|
it 'adds a after_validation filter' do
|
@@ -619,7 +617,7 @@ describe Grape::API do
|
|
619
617
|
end
|
620
618
|
|
621
619
|
get '/', id: "32"
|
622
|
-
last_response.body.
|
620
|
+
expect(last_response.body).to eql 'first 32:Fixnum second'
|
623
621
|
end
|
624
622
|
|
625
623
|
it 'adds a after filter' do
|
@@ -630,9 +628,9 @@ describe Grape::API do
|
|
630
628
|
@var ||= 'default'
|
631
629
|
end
|
632
630
|
|
633
|
-
m.
|
631
|
+
expect(m).to receive(:do_something!).exactly(2).times
|
634
632
|
get '/'
|
635
|
-
last_response.body.
|
633
|
+
expect(last_response.body).to eql 'default'
|
636
634
|
end
|
637
635
|
|
638
636
|
it 'calls all filters when validation passes' do
|
@@ -654,14 +652,14 @@ describe Grape::API do
|
|
654
652
|
end
|
655
653
|
end
|
656
654
|
|
657
|
-
a.
|
658
|
-
b.
|
659
|
-
c.
|
660
|
-
d.
|
655
|
+
expect(a).to receive(:do_something!).exactly(1).times
|
656
|
+
expect(b).to receive(:do_something!).exactly(1).times
|
657
|
+
expect(c).to receive(:do_something!).exactly(1).times
|
658
|
+
expect(d).to receive(:do_something!).exactly(1).times
|
661
659
|
|
662
660
|
get '/123'
|
663
|
-
last_response.status.
|
664
|
-
last_response.body.
|
661
|
+
expect(last_response.status).to eql 200
|
662
|
+
expect(last_response.body).to eql 'got it'
|
665
663
|
end
|
666
664
|
|
667
665
|
it 'calls only before filters when validation fails' do
|
@@ -683,14 +681,14 @@ describe Grape::API do
|
|
683
681
|
end
|
684
682
|
end
|
685
683
|
|
686
|
-
a.
|
687
|
-
b.
|
688
|
-
c.
|
689
|
-
d.
|
684
|
+
expect(a).to receive(:do_something!).exactly(1).times
|
685
|
+
expect(b).to receive(:do_something!).exactly(1).times
|
686
|
+
expect(c).to receive(:do_something!).exactly(0).times
|
687
|
+
expect(d).to receive(:do_something!).exactly(0).times
|
690
688
|
|
691
689
|
get '/abc'
|
692
|
-
last_response.status.
|
693
|
-
last_response.body.
|
690
|
+
expect(last_response.status).to eql 400
|
691
|
+
expect(last_response.body).to eql 'id is invalid'
|
694
692
|
end
|
695
693
|
|
696
694
|
it 'calls filters in the correct order' do
|
@@ -713,14 +711,14 @@ describe Grape::API do
|
|
713
711
|
end
|
714
712
|
end
|
715
713
|
|
716
|
-
a.
|
717
|
-
b.
|
718
|
-
c.
|
719
|
-
d.
|
714
|
+
expect(a).to receive(:here).with(1).exactly(1).times
|
715
|
+
expect(b).to receive(:here).with(2).exactly(1).times
|
716
|
+
expect(c).to receive(:here).with(3).exactly(1).times
|
717
|
+
expect(d).to receive(:here).with(4).exactly(1).times
|
720
718
|
|
721
719
|
get '/123'
|
722
|
-
last_response.status.
|
723
|
-
last_response.body.
|
720
|
+
expect(last_response.status).to eql 200
|
721
|
+
expect(last_response.body).to eql 'got it'
|
724
722
|
end
|
725
723
|
end
|
726
724
|
|
@@ -731,32 +729,32 @@ describe Grape::API do
|
|
731
729
|
|
732
730
|
it 'sets content type for txt format' do
|
733
731
|
get '/foo'
|
734
|
-
last_response.headers['Content-Type'].
|
732
|
+
expect(last_response.headers['Content-Type']).to eql 'text/plain'
|
735
733
|
end
|
736
734
|
|
737
735
|
it 'sets content type for json' do
|
738
736
|
get '/foo.json'
|
739
|
-
last_response.headers['Content-Type'].
|
737
|
+
expect(last_response.headers['Content-Type']).to eql 'application/json'
|
740
738
|
end
|
741
739
|
|
742
740
|
it 'sets content type for error' do
|
743
741
|
subject.get('/error') { error!('error in plain text', 500) }
|
744
742
|
get '/error'
|
745
|
-
last_response.headers['Content-Type'].
|
743
|
+
expect(last_response.headers['Content-Type']).to eql 'text/plain'
|
746
744
|
end
|
747
745
|
|
748
746
|
it 'sets content type for error' do
|
749
747
|
subject.format :json
|
750
748
|
subject.get('/error') { error!('error in json', 500) }
|
751
749
|
get '/error.json'
|
752
|
-
last_response.headers['Content-Type'].
|
750
|
+
expect(last_response.headers['Content-Type']).to eql 'application/json'
|
753
751
|
end
|
754
752
|
|
755
753
|
it 'sets content type for xml' do
|
756
754
|
subject.format :xml
|
757
755
|
subject.get('/error') { error!('error in xml', 500) }
|
758
756
|
get '/error.xml'
|
759
|
-
last_response.headers['Content-Type'].
|
757
|
+
expect(last_response.headers['Content-Type']).to eql 'application/xml'
|
760
758
|
end
|
761
759
|
|
762
760
|
context 'with a custom content_type' do
|
@@ -770,12 +768,48 @@ describe Grape::API do
|
|
770
768
|
|
771
769
|
it 'sets content type' do
|
772
770
|
get '/custom.custom'
|
773
|
-
last_response.headers['Content-Type'].
|
771
|
+
expect(last_response.headers['Content-Type']).to eql 'application/custom'
|
774
772
|
end
|
775
773
|
|
776
774
|
it 'sets content type for error' do
|
777
775
|
get '/error.custom'
|
778
|
-
last_response.headers['Content-Type'].
|
776
|
+
expect(last_response.headers['Content-Type']).to eql 'application/custom'
|
777
|
+
end
|
778
|
+
end
|
779
|
+
|
780
|
+
context 'env["api.format"]' do
|
781
|
+
before do
|
782
|
+
subject.post "attachment" do
|
783
|
+
filename = params[:file][:filename]
|
784
|
+
content_type MIME::Types.type_for(filename)[0].to_s
|
785
|
+
env['api.format'] = :binary # there's no formatter for :binary, data will be returned "as is"
|
786
|
+
header "Content-Disposition", "attachment; filename*=UTF-8''#{URI.escape(filename)}"
|
787
|
+
params[:file][:tempfile].read
|
788
|
+
end
|
789
|
+
end
|
790
|
+
|
791
|
+
['/attachment.png', 'attachment'].each do |url|
|
792
|
+
it "uploads and downloads a PNG file via #{url}" do
|
793
|
+
image_filename = "grape.png"
|
794
|
+
post url, file: Rack::Test::UploadedFile.new(image_filename, 'image/png', true)
|
795
|
+
last_response.status.should == 201
|
796
|
+
last_response.headers['Content-Type'].should == "image/png"
|
797
|
+
last_response.headers['Content-Disposition'].should == "attachment; filename*=UTF-8''grape.png"
|
798
|
+
File.open(image_filename, 'rb') do |io|
|
799
|
+
last_response.body.should eq io.read
|
800
|
+
end
|
801
|
+
end
|
802
|
+
end
|
803
|
+
|
804
|
+
it "uploads and downloads a Ruby file" do
|
805
|
+
filename = __FILE__
|
806
|
+
post "/attachment.rb", file: Rack::Test::UploadedFile.new(filename, 'application/x-ruby', true)
|
807
|
+
last_response.status.should == 201
|
808
|
+
last_response.headers['Content-Type'].should == "application/x-ruby"
|
809
|
+
last_response.headers['Content-Disposition'].should == "attachment; filename*=UTF-8''api_spec.rb"
|
810
|
+
File.open(filename, 'rb') do |io|
|
811
|
+
last_response.body.should eq io.read
|
812
|
+
end
|
779
813
|
end
|
780
814
|
end
|
781
815
|
end
|
@@ -801,21 +835,21 @@ describe Grape::API do
|
|
801
835
|
describe '.middleware' do
|
802
836
|
it 'includes middleware arguments from settings' do
|
803
837
|
settings = Grape::Util::HashStack.new
|
804
|
-
settings.
|
805
|
-
subject.
|
806
|
-
subject.middleware.
|
838
|
+
allow(settings).to receive(:stack).and_return([{ middleware: [[ApiSpec::PhonyMiddleware, 'abc', 123]] }])
|
839
|
+
allow(subject).to receive(:settings).and_return(settings)
|
840
|
+
expect(subject.middleware).to eql [[ApiSpec::PhonyMiddleware, 'abc', 123]]
|
807
841
|
end
|
808
842
|
|
809
843
|
it 'includes all middleware from stacked settings' do
|
810
844
|
settings = Grape::Util::HashStack.new
|
811
|
-
settings.
|
845
|
+
allow(settings).to receive(:stack).and_return [
|
812
846
|
{ middleware: [[ApiSpec::PhonyMiddleware, 123], [ApiSpec::PhonyMiddleware, 'abc']] },
|
813
847
|
{ middleware: [[ApiSpec::PhonyMiddleware, 'foo']] }
|
814
848
|
]
|
815
849
|
|
816
|
-
subject.
|
850
|
+
allow(subject).to receive(:settings).and_return(settings)
|
817
851
|
|
818
|
-
subject.middleware.
|
852
|
+
expect(subject.middleware).to eql [
|
819
853
|
[ApiSpec::PhonyMiddleware, 123],
|
820
854
|
[ApiSpec::PhonyMiddleware, 'abc'],
|
821
855
|
[ApiSpec::PhonyMiddleware, 'foo']
|
@@ -826,7 +860,7 @@ describe Grape::API do
|
|
826
860
|
describe '.use' do
|
827
861
|
it 'adds middleware' do
|
828
862
|
subject.use ApiSpec::PhonyMiddleware, 123
|
829
|
-
subject.middleware.
|
863
|
+
expect(subject.middleware).to eql [[ApiSpec::PhonyMiddleware, 123]]
|
830
864
|
end
|
831
865
|
|
832
866
|
it 'does not show up outside the namespace' do
|
@@ -836,7 +870,7 @@ describe Grape::API do
|
|
836
870
|
middleware.should == [[ApiSpec::PhonyMiddleware, 123], [ApiSpec::PhonyMiddleware, 'abc']]
|
837
871
|
end
|
838
872
|
|
839
|
-
subject.middleware.
|
873
|
+
expect(subject.middleware).to eql [[ApiSpec::PhonyMiddleware, 123]]
|
840
874
|
end
|
841
875
|
|
842
876
|
it 'calls the middleware' do
|
@@ -846,13 +880,13 @@ describe Grape::API do
|
|
846
880
|
end
|
847
881
|
|
848
882
|
get '/'
|
849
|
-
last_response.body.
|
883
|
+
expect(last_response.body).to eql 'hello'
|
850
884
|
end
|
851
885
|
|
852
886
|
it 'adds a block if one is given' do
|
853
887
|
block = lambda {}
|
854
888
|
subject.use ApiSpec::PhonyMiddleware, &block
|
855
|
-
subject.middleware.
|
889
|
+
expect(subject.middleware).to eql [[ApiSpec::PhonyMiddleware, block]]
|
856
890
|
end
|
857
891
|
|
858
892
|
it 'uses a block if one is given' do
|
@@ -863,7 +897,7 @@ describe Grape::API do
|
|
863
897
|
end
|
864
898
|
|
865
899
|
get '/'
|
866
|
-
last_response.body.
|
900
|
+
expect(last_response.body).to eq('true')
|
867
901
|
end
|
868
902
|
|
869
903
|
it 'does not destroy the middleware settings on multiple runs' do
|
@@ -875,7 +909,7 @@ describe Grape::API do
|
|
875
909
|
|
876
910
|
2.times do
|
877
911
|
get '/'
|
878
|
-
last_response.body.
|
912
|
+
expect(last_response.body).to eq('true')
|
879
913
|
end
|
880
914
|
end
|
881
915
|
|
@@ -889,8 +923,8 @@ describe Grape::API do
|
|
889
923
|
subject.get "/" do
|
890
924
|
end
|
891
925
|
get "/"
|
892
|
-
last_response.status.
|
893
|
-
last_response.body.
|
926
|
+
expect(last_response.status).to eq(400)
|
927
|
+
expect(last_response.body).to eq("Caught in the Net")
|
894
928
|
end
|
895
929
|
end
|
896
930
|
end
|
@@ -901,9 +935,9 @@ describe Grape::API do
|
|
901
935
|
end
|
902
936
|
subject.get(:hello) { "Hello, world." }
|
903
937
|
get '/hello'
|
904
|
-
last_response.status.
|
938
|
+
expect(last_response.status).to eql 401
|
905
939
|
get '/hello', {}, 'HTTP_AUTHORIZATION' => encode_basic_auth('allow', 'whatever')
|
906
|
-
last_response.status.
|
940
|
+
expect(last_response.status).to eql 200
|
907
941
|
end
|
908
942
|
|
909
943
|
it 'is scopable' do
|
@@ -917,9 +951,9 @@ describe Grape::API do
|
|
917
951
|
end
|
918
952
|
|
919
953
|
get '/hello'
|
920
|
-
last_response.status.
|
954
|
+
expect(last_response.status).to eql 200
|
921
955
|
get '/admin/hello'
|
922
|
-
last_response.status.
|
956
|
+
expect(last_response.status).to eql 401
|
923
957
|
end
|
924
958
|
|
925
959
|
it 'is callable via .auth as well' do
|
@@ -929,9 +963,9 @@ describe Grape::API do
|
|
929
963
|
|
930
964
|
subject.get(:hello) { "Hello, world." }
|
931
965
|
get '/hello'
|
932
|
-
last_response.status.
|
966
|
+
expect(last_response.status).to eql 401
|
933
967
|
get '/hello', {}, 'HTTP_AUTHORIZATION' => encode_basic_auth('allow', 'whatever')
|
934
|
-
last_response.status.
|
968
|
+
expect(last_response.status).to eql 200
|
935
969
|
end
|
936
970
|
|
937
971
|
it 'has access to the current endpoint' do
|
@@ -945,7 +979,7 @@ describe Grape::API do
|
|
945
979
|
|
946
980
|
subject.get(:hello) { "Hello, world." }
|
947
981
|
get '/hello', {}, 'HTTP_AUTHORIZATION' => encode_basic_auth('allow', 'whatever')
|
948
|
-
basic_auth_context.
|
982
|
+
expect(basic_auth_context).to be_an_instance_of(Grape::Endpoint)
|
949
983
|
end
|
950
984
|
|
951
985
|
it 'has access to helper methods' do
|
@@ -961,9 +995,9 @@ describe Grape::API do
|
|
961
995
|
|
962
996
|
subject.get(:hello) { "Hello, world." }
|
963
997
|
get '/hello', {}, 'HTTP_AUTHORIZATION' => encode_basic_auth('allow', 'whatever')
|
964
|
-
last_response.status.
|
998
|
+
expect(last_response.status).to eql 200
|
965
999
|
get '/hello', {}, 'HTTP_AUTHORIZATION' => encode_basic_auth('disallow', 'whatever')
|
966
|
-
last_response.status.
|
1000
|
+
expect(last_response.status).to eql 401
|
967
1001
|
end
|
968
1002
|
|
969
1003
|
it 'can set instance variables accessible to routes' do
|
@@ -975,27 +1009,27 @@ describe Grape::API do
|
|
975
1009
|
|
976
1010
|
subject.get(:hello) { @hello }
|
977
1011
|
get '/hello', {}, 'HTTP_AUTHORIZATION' => encode_basic_auth('allow', 'whatever')
|
978
|
-
last_response.status.
|
979
|
-
last_response.body.
|
1012
|
+
expect(last_response.status).to eql 200
|
1013
|
+
expect(last_response.body).to eql "Hello, world."
|
980
1014
|
end
|
981
1015
|
end
|
982
1016
|
|
983
1017
|
describe '.logger' do
|
984
1018
|
it 'returns an instance of Logger class by default' do
|
985
|
-
subject.logger.class.
|
1019
|
+
expect(subject.logger.class).to eql Logger
|
986
1020
|
end
|
987
1021
|
|
988
1022
|
it 'allows setting a custom logger' do
|
989
1023
|
mylogger = Class.new
|
990
1024
|
subject.logger mylogger
|
991
|
-
mylogger.
|
1025
|
+
expect(mylogger).to receive(:info).exactly(1).times
|
992
1026
|
subject.logger.info "this will be logged"
|
993
1027
|
end
|
994
1028
|
|
995
1029
|
it "defaults to a standard logger log format" do
|
996
1030
|
t = Time.at(100)
|
997
|
-
Time.
|
998
|
-
STDOUT.
|
1031
|
+
allow(Time).to receive(:now).and_return(t)
|
1032
|
+
expect(STDOUT).to receive(:write).with("I, [#{Logger::Formatter.new.send(:format_datetime, t)}\##{Process.pid}] INFO -- : this will be logged\n")
|
999
1033
|
subject.logger.info "this will be logged"
|
1000
1034
|
end
|
1001
1035
|
end
|
@@ -1013,7 +1047,7 @@ describe Grape::API do
|
|
1013
1047
|
end
|
1014
1048
|
|
1015
1049
|
get '/howdy'
|
1016
|
-
last_response.body.
|
1050
|
+
expect(last_response.body).to eql 'Hello, world.'
|
1017
1051
|
end
|
1018
1052
|
|
1019
1053
|
it 'is scopable' do
|
@@ -1040,9 +1074,9 @@ describe Grape::API do
|
|
1040
1074
|
end
|
1041
1075
|
|
1042
1076
|
get '/generic'
|
1043
|
-
last_response.body.
|
1077
|
+
expect(last_response.body).to eql 'always there:false'
|
1044
1078
|
get '/admin/secret'
|
1045
|
-
last_response.body.
|
1079
|
+
expect(last_response.body).to eql 'always there:only in admin'
|
1046
1080
|
end
|
1047
1081
|
|
1048
1082
|
it 'is reopenable' do
|
@@ -1062,7 +1096,7 @@ describe Grape::API do
|
|
1062
1096
|
[one, two]
|
1063
1097
|
end
|
1064
1098
|
|
1065
|
-
|
1099
|
+
expect { get '/howdy' }.not_to raise_error
|
1066
1100
|
end
|
1067
1101
|
|
1068
1102
|
it 'allows for modules' do
|
@@ -1078,7 +1112,7 @@ describe Grape::API do
|
|
1078
1112
|
end
|
1079
1113
|
|
1080
1114
|
get '/howdy'
|
1081
|
-
last_response.body.
|
1115
|
+
expect(last_response.body).to eql 'Hello, world.'
|
1082
1116
|
end
|
1083
1117
|
|
1084
1118
|
it 'allows multiple calls with modules and blocks' do
|
@@ -1100,7 +1134,7 @@ describe Grape::API do
|
|
1100
1134
|
subject.get 'howdy' do
|
1101
1135
|
[one, two, three]
|
1102
1136
|
end
|
1103
|
-
|
1137
|
+
expect { get '/howdy' }.not_to raise_error
|
1104
1138
|
end
|
1105
1139
|
end
|
1106
1140
|
|
@@ -1122,13 +1156,13 @@ describe Grape::API do
|
|
1122
1156
|
end
|
1123
1157
|
|
1124
1158
|
get '/new/abc'
|
1125
|
-
last_response.status.
|
1159
|
+
expect(last_response.status).to eql 404
|
1126
1160
|
get '/legacy/abc'
|
1127
|
-
last_response.status.
|
1161
|
+
expect(last_response.status).to eql 200
|
1128
1162
|
get '/legacy/def'
|
1129
|
-
last_response.status.
|
1163
|
+
expect(last_response.status).to eql 404
|
1130
1164
|
get '/new/def'
|
1131
|
-
last_response.status.
|
1165
|
+
expect(last_response.status).to eql 200
|
1132
1166
|
end
|
1133
1167
|
end
|
1134
1168
|
|
@@ -1137,7 +1171,7 @@ describe Grape::API do
|
|
1137
1171
|
subject.get '/exception' do
|
1138
1172
|
raise "rain!"
|
1139
1173
|
end
|
1140
|
-
|
1174
|
+
expect { get '/exception' }.to raise_error
|
1141
1175
|
end
|
1142
1176
|
|
1143
1177
|
it 'rescues all errors if rescue_from :all is called' do
|
@@ -1146,7 +1180,7 @@ describe Grape::API do
|
|
1146
1180
|
raise "rain!"
|
1147
1181
|
end
|
1148
1182
|
get '/exception'
|
1149
|
-
last_response.status.
|
1183
|
+
expect(last_response.status).to eql 500
|
1150
1184
|
end
|
1151
1185
|
|
1152
1186
|
it 'rescues only certain errors if rescue_from is called with specific errors' do
|
@@ -1155,9 +1189,9 @@ describe Grape::API do
|
|
1155
1189
|
subject.get('/unrescued') { raise "beefcake" }
|
1156
1190
|
|
1157
1191
|
get '/rescued'
|
1158
|
-
last_response.status.
|
1192
|
+
expect(last_response.status).to eql 500
|
1159
1193
|
|
1160
|
-
|
1194
|
+
expect { get '/unrescued' }.to raise_error
|
1161
1195
|
end
|
1162
1196
|
|
1163
1197
|
context 'CustomError subclass of Grape::Exceptions::Base' do
|
@@ -1168,7 +1202,7 @@ describe Grape::API do
|
|
1168
1202
|
it 'does not re-raise exceptions of type Grape::Exceptions::Base' do
|
1169
1203
|
subject.get('/custom_exception') { raise CustomError }
|
1170
1204
|
|
1171
|
-
|
1205
|
+
expect { get '/custom_exception' }.not_to raise_error
|
1172
1206
|
end
|
1173
1207
|
|
1174
1208
|
it 'rescues custom grape exceptions' do
|
@@ -1180,15 +1214,15 @@ describe Grape::API do
|
|
1180
1214
|
end
|
1181
1215
|
|
1182
1216
|
get '/custom_error'
|
1183
|
-
last_response.status.
|
1184
|
-
last_response.body.
|
1217
|
+
expect(last_response.status).to eq(400)
|
1218
|
+
expect(last_response.body).to eq('New Error')
|
1185
1219
|
end
|
1186
1220
|
end
|
1187
1221
|
|
1188
1222
|
it 'can rescue exceptions raised in the formatter' do
|
1189
1223
|
formatter = double(:formatter)
|
1190
|
-
formatter.
|
1191
|
-
Grape::Formatter::Base.
|
1224
|
+
allow(formatter).to receive(:call) { raise StandardError }
|
1225
|
+
allow(Grape::Formatter::Base).to receive(:formatter_for) { formatter }
|
1192
1226
|
|
1193
1227
|
subject.rescue_from :all do |e|
|
1194
1228
|
rack_response('Formatter Error', 500)
|
@@ -1196,8 +1230,8 @@ describe Grape::API do
|
|
1196
1230
|
subject.get('/formatter_exception') { 'Hello world' }
|
1197
1231
|
|
1198
1232
|
get '/formatter_exception'
|
1199
|
-
last_response.status.
|
1200
|
-
last_response.body.
|
1233
|
+
expect(last_response.status).to eql 500
|
1234
|
+
expect(last_response.body).to eq('Formatter Error')
|
1201
1235
|
end
|
1202
1236
|
end
|
1203
1237
|
|
@@ -1210,8 +1244,8 @@ describe Grape::API do
|
|
1210
1244
|
raise "rain!"
|
1211
1245
|
end
|
1212
1246
|
get '/exception'
|
1213
|
-
last_response.status.
|
1214
|
-
last_response.body.
|
1247
|
+
expect(last_response.status).to eql 202
|
1248
|
+
expect(last_response.body).to eq('rescued from rain!')
|
1215
1249
|
end
|
1216
1250
|
|
1217
1251
|
context 'custom errors' do
|
@@ -1229,8 +1263,8 @@ describe Grape::API do
|
|
1229
1263
|
raise ConnectionError
|
1230
1264
|
end
|
1231
1265
|
get '/exception'
|
1232
|
-
last_response.status.
|
1233
|
-
last_response.body.
|
1266
|
+
expect(last_response.status).to eql 500
|
1267
|
+
expect(last_response.body).to eq('rescued from ConnectionError')
|
1234
1268
|
end
|
1235
1269
|
it 'rescues a specific error' do
|
1236
1270
|
subject.rescue_from ConnectionError do |e|
|
@@ -1240,8 +1274,19 @@ describe Grape::API do
|
|
1240
1274
|
raise ConnectionError
|
1241
1275
|
end
|
1242
1276
|
get '/exception'
|
1243
|
-
last_response.status.
|
1244
|
-
last_response.body.
|
1277
|
+
expect(last_response.status).to eql 500
|
1278
|
+
expect(last_response.body).to eq('rescued from ConnectionError')
|
1279
|
+
end
|
1280
|
+
it 'rescues a subclass of an error by default' do
|
1281
|
+
subject.rescue_from RuntimeError do |e|
|
1282
|
+
rack_response("rescued from #{e.class.name}", 500)
|
1283
|
+
end
|
1284
|
+
subject.get '/exception' do
|
1285
|
+
raise ConnectionError
|
1286
|
+
end
|
1287
|
+
get '/exception'
|
1288
|
+
expect(last_response.status).to eql 500
|
1289
|
+
expect(last_response.body).to eq('rescued from ConnectionError')
|
1245
1290
|
end
|
1246
1291
|
it 'rescues multiple specific errors' do
|
1247
1292
|
subject.rescue_from ConnectionError do |e|
|
@@ -1257,11 +1302,11 @@ describe Grape::API do
|
|
1257
1302
|
raise DatabaseError
|
1258
1303
|
end
|
1259
1304
|
get '/connection'
|
1260
|
-
last_response.status.
|
1261
|
-
last_response.body.
|
1305
|
+
expect(last_response.status).to eql 500
|
1306
|
+
expect(last_response.body).to eq('rescued from ConnectionError')
|
1262
1307
|
get '/database'
|
1263
|
-
last_response.status.
|
1264
|
-
last_response.body.
|
1308
|
+
expect(last_response.status).to eql 500
|
1309
|
+
expect(last_response.body).to eq('rescued from DatabaseError')
|
1265
1310
|
end
|
1266
1311
|
it 'does not rescue a different error' do
|
1267
1312
|
subject.rescue_from RuntimeError do |e|
|
@@ -1270,7 +1315,7 @@ describe Grape::API do
|
|
1270
1315
|
subject.get '/uncaught' do
|
1271
1316
|
raise CommunicationError
|
1272
1317
|
end
|
1273
|
-
|
1318
|
+
expect { get '/uncaught' }.to raise_error(CommunicationError)
|
1274
1319
|
end
|
1275
1320
|
end
|
1276
1321
|
end
|
@@ -1283,8 +1328,8 @@ describe Grape::API do
|
|
1283
1328
|
subject.get('/rescue_lambda') { raise ArgumentError }
|
1284
1329
|
|
1285
1330
|
get '/rescue_lambda'
|
1286
|
-
last_response.status.
|
1287
|
-
last_response.body.
|
1331
|
+
expect(last_response.status).to eq(400)
|
1332
|
+
expect(last_response.body).to eq("rescued with a lambda")
|
1288
1333
|
end
|
1289
1334
|
|
1290
1335
|
it 'can execute the lambda with an argument' do
|
@@ -1294,8 +1339,8 @@ describe Grape::API do
|
|
1294
1339
|
subject.get('/rescue_lambda') { raise ArgumentError, 'lambda takes an argument' }
|
1295
1340
|
|
1296
1341
|
get '/rescue_lambda'
|
1297
|
-
last_response.status.
|
1298
|
-
last_response.body.
|
1342
|
+
expect(last_response.status).to eq(400)
|
1343
|
+
expect(last_response.body).to eq('lambda takes an argument')
|
1299
1344
|
end
|
1300
1345
|
end
|
1301
1346
|
|
@@ -1309,8 +1354,8 @@ describe Grape::API do
|
|
1309
1354
|
subject.get('/rescue_method') { raise ArgumentError }
|
1310
1355
|
|
1311
1356
|
get '/rescue_method'
|
1312
|
-
last_response.status.
|
1313
|
-
last_response.body.
|
1357
|
+
expect(last_response.status).to eq(400)
|
1358
|
+
expect(last_response.body).to eq('rescued with a method')
|
1314
1359
|
end
|
1315
1360
|
end
|
1316
1361
|
|
@@ -1337,10 +1382,22 @@ describe Grape::API do
|
|
1337
1382
|
end
|
1338
1383
|
|
1339
1384
|
get '/caught_child'
|
1340
|
-
last_response.status.
|
1385
|
+
expect(last_response.status).to eql 500
|
1341
1386
|
get '/caught_parent'
|
1342
|
-
last_response.status.
|
1343
|
-
|
1387
|
+
expect(last_response.status).to eql 500
|
1388
|
+
expect { get '/uncaught_parent' }.to raise_error(StandardError)
|
1389
|
+
end
|
1390
|
+
|
1391
|
+
it 'sets rescue_subclasses to true by default' do
|
1392
|
+
subject.rescue_from APIErrors::ParentError do |e|
|
1393
|
+
rack_response("rescued from #{e.class.name}", 500)
|
1394
|
+
end
|
1395
|
+
subject.get '/caught_child' do
|
1396
|
+
raise APIErrors::ChildError
|
1397
|
+
end
|
1398
|
+
|
1399
|
+
get '/caught_child'
|
1400
|
+
expect(last_response.status).to eql 500
|
1344
1401
|
end
|
1345
1402
|
|
1346
1403
|
it 'does not rescue child errors if rescue_subclasses is false' do
|
@@ -1350,7 +1407,7 @@ describe Grape::API do
|
|
1350
1407
|
subject.get '/uncaught' do
|
1351
1408
|
raise APIErrors::ChildError
|
1352
1409
|
end
|
1353
|
-
|
1410
|
+
expect { get '/uncaught' }.to raise_error(APIErrors::ChildError)
|
1354
1411
|
end
|
1355
1412
|
end
|
1356
1413
|
|
@@ -1362,7 +1419,7 @@ describe Grape::API do
|
|
1362
1419
|
raise "rain!"
|
1363
1420
|
end
|
1364
1421
|
get '/exception'
|
1365
|
-
last_response.body.
|
1422
|
+
expect(last_response.body).to eql "rain!"
|
1366
1423
|
end
|
1367
1424
|
|
1368
1425
|
it 'rescues all errors and return :txt with backtrace' do
|
@@ -1372,7 +1429,7 @@ describe Grape::API do
|
|
1372
1429
|
raise "rain!"
|
1373
1430
|
end
|
1374
1431
|
get '/exception'
|
1375
|
-
last_response.body.start_with?("rain!\r\n").
|
1432
|
+
expect(last_response.body.start_with?("rain!\r\n")).to be true
|
1376
1433
|
end
|
1377
1434
|
|
1378
1435
|
it 'rescues all errors with a default formatter' do
|
@@ -1383,7 +1440,7 @@ describe Grape::API do
|
|
1383
1440
|
raise "rain!"
|
1384
1441
|
end
|
1385
1442
|
get '/exception.foo'
|
1386
|
-
last_response.body.
|
1443
|
+
expect(last_response.body).to start_with "rain!"
|
1387
1444
|
end
|
1388
1445
|
|
1389
1446
|
it 'defaults the error formatter to format' do
|
@@ -1395,9 +1452,9 @@ describe Grape::API do
|
|
1395
1452
|
raise "rain!"
|
1396
1453
|
end
|
1397
1454
|
get '/exception.json'
|
1398
|
-
last_response.body.
|
1455
|
+
expect(last_response.body).to eq('{"error":"rain!"}')
|
1399
1456
|
get '/exception.foo'
|
1400
|
-
last_response.body.
|
1457
|
+
expect(last_response.body).to eq('{"error":"rain!"}')
|
1401
1458
|
end
|
1402
1459
|
|
1403
1460
|
context 'class' do
|
@@ -1415,7 +1472,7 @@ describe Grape::API do
|
|
1415
1472
|
raise "rain!"
|
1416
1473
|
end
|
1417
1474
|
get '/exception'
|
1418
|
-
last_response.body.
|
1475
|
+
expect(last_response.body).to eq("message: rain! @backtrace")
|
1419
1476
|
end
|
1420
1477
|
end
|
1421
1478
|
|
@@ -1435,7 +1492,7 @@ describe Grape::API do
|
|
1435
1492
|
subject.get('/exception') { raise "rain!" }
|
1436
1493
|
|
1437
1494
|
get '/exception'
|
1438
|
-
last_response.body.
|
1495
|
+
expect(last_response.body).to eq('message: rain! @backtrace')
|
1439
1496
|
end
|
1440
1497
|
end
|
1441
1498
|
end
|
@@ -1447,7 +1504,7 @@ describe Grape::API do
|
|
1447
1504
|
raise "rain!"
|
1448
1505
|
end
|
1449
1506
|
get '/exception'
|
1450
|
-
last_response.body.
|
1507
|
+
expect(last_response.body).to eql '{"error":"rain!"}'
|
1451
1508
|
end
|
1452
1509
|
it 'rescues all errors and return :json with backtrace' do
|
1453
1510
|
subject.rescue_from :all, backtrace: true
|
@@ -1457,8 +1514,8 @@ describe Grape::API do
|
|
1457
1514
|
end
|
1458
1515
|
get '/exception'
|
1459
1516
|
json = MultiJson.load(last_response.body)
|
1460
|
-
json["error"].
|
1461
|
-
json["backtrace"].length.
|
1517
|
+
expect(json["error"]).to eql 'rain!'
|
1518
|
+
expect(json["backtrace"].length).to be > 0
|
1462
1519
|
end
|
1463
1520
|
it 'rescues error! and return txt' do
|
1464
1521
|
subject.format :txt
|
@@ -1466,7 +1523,7 @@ describe Grape::API do
|
|
1466
1523
|
error!("Access Denied", 401)
|
1467
1524
|
end
|
1468
1525
|
get '/error'
|
1469
|
-
last_response.body.
|
1526
|
+
expect(last_response.body).to eql "Access Denied"
|
1470
1527
|
end
|
1471
1528
|
it 'rescues error! and return json' do
|
1472
1529
|
subject.format :json
|
@@ -1474,7 +1531,7 @@ describe Grape::API do
|
|
1474
1531
|
error!("Access Denied", 401)
|
1475
1532
|
end
|
1476
1533
|
get '/error'
|
1477
|
-
last_response.body.
|
1534
|
+
expect(last_response.body).to eql '{"error":"Access Denied"}'
|
1478
1535
|
end
|
1479
1536
|
end
|
1480
1537
|
|
@@ -1485,7 +1542,7 @@ describe Grape::API do
|
|
1485
1542
|
"some binary content"
|
1486
1543
|
end
|
1487
1544
|
get '/excel.xls'
|
1488
|
-
last_response.content_type.
|
1545
|
+
expect(last_response.content_type).to eq("application/vnd.ms-excel")
|
1489
1546
|
end
|
1490
1547
|
it 'allows to override content-type' do
|
1491
1548
|
subject.get :content do
|
@@ -1493,7 +1550,7 @@ describe Grape::API do
|
|
1493
1550
|
"var x = 1;"
|
1494
1551
|
end
|
1495
1552
|
get '/content'
|
1496
|
-
last_response.content_type.
|
1553
|
+
expect(last_response.content_type).to eq("text/javascript")
|
1497
1554
|
end
|
1498
1555
|
it 'removes existing content types' do
|
1499
1556
|
subject.content_type :xls, "application/vnd.ms-excel"
|
@@ -1501,8 +1558,8 @@ describe Grape::API do
|
|
1501
1558
|
"some binary content"
|
1502
1559
|
end
|
1503
1560
|
get '/excel.json'
|
1504
|
-
last_response.status.
|
1505
|
-
last_response.body.
|
1561
|
+
expect(last_response.status).to eq(406)
|
1562
|
+
expect(last_response.body).to eq("The requested format 'txt' is not supported.")
|
1506
1563
|
end
|
1507
1564
|
end
|
1508
1565
|
|
@@ -1517,11 +1574,11 @@ describe Grape::API do
|
|
1517
1574
|
end
|
1518
1575
|
it 'sets one formatter' do
|
1519
1576
|
get '/simple.json'
|
1520
|
-
last_response.body.
|
1577
|
+
expect(last_response.body).to eql '{"custom_formatter":"hash"}'
|
1521
1578
|
end
|
1522
1579
|
it 'sets another formatter' do
|
1523
1580
|
get '/simple.txt'
|
1524
|
-
last_response.body.
|
1581
|
+
expect(last_response.body).to eql 'custom_formatter: hash'
|
1525
1582
|
end
|
1526
1583
|
end
|
1527
1584
|
context 'custom formatter' do
|
@@ -1535,11 +1592,11 @@ describe Grape::API do
|
|
1535
1592
|
end
|
1536
1593
|
it 'uses json' do
|
1537
1594
|
get '/simple.json'
|
1538
|
-
last_response.body.
|
1595
|
+
expect(last_response.body).to eql '{"some":"hash"}'
|
1539
1596
|
end
|
1540
1597
|
it 'uses custom formatter' do
|
1541
1598
|
get '/simple.custom', 'HTTP_ACCEPT' => 'application/custom'
|
1542
|
-
last_response.body.
|
1599
|
+
expect(last_response.body).to eql '{"custom_formatter":"hash"}'
|
1543
1600
|
end
|
1544
1601
|
end
|
1545
1602
|
context 'custom formatter class' do
|
@@ -1558,11 +1615,11 @@ describe Grape::API do
|
|
1558
1615
|
end
|
1559
1616
|
it 'uses json' do
|
1560
1617
|
get '/simple.json'
|
1561
|
-
last_response.body.
|
1618
|
+
expect(last_response.body).to eql '{"some":"hash"}'
|
1562
1619
|
end
|
1563
1620
|
it 'uses custom formatter' do
|
1564
1621
|
get '/simple.custom', 'HTTP_ACCEPT' => 'application/custom'
|
1565
|
-
last_response.body.
|
1622
|
+
expect(last_response.body).to eql '{"custom_formatter":"hash"}'
|
1566
1623
|
end
|
1567
1624
|
end
|
1568
1625
|
end
|
@@ -1574,8 +1631,8 @@ describe Grape::API do
|
|
1574
1631
|
{ x: params[:x] }
|
1575
1632
|
end
|
1576
1633
|
post "/data", '{"x":42}', 'CONTENT_TYPE' => 'application/json'
|
1577
|
-
last_response.status.
|
1578
|
-
last_response.body.
|
1634
|
+
expect(last_response.status).to eq(201)
|
1635
|
+
expect(last_response.body).to eq('{"x":42}')
|
1579
1636
|
end
|
1580
1637
|
context 'lambda parser' do
|
1581
1638
|
before :each do
|
@@ -1589,8 +1646,8 @@ describe Grape::API do
|
|
1589
1646
|
["text/custom", "text/custom; charset=UTF-8"].each do |content_type|
|
1590
1647
|
it "uses parser for #{content_type}" do
|
1591
1648
|
put '/simple', "simple", "CONTENT_TYPE" => content_type
|
1592
|
-
last_response.status.
|
1593
|
-
last_response.body.
|
1649
|
+
expect(last_response.status).to eq(200)
|
1650
|
+
expect(last_response.body).to eql "elpmis"
|
1594
1651
|
end
|
1595
1652
|
end
|
1596
1653
|
end
|
@@ -1610,8 +1667,8 @@ describe Grape::API do
|
|
1610
1667
|
end
|
1611
1668
|
it 'uses custom parser' do
|
1612
1669
|
put '/simple', "simple", "CONTENT_TYPE" => "text/custom"
|
1613
|
-
last_response.status.
|
1614
|
-
last_response.body.
|
1670
|
+
expect(last_response.status).to eq(200)
|
1671
|
+
expect(last_response.body).to eql "elpmis"
|
1615
1672
|
end
|
1616
1673
|
end
|
1617
1674
|
context "multi_xml" do
|
@@ -1620,8 +1677,8 @@ describe Grape::API do
|
|
1620
1677
|
params[:tag]
|
1621
1678
|
end
|
1622
1679
|
put '/yaml', '<tag type="symbol">a123</tag>', "CONTENT_TYPE" => "application/xml"
|
1623
|
-
last_response.status.
|
1624
|
-
last_response.body.
|
1680
|
+
expect(last_response.status).to eq(400)
|
1681
|
+
expect(last_response.body).to eql 'Disallowed type attribute: "symbol"'
|
1625
1682
|
end
|
1626
1683
|
end
|
1627
1684
|
context "none parser class" do
|
@@ -1633,8 +1690,8 @@ describe Grape::API do
|
|
1633
1690
|
end
|
1634
1691
|
it "does not parse data" do
|
1635
1692
|
put '/data', 'not valid json', "CONTENT_TYPE" => "application/json"
|
1636
|
-
last_response.status.
|
1637
|
-
last_response.body.
|
1693
|
+
expect(last_response.status).to eq(200)
|
1694
|
+
expect(last_response.body).to eq("body: not valid json")
|
1638
1695
|
end
|
1639
1696
|
end
|
1640
1697
|
end
|
@@ -1649,16 +1706,16 @@ describe Grape::API do
|
|
1649
1706
|
{ x: 42 }
|
1650
1707
|
end
|
1651
1708
|
get "/data"
|
1652
|
-
last_response.status.
|
1653
|
-
last_response.body.
|
1709
|
+
expect(last_response.status).to eq(200)
|
1710
|
+
expect(last_response.body).to eq('{"x":42}')
|
1654
1711
|
end
|
1655
1712
|
it 'parses data in default format' do
|
1656
1713
|
subject.post '/data' do
|
1657
1714
|
{ x: params[:x] }
|
1658
1715
|
end
|
1659
1716
|
post "/data", '{"x":42}', "CONTENT_TYPE" => ""
|
1660
|
-
last_response.status.
|
1661
|
-
last_response.body.
|
1717
|
+
expect(last_response.status).to eq(201)
|
1718
|
+
expect(last_response.body).to eq('{"x":42}')
|
1662
1719
|
end
|
1663
1720
|
end
|
1664
1721
|
|
@@ -1670,7 +1727,7 @@ describe Grape::API do
|
|
1670
1727
|
raise "rain!"
|
1671
1728
|
end
|
1672
1729
|
get '/exception'
|
1673
|
-
last_response.status.
|
1730
|
+
expect(last_response.status).to eql 200
|
1674
1731
|
end
|
1675
1732
|
it 'has a default error status' do
|
1676
1733
|
subject.rescue_from :all
|
@@ -1678,7 +1735,7 @@ describe Grape::API do
|
|
1678
1735
|
raise "rain!"
|
1679
1736
|
end
|
1680
1737
|
get '/exception'
|
1681
|
-
last_response.status.
|
1738
|
+
expect(last_response.status).to eql 500
|
1682
1739
|
end
|
1683
1740
|
it 'uses the default error status in error!' do
|
1684
1741
|
subject.rescue_from :all
|
@@ -1687,14 +1744,14 @@ describe Grape::API do
|
|
1687
1744
|
error! "rain!"
|
1688
1745
|
end
|
1689
1746
|
get '/exception'
|
1690
|
-
last_response.status.
|
1747
|
+
expect(last_response.status).to eql 400
|
1691
1748
|
end
|
1692
1749
|
end
|
1693
1750
|
|
1694
1751
|
context 'routes' do
|
1695
1752
|
describe 'empty api structure' do
|
1696
1753
|
it 'returns an empty array of routes' do
|
1697
|
-
subject.routes.
|
1754
|
+
expect(subject.routes).to eq([])
|
1698
1755
|
end
|
1699
1756
|
end
|
1700
1757
|
describe 'single method api structure' do
|
@@ -1704,11 +1761,11 @@ describe Grape::API do
|
|
1704
1761
|
end
|
1705
1762
|
end
|
1706
1763
|
it 'returns one route' do
|
1707
|
-
subject.routes.size.
|
1764
|
+
expect(subject.routes.size).to eq(1)
|
1708
1765
|
route = subject.routes[0]
|
1709
|
-
route.route_version.
|
1710
|
-
route.route_path.
|
1711
|
-
route.route_method.
|
1766
|
+
expect(route.route_version).to be_nil
|
1767
|
+
expect(route.route_path).to eq("/ping(.:format)")
|
1768
|
+
expect(route.route_method).to eq("GET")
|
1712
1769
|
end
|
1713
1770
|
end
|
1714
1771
|
describe 'api structure with two versions and a namespace' do
|
@@ -1729,25 +1786,25 @@ describe Grape::API do
|
|
1729
1786
|
end
|
1730
1787
|
end
|
1731
1788
|
it 'returns the latest version set' do
|
1732
|
-
subject.version.
|
1789
|
+
expect(subject.version).to eq('v2')
|
1733
1790
|
end
|
1734
1791
|
it 'returns versions' do
|
1735
|
-
subject.versions.
|
1792
|
+
expect(subject.versions).to eq(['v1', 'v2'])
|
1736
1793
|
end
|
1737
1794
|
it 'sets route paths' do
|
1738
|
-
subject.routes.size.
|
1739
|
-
subject.routes[0].route_path.
|
1740
|
-
subject.routes[1].route_path.
|
1795
|
+
expect(subject.routes.size).to be >= 2
|
1796
|
+
expect(subject.routes[0].route_path).to eq("/:version/version(.:format)")
|
1797
|
+
expect(subject.routes[1].route_path).to eq("/p/:version/n1/n2/version(.:format)")
|
1741
1798
|
end
|
1742
1799
|
it 'sets route versions' do
|
1743
|
-
subject.routes[0].route_version.
|
1744
|
-
subject.routes[1].route_version.
|
1800
|
+
expect(subject.routes[0].route_version).to eq('v1')
|
1801
|
+
expect(subject.routes[1].route_version).to eq('v2')
|
1745
1802
|
end
|
1746
1803
|
it 'sets a nested namespace' do
|
1747
|
-
subject.routes[1].route_namespace.
|
1804
|
+
expect(subject.routes[1].route_namespace).to eq("/n1/n2")
|
1748
1805
|
end
|
1749
1806
|
it 'sets prefix' do
|
1750
|
-
subject.routes[1].route_prefix.
|
1807
|
+
expect(subject.routes[1].route_prefix).to eq('p')
|
1751
1808
|
end
|
1752
1809
|
end
|
1753
1810
|
describe 'api structure with additional parameters' do
|
@@ -1758,16 +1815,16 @@ describe Grape::API do
|
|
1758
1815
|
end
|
1759
1816
|
it 'splits a string' do
|
1760
1817
|
get "/split/a,b,c.json", token: ','
|
1761
|
-
last_response.body.
|
1818
|
+
expect(last_response.body).to eq('["a","b","c"]')
|
1762
1819
|
end
|
1763
1820
|
it 'splits a string with limit' do
|
1764
1821
|
get "/split/a,b,c.json", token: ',', limit: '2'
|
1765
|
-
last_response.body.
|
1822
|
+
expect(last_response.body).to eq('["a","b,c"]')
|
1766
1823
|
end
|
1767
1824
|
it 'sets route_params' do
|
1768
|
-
subject.routes.map { |route|
|
1825
|
+
expect(subject.routes.map { |route|
|
1769
1826
|
{ params: route.route_params, optional_params: route.route_optional_params }
|
1770
|
-
}.
|
1827
|
+
}).to eq [
|
1771
1828
|
{ params: { "string" => "", "token" => "a token" }, optional_params: { "limit" => "the limit" } }
|
1772
1829
|
]
|
1773
1830
|
end
|
@@ -1776,30 +1833,30 @@ describe Grape::API do
|
|
1776
1833
|
|
1777
1834
|
context 'desc' do
|
1778
1835
|
it 'empty array of routes' do
|
1779
|
-
subject.routes.
|
1836
|
+
expect(subject.routes).to eq([])
|
1780
1837
|
end
|
1781
1838
|
it 'empty array of routes' do
|
1782
1839
|
subject.desc "grape api"
|
1783
|
-
subject.routes.
|
1840
|
+
expect(subject.routes).to eq([])
|
1784
1841
|
end
|
1785
1842
|
it 'describes a method' do
|
1786
1843
|
subject.desc "first method"
|
1787
1844
|
subject.get :first do ; end
|
1788
|
-
subject.routes.length.
|
1845
|
+
expect(subject.routes.length).to eq(1)
|
1789
1846
|
route = subject.routes.first
|
1790
|
-
route.route_description.
|
1791
|
-
route.route_foo.
|
1792
|
-
route.route_params.
|
1847
|
+
expect(route.route_description).to eq("first method")
|
1848
|
+
expect(route.route_foo).to be_nil
|
1849
|
+
expect(route.route_params).to eq({})
|
1793
1850
|
end
|
1794
1851
|
it 'describes methods separately' do
|
1795
1852
|
subject.desc "first method"
|
1796
1853
|
subject.get :first do ; end
|
1797
1854
|
subject.desc "second method"
|
1798
1855
|
subject.get :second do ; end
|
1799
|
-
subject.routes.count.
|
1800
|
-
subject.routes.map { |route|
|
1856
|
+
expect(subject.routes.count).to eq(2)
|
1857
|
+
expect(subject.routes.map { |route|
|
1801
1858
|
{ description: route.route_description, params: route.route_params }
|
1802
|
-
}.
|
1859
|
+
}).to eq [
|
1803
1860
|
{ description: "first method", params: {} },
|
1804
1861
|
{ description: "second method", params: {} }
|
1805
1862
|
]
|
@@ -1808,9 +1865,9 @@ describe Grape::API do
|
|
1808
1865
|
subject.desc "first method"
|
1809
1866
|
subject.get :first do ; end
|
1810
1867
|
subject.get :second do ; end
|
1811
|
-
subject.routes.map { |route|
|
1868
|
+
expect(subject.routes.map { |route|
|
1812
1869
|
{ description: route.route_description, params: route.route_params }
|
1813
|
-
}.
|
1870
|
+
}).to eq [
|
1814
1871
|
{ description: "first method", params: {} },
|
1815
1872
|
{ description: nil, params: {} }
|
1816
1873
|
]
|
@@ -1820,19 +1877,19 @@ describe Grape::API do
|
|
1820
1877
|
desc "ns second", foo: "bar"
|
1821
1878
|
get 'second' do ; end
|
1822
1879
|
end
|
1823
|
-
subject.routes.map { |route|
|
1880
|
+
expect(subject.routes.map { |route|
|
1824
1881
|
{ description: route.route_description, foo: route.route_foo, params: route.route_params }
|
1825
|
-
}.
|
1826
|
-
{ description: "ns second", foo: "bar", params: {} }
|
1882
|
+
}).to eq [
|
1883
|
+
{ description: "ns second", foo: "bar", params: {} }
|
1827
1884
|
]
|
1828
1885
|
end
|
1829
1886
|
it 'includes details' do
|
1830
1887
|
subject.desc "method", details: "method details"
|
1831
1888
|
subject.get 'method' do ; end
|
1832
|
-
subject.routes.map { |route|
|
1889
|
+
expect(subject.routes.map { |route|
|
1833
1890
|
{ description: route.route_description, details: route.route_details, params: route.route_params }
|
1834
|
-
}.
|
1835
|
-
{ description: "method", details: "method details", params: {} }
|
1891
|
+
}).to eq [
|
1892
|
+
{ description: "method", details: "method details", params: {} }
|
1836
1893
|
]
|
1837
1894
|
end
|
1838
1895
|
it 'describes a method with parameters' do
|
@@ -1840,9 +1897,9 @@ describe Grape::API do
|
|
1840
1897
|
subject.get 'reverse' do
|
1841
1898
|
params[:s].reverse
|
1842
1899
|
end
|
1843
|
-
subject.routes.map { |route|
|
1900
|
+
expect(subject.routes.map { |route|
|
1844
1901
|
{ description: route.route_description, params: route.route_params }
|
1845
|
-
}.
|
1902
|
+
}).to eq [
|
1846
1903
|
{ description: "Reverses a string.", params: { "s" => { desc: "string to reverse", type: "string" } } }
|
1847
1904
|
]
|
1848
1905
|
end
|
@@ -1858,9 +1915,9 @@ describe Grape::API do
|
|
1858
1915
|
end
|
1859
1916
|
get 'method' do ; end
|
1860
1917
|
end
|
1861
|
-
subject.routes.map { |route|
|
1918
|
+
expect(subject.routes.map { |route|
|
1862
1919
|
{ description: route.route_description, params: route.route_params }
|
1863
|
-
}.
|
1920
|
+
}).to eq [
|
1864
1921
|
{ description: "method",
|
1865
1922
|
params: {
|
1866
1923
|
"ns_param" => { required: true, desc: "namespace parameter" },
|
@@ -1889,9 +1946,9 @@ describe Grape::API do
|
|
1889
1946
|
get 'method' do ; end
|
1890
1947
|
end
|
1891
1948
|
end
|
1892
|
-
subject.routes.map { |route|
|
1949
|
+
expect(subject.routes.map { |route|
|
1893
1950
|
{ description: route.route_description, params: route.route_params }
|
1894
|
-
}.
|
1951
|
+
}).to eq [
|
1895
1952
|
{ description: "method",
|
1896
1953
|
params: {
|
1897
1954
|
"ns_param" => { required: true, desc: "ns param 2" },
|
@@ -1916,9 +1973,9 @@ describe Grape::API do
|
|
1916
1973
|
end
|
1917
1974
|
subject.get "method" do ; end
|
1918
1975
|
|
1919
|
-
subject.routes.map { |route|
|
1976
|
+
expect(subject.routes.map { |route|
|
1920
1977
|
route.route_params
|
1921
|
-
}.
|
1978
|
+
}).to eq [{
|
1922
1979
|
"group1" => { required: true, type: "Array" },
|
1923
1980
|
"group1[param1]" => { required: false, desc: "group1 param1 desc" },
|
1924
1981
|
"group1[param2]" => { required: true, desc: "group1 param2 desc" },
|
@@ -1936,9 +1993,9 @@ describe Grape::API do
|
|
1936
1993
|
end
|
1937
1994
|
end
|
1938
1995
|
subject.get 'method' do ; end
|
1939
|
-
subject.routes.map { |route|
|
1996
|
+
expect(subject.routes.map { |route|
|
1940
1997
|
{ description: route.route_description, params: route.route_params }
|
1941
|
-
}.
|
1998
|
+
}).to eq [
|
1942
1999
|
{ description: "nesting",
|
1943
2000
|
params: {
|
1944
2001
|
"root_param" => { required: true, desc: "root param" },
|
@@ -1960,9 +2017,9 @@ describe Grape::API do
|
|
1960
2017
|
requires :one_param, desc: "one param"
|
1961
2018
|
end
|
1962
2019
|
subject.get 'method' do ; end
|
1963
|
-
subject.routes.map { |route|
|
2020
|
+
expect(subject.routes.map { |route|
|
1964
2021
|
{ description: route.route_description, params: route.route_params }
|
1965
|
-
}.
|
2022
|
+
}).to eq [
|
1966
2023
|
{ description: nil, params: { "one_param" => { required: true, desc: "one param" } } }
|
1967
2024
|
]
|
1968
2025
|
end
|
@@ -1971,9 +2028,9 @@ describe Grape::API do
|
|
1971
2028
|
subject.get 'reverse/:s' do
|
1972
2029
|
params[:s].reverse
|
1973
2030
|
end
|
1974
|
-
subject.routes.map { |route|
|
2031
|
+
expect(subject.routes.map { |route|
|
1975
2032
|
{ description: route.route_description, params: route.route_params }
|
1976
|
-
}.
|
2033
|
+
}).to eq [
|
1977
2034
|
{ description: "Reverses a string.", params: { "s" => { desc: "string to reverse", type: "string" } } }
|
1978
2035
|
]
|
1979
2036
|
end
|
@@ -1989,12 +2046,12 @@ describe Grape::API do
|
|
1989
2046
|
|
1990
2047
|
it 'makes a bare Rack app available at the endpoint' do
|
1991
2048
|
get '/mounty'
|
1992
|
-
last_response.body.
|
2049
|
+
expect(last_response.body).to eq('MOUNTED')
|
1993
2050
|
end
|
1994
2051
|
|
1995
2052
|
it 'anchors the routes, passing all subroutes to it' do
|
1996
2053
|
get '/mounty/awesome'
|
1997
|
-
last_response.body.
|
2054
|
+
expect(last_response.body).to eq('MOUNTED')
|
1998
2055
|
end
|
1999
2056
|
|
2000
2057
|
it 'is able to cascade' do
|
@@ -2005,9 +2062,9 @@ describe Grape::API do
|
|
2005
2062
|
} => '/'
|
2006
2063
|
|
2007
2064
|
get '/boo'
|
2008
|
-
last_response.body.
|
2065
|
+
expect(last_response.body).to eq('Farfegnugen')
|
2009
2066
|
get '/mounty'
|
2010
|
-
last_response.body.
|
2067
|
+
expect(last_response.body).to eq('MOUNTED')
|
2011
2068
|
end
|
2012
2069
|
end
|
2013
2070
|
|
@@ -2015,7 +2072,7 @@ describe Grape::API do
|
|
2015
2072
|
it 'calls through setting the route to "/"' do
|
2016
2073
|
subject.mount mounted_app
|
2017
2074
|
get '/'
|
2018
|
-
last_response.body.
|
2075
|
+
expect(last_response.body).to eq('MOUNTED')
|
2019
2076
|
end
|
2020
2077
|
end
|
2021
2078
|
|
@@ -2033,7 +2090,7 @@ describe Grape::API do
|
|
2033
2090
|
end
|
2034
2091
|
|
2035
2092
|
get '/v1/cool/awesome'
|
2036
|
-
last_response.body.
|
2093
|
+
expect(last_response.body).to eq('yo')
|
2037
2094
|
end
|
2038
2095
|
|
2039
2096
|
it 'applies the settings to nested mounted apis' do
|
@@ -2051,7 +2108,7 @@ describe Grape::API do
|
|
2051
2108
|
end
|
2052
2109
|
|
2053
2110
|
get '/v1/cool/awesome'
|
2054
|
-
last_response.body.
|
2111
|
+
expect(last_response.body).to eq('yo')
|
2055
2112
|
end
|
2056
2113
|
|
2057
2114
|
it 'inherits rescues even when some defined by mounted' do
|
@@ -2065,8 +2122,8 @@ describe Grape::API do
|
|
2065
2122
|
mount app
|
2066
2123
|
end
|
2067
2124
|
get '/mounted/fail'
|
2068
|
-
last_response.status.
|
2069
|
-
last_response.body.
|
2125
|
+
expect(last_response.status).to eql 202
|
2126
|
+
expect(last_response.body).to eq('rescued from doh!')
|
2070
2127
|
end
|
2071
2128
|
|
2072
2129
|
it 'collects the routes of the mounted api' do
|
@@ -2076,9 +2133,9 @@ describe Grape::API do
|
|
2076
2133
|
app.post('/sauce') {}
|
2077
2134
|
mount app
|
2078
2135
|
end
|
2079
|
-
subject.routes.size.
|
2080
|
-
subject.routes.first.route_path.
|
2081
|
-
subject.routes.last.route_path.
|
2136
|
+
expect(subject.routes.size).to eq(2)
|
2137
|
+
expect(subject.routes.first.route_path).to match(%r{\/cool\/awesome})
|
2138
|
+
expect(subject.routes.last.route_path).to match(%r{\/cool\/sauce})
|
2082
2139
|
end
|
2083
2140
|
|
2084
2141
|
it 'mounts on a path' do
|
@@ -2090,8 +2147,8 @@ describe Grape::API do
|
|
2090
2147
|
mount app => '/mounted'
|
2091
2148
|
end
|
2092
2149
|
get "/mounted/cool/awesome"
|
2093
|
-
last_response.status.
|
2094
|
-
last_response.body.
|
2150
|
+
expect(last_response.status).to eq(200)
|
2151
|
+
expect(last_response.body).to eq("sauce")
|
2095
2152
|
end
|
2096
2153
|
|
2097
2154
|
it 'mounts on a nested path' do
|
@@ -2104,10 +2161,10 @@ describe Grape::API do
|
|
2104
2161
|
subject.mount app1 => '/app1'
|
2105
2162
|
app1.mount app2 => '/app2'
|
2106
2163
|
get "/app1/app2/nice"
|
2107
|
-
last_response.status.
|
2108
|
-
last_response.body.
|
2164
|
+
expect(last_response.status).to eq(200)
|
2165
|
+
expect(last_response.body).to eq("play")
|
2109
2166
|
options "/app1/app2/nice"
|
2110
|
-
last_response.status.
|
2167
|
+
expect(last_response.status).to eq(204)
|
2111
2168
|
end
|
2112
2169
|
|
2113
2170
|
it 'responds to options' do
|
@@ -2124,15 +2181,15 @@ describe Grape::API do
|
|
2124
2181
|
mount app
|
2125
2182
|
end
|
2126
2183
|
get '/apples/colour'
|
2127
|
-
last_response.status.
|
2128
|
-
last_response.body.
|
2184
|
+
expect(last_response.status).to eql 200
|
2185
|
+
expect(last_response.body).to eq('red')
|
2129
2186
|
options '/apples/colour'
|
2130
|
-
last_response.status.
|
2187
|
+
expect(last_response.status).to eql 204
|
2131
2188
|
get '/apples/pears/colour'
|
2132
|
-
last_response.status.
|
2133
|
-
last_response.body.
|
2189
|
+
expect(last_response.status).to eql 200
|
2190
|
+
expect(last_response.body).to eq('green')
|
2134
2191
|
options '/apples/pears/colour'
|
2135
|
-
last_response.status.
|
2192
|
+
expect(last_response.status).to eql 204
|
2136
2193
|
end
|
2137
2194
|
|
2138
2195
|
it 'responds to options with path versioning' do
|
@@ -2146,10 +2203,10 @@ describe Grape::API do
|
|
2146
2203
|
end
|
2147
2204
|
|
2148
2205
|
get '/v1/apples/colour'
|
2149
|
-
last_response.status.
|
2150
|
-
last_response.body.
|
2206
|
+
expect(last_response.status).to eql 200
|
2207
|
+
expect(last_response.body).to eq('red')
|
2151
2208
|
options '/v1/apples/colour'
|
2152
|
-
last_response.status.
|
2209
|
+
expect(last_response.status).to eql 204
|
2153
2210
|
end
|
2154
2211
|
|
2155
2212
|
end
|
@@ -2159,15 +2216,15 @@ describe Grape::API do
|
|
2159
2216
|
it 'adds one for each route created' do
|
2160
2217
|
subject.get '/'
|
2161
2218
|
subject.post '/'
|
2162
|
-
subject.endpoints.size.
|
2219
|
+
expect(subject.endpoints.size).to eq(2)
|
2163
2220
|
end
|
2164
2221
|
end
|
2165
2222
|
|
2166
2223
|
describe '.compile' do
|
2167
2224
|
it 'sets the instance' do
|
2168
|
-
subject.instance.
|
2225
|
+
expect(subject.instance).to be_nil
|
2169
2226
|
subject.compile
|
2170
|
-
subject.instance.
|
2227
|
+
expect(subject.instance).to be_kind_of(subject)
|
2171
2228
|
end
|
2172
2229
|
end
|
2173
2230
|
|
@@ -2175,7 +2232,7 @@ describe Grape::API do
|
|
2175
2232
|
it 'invalidates any compiled instance' do
|
2176
2233
|
subject.compile
|
2177
2234
|
subject.change!
|
2178
|
-
subject.instance.
|
2235
|
+
expect(subject.instance).to be_nil
|
2179
2236
|
end
|
2180
2237
|
end
|
2181
2238
|
|
@@ -2192,9 +2249,9 @@ describe Grape::API do
|
|
2192
2249
|
it 'path' do
|
2193
2250
|
get '/endpoint/options'
|
2194
2251
|
options = MultiJson.load(last_response.body)
|
2195
|
-
options["path"].
|
2196
|
-
options["source_location"][0].
|
2197
|
-
options["source_location"][1].to_i.
|
2252
|
+
expect(options["path"]).to eq(["/endpoint/options"])
|
2253
|
+
expect(options["source_location"][0]).to include "api_spec.rb"
|
2254
|
+
expect(options["source_location"][1].to_i).to be > 0
|
2198
2255
|
end
|
2199
2256
|
end
|
2200
2257
|
|
@@ -2210,9 +2267,9 @@ describe Grape::API do
|
|
2210
2267
|
end
|
2211
2268
|
it 'provides access to route info' do
|
2212
2269
|
get '/'
|
2213
|
-
last_response.body.
|
2270
|
+
expect(last_response.body).to eq("/(.:format)")
|
2214
2271
|
get '/path'
|
2215
|
-
last_response.body.
|
2272
|
+
expect(last_response.body).to eq("/path(.:format)")
|
2216
2273
|
end
|
2217
2274
|
end
|
2218
2275
|
context 'with desc' do
|
@@ -2228,11 +2285,11 @@ describe Grape::API do
|
|
2228
2285
|
end
|
2229
2286
|
it 'returns route description' do
|
2230
2287
|
get '/description'
|
2231
|
-
last_response.body.
|
2288
|
+
expect(last_response.body).to eq("returns description")
|
2232
2289
|
end
|
2233
2290
|
it 'returns route parameters' do
|
2234
2291
|
get '/params/x'
|
2235
|
-
last_response.body.
|
2292
|
+
expect(last_response.body).to eq("y")
|
2236
2293
|
end
|
2237
2294
|
end
|
2238
2295
|
end
|
@@ -2247,15 +2304,15 @@ describe Grape::API do
|
|
2247
2304
|
end
|
2248
2305
|
it 'forces txt without an extension' do
|
2249
2306
|
get '/meaning_of_life'
|
2250
|
-
last_response.body.
|
2307
|
+
expect(last_response.body).to eq({ meaning_of_life: 42 }.to_s)
|
2251
2308
|
end
|
2252
2309
|
it 'does not force txt with an extension' do
|
2253
2310
|
get '/meaning_of_life.json'
|
2254
|
-
last_response.body.
|
2311
|
+
expect(last_response.body).to eq({ meaning_of_life: 42 }.to_json)
|
2255
2312
|
end
|
2256
2313
|
it 'forces txt from a non-accepting header' do
|
2257
2314
|
get '/meaning_of_life', {}, 'HTTP_ACCEPT' => 'application/json'
|
2258
|
-
last_response.body.
|
2315
|
+
expect(last_response.body).to eq({ meaning_of_life: 42 }.to_s)
|
2259
2316
|
end
|
2260
2317
|
end
|
2261
2318
|
context ':txt only' do
|
@@ -2267,15 +2324,15 @@ describe Grape::API do
|
|
2267
2324
|
end
|
2268
2325
|
it 'forces txt without an extension' do
|
2269
2326
|
get '/meaning_of_life'
|
2270
|
-
last_response.body.
|
2327
|
+
expect(last_response.body).to eq({ meaning_of_life: 42 }.to_s)
|
2271
2328
|
end
|
2272
2329
|
it 'forces txt with the wrong extension' do
|
2273
2330
|
get '/meaning_of_life.json'
|
2274
|
-
last_response.body.
|
2331
|
+
expect(last_response.body).to eq({ meaning_of_life: 42 }.to_s)
|
2275
2332
|
end
|
2276
2333
|
it 'forces txt from a non-accepting header' do
|
2277
2334
|
get '/meaning_of_life', {}, 'HTTP_ACCEPT' => 'application/json'
|
2278
|
-
last_response.body.
|
2335
|
+
expect(last_response.body).to eq({ meaning_of_life: 42 }.to_s)
|
2279
2336
|
end
|
2280
2337
|
end
|
2281
2338
|
context ':json' do
|
@@ -2288,15 +2345,15 @@ describe Grape::API do
|
|
2288
2345
|
end
|
2289
2346
|
it 'forces json without an extension' do
|
2290
2347
|
get '/meaning_of_life'
|
2291
|
-
last_response.body.
|
2348
|
+
expect(last_response.body).to eq({ meaning_of_life: 42 }.to_json)
|
2292
2349
|
end
|
2293
2350
|
it 'does not force json with an extension' do
|
2294
2351
|
get '/meaning_of_life.txt'
|
2295
|
-
last_response.body.
|
2352
|
+
expect(last_response.body).to eq({ meaning_of_life: 42 }.to_s)
|
2296
2353
|
end
|
2297
2354
|
it 'forces json from a non-accepting header' do
|
2298
2355
|
get '/meaning_of_life', {}, 'HTTP_ACCEPT' => 'text/html'
|
2299
|
-
last_response.body.
|
2356
|
+
expect(last_response.body).to eq({ meaning_of_life: 42 }.to_json)
|
2300
2357
|
end
|
2301
2358
|
it 'can be overwritten with an explicit content type' do
|
2302
2359
|
subject.get '/meaning_of_life_with_content_type' do
|
@@ -2304,7 +2361,7 @@ describe Grape::API do
|
|
2304
2361
|
{ meaning_of_life: 42 }.to_s
|
2305
2362
|
end
|
2306
2363
|
get '/meaning_of_life_with_content_type'
|
2307
|
-
last_response.body.
|
2364
|
+
expect(last_response.body).to eq({ meaning_of_life: 42 }.to_s)
|
2308
2365
|
end
|
2309
2366
|
it 'raised :error from middleware' do
|
2310
2367
|
middleware = Class.new(Grape::Middleware::Base) do
|
@@ -2317,8 +2374,8 @@ describe Grape::API do
|
|
2317
2374
|
|
2318
2375
|
end
|
2319
2376
|
get "/"
|
2320
|
-
last_response.status.
|
2321
|
-
last_response.body.
|
2377
|
+
expect(last_response.status).to eq(42)
|
2378
|
+
expect(last_response.body).to eq({ error: "Unauthorized" }.to_json)
|
2322
2379
|
end
|
2323
2380
|
|
2324
2381
|
end
|
@@ -2336,21 +2393,21 @@ describe Grape::API do
|
|
2336
2393
|
SerializableHashExample.new
|
2337
2394
|
end
|
2338
2395
|
get '/example'
|
2339
|
-
last_response.body.
|
2396
|
+
expect(last_response.body).to eq('{"abc":"def"}')
|
2340
2397
|
end
|
2341
2398
|
it 'root' do
|
2342
2399
|
subject.get '/example' do
|
2343
2400
|
{ "root" => SerializableHashExample.new }
|
2344
2401
|
end
|
2345
2402
|
get '/example'
|
2346
|
-
last_response.body.
|
2403
|
+
expect(last_response.body).to eq('{"root":{"abc":"def"}}')
|
2347
2404
|
end
|
2348
2405
|
it 'array' do
|
2349
2406
|
subject.get '/examples' do
|
2350
2407
|
[SerializableHashExample.new, SerializableHashExample.new]
|
2351
2408
|
end
|
2352
2409
|
get '/examples'
|
2353
|
-
last_response.body.
|
2410
|
+
expect(last_response.body).to eq('[{"abc":"def"},{"abc":"def"}]')
|
2354
2411
|
end
|
2355
2412
|
end
|
2356
2413
|
context ":xml" do
|
@@ -2362,8 +2419,8 @@ describe Grape::API do
|
|
2362
2419
|
"example"
|
2363
2420
|
end
|
2364
2421
|
get '/example'
|
2365
|
-
last_response.status.
|
2366
|
-
last_response.body.
|
2422
|
+
expect(last_response.status).to eq(500)
|
2423
|
+
expect(last_response.body).to eq <<-XML
|
2367
2424
|
<?xml version="1.0" encoding="UTF-8"?>
|
2368
2425
|
<error>
|
2369
2426
|
<message>cannot convert String to xml</message>
|
@@ -2378,8 +2435,8 @@ XML
|
|
2378
2435
|
]
|
2379
2436
|
end
|
2380
2437
|
get '/example'
|
2381
|
-
last_response.status.
|
2382
|
-
last_response.body.
|
2438
|
+
expect(last_response.status).to eq(200)
|
2439
|
+
expect(last_response.body).to eq <<-XML
|
2383
2440
|
<?xml version="1.0" encoding="UTF-8"?>
|
2384
2441
|
<hash>
|
2385
2442
|
<example1>example1</example1>
|
@@ -2392,8 +2449,8 @@ XML
|
|
2392
2449
|
["example1", "example2"]
|
2393
2450
|
end
|
2394
2451
|
get '/example'
|
2395
|
-
last_response.status.
|
2396
|
-
last_response.body.
|
2452
|
+
expect(last_response.status).to eq(200)
|
2453
|
+
expect(last_response.body).to eq <<-XML
|
2397
2454
|
<?xml version="1.0" encoding="UTF-8"?>
|
2398
2455
|
<strings type="array">
|
2399
2456
|
<string>example1</string>
|
@@ -2412,8 +2469,8 @@ XML
|
|
2412
2469
|
|
2413
2470
|
end
|
2414
2471
|
get "/"
|
2415
|
-
last_response.status.
|
2416
|
-
last_response.body.
|
2472
|
+
expect(last_response.status).to eq(42)
|
2473
|
+
expect(last_response.body).to eq <<-XML
|
2417
2474
|
<?xml version="1.0" encoding="UTF-8"?>
|
2418
2475
|
<error>
|
2419
2476
|
<message>Unauthorized</message>
|
@@ -2444,14 +2501,14 @@ XML
|
|
2444
2501
|
error!("Unrecognized request path: #{params[:path] } - #{env['PATH_INFO'] }#{env['SCRIPT_NAME'] }", 404)
|
2445
2502
|
end
|
2446
2503
|
get "/v1/hello"
|
2447
|
-
last_response.status.
|
2448
|
-
last_response.body.
|
2504
|
+
expect(last_response.status).to eq(200)
|
2505
|
+
expect(last_response.body).to eq("v1")
|
2449
2506
|
get "/v2/hello"
|
2450
|
-
last_response.status.
|
2451
|
-
last_response.body.
|
2507
|
+
expect(last_response.status).to eq(200)
|
2508
|
+
expect(last_response.body).to eq("v2")
|
2452
2509
|
get "/foobar"
|
2453
|
-
last_response.status.
|
2454
|
-
last_response.body.
|
2510
|
+
expect(last_response.status).to eq(404)
|
2511
|
+
expect(last_response.body).to eq("Unrecognized request path: foobar - /foobar")
|
2455
2512
|
end
|
2456
2513
|
end
|
2457
2514
|
end
|
@@ -2461,28 +2518,28 @@ XML
|
|
2461
2518
|
it "cascades" do
|
2462
2519
|
subject.version 'v1', using: :path, cascade: true
|
2463
2520
|
get "/v1/hello"
|
2464
|
-
last_response.status.
|
2465
|
-
last_response.headers["X-Cascade"].
|
2521
|
+
expect(last_response.status).to eq(404)
|
2522
|
+
expect(last_response.headers["X-Cascade"]).to eq("pass")
|
2466
2523
|
end
|
2467
2524
|
it "does not cascade" do
|
2468
2525
|
subject.version 'v2', using: :path, cascade: false
|
2469
2526
|
get "/v2/hello"
|
2470
|
-
last_response.status.
|
2471
|
-
last_response.headers.keys.
|
2527
|
+
expect(last_response.status).to eq(404)
|
2528
|
+
expect(last_response.headers.keys).not_to include "X-Cascade"
|
2472
2529
|
end
|
2473
2530
|
end
|
2474
2531
|
context "via endpoint" do
|
2475
2532
|
it "cascades" do
|
2476
2533
|
subject.cascade true
|
2477
2534
|
get "/hello"
|
2478
|
-
last_response.status.
|
2479
|
-
last_response.headers["X-Cascade"].
|
2535
|
+
expect(last_response.status).to eq(404)
|
2536
|
+
expect(last_response.headers["X-Cascade"]).to eq("pass")
|
2480
2537
|
end
|
2481
2538
|
it "does not cascade" do
|
2482
2539
|
subject.cascade false
|
2483
2540
|
get "/hello"
|
2484
|
-
last_response.status.
|
2485
|
-
last_response.headers.keys.
|
2541
|
+
expect(last_response.status).to eq(404)
|
2542
|
+
expect(last_response.headers.keys).not_to include "X-Cascade"
|
2486
2543
|
end
|
2487
2544
|
end
|
2488
2545
|
end
|
@@ -2495,8 +2552,8 @@ XML
|
|
2495
2552
|
'foo'
|
2496
2553
|
end
|
2497
2554
|
get '/something'
|
2498
|
-
last_response.status.
|
2499
|
-
last_response.body.
|
2555
|
+
expect(last_response.status).to eq(406)
|
2556
|
+
expect(last_response.body).to eq("{\"error\":\"The requested format 'txt' is not supported.\"}")
|
2500
2557
|
end
|
2501
2558
|
end
|
2502
2559
|
end
|