grape 1.8.0 → 2.0.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +15 -0
- data/README.md +19 -22
- data/UPGRADING.md +35 -0
- data/grape.gemspec +1 -4
- data/lib/grape/dsl/desc.rb +1 -1
- data/lib/grape/dsl/inside_route.rb +9 -9
- data/lib/grape/endpoint.rb +9 -1
- data/lib/grape/exceptions/missing_group_type.rb +1 -1
- data/lib/grape/exceptions/unsupported_group_type.rb +1 -1
- data/lib/grape/http/headers.rb +12 -2
- data/lib/grape/middleware/auth/strategies.rb +1 -2
- data/lib/grape/middleware/error.rb +4 -4
- data/lib/grape/middleware/formatter.rb +5 -5
- data/lib/grape/railtie.rb +9 -0
- data/lib/grape/request.rb +8 -2
- data/lib/grape/router/route.rb +1 -1
- data/lib/grape/validations/validators/base.rb +1 -1
- data/lib/grape/validations/validators/values_validator.rb +2 -2
- data/lib/grape/version.rb +1 -1
- data/lib/grape.rb +15 -2
- metadata +8 -243
- data/spec/grape/api/custom_validations_spec.rb +0 -213
- data/spec/grape/api/deeply_included_options_spec.rb +0 -56
- data/spec/grape/api/defines_boolean_in_params_spec.rb +0 -38
- data/spec/grape/api/documentation_spec.rb +0 -59
- data/spec/grape/api/inherited_helpers_spec.rb +0 -114
- data/spec/grape/api/instance_spec.rb +0 -103
- data/spec/grape/api/invalid_format_spec.rb +0 -45
- data/spec/grape/api/namespace_parameters_in_route_spec.rb +0 -38
- data/spec/grape/api/nested_helpers_spec.rb +0 -50
- data/spec/grape/api/optional_parameters_in_route_spec.rb +0 -43
- data/spec/grape/api/parameters_modification_spec.rb +0 -41
- data/spec/grape/api/patch_method_helpers_spec.rb +0 -79
- data/spec/grape/api/recognize_path_spec.rb +0 -21
- data/spec/grape/api/required_parameters_in_route_spec.rb +0 -37
- data/spec/grape/api/required_parameters_with_invalid_method_spec.rb +0 -26
- data/spec/grape/api/routes_with_requirements_spec.rb +0 -59
- data/spec/grape/api/shared_helpers_exactly_one_of_spec.rb +0 -41
- data/spec/grape/api/shared_helpers_spec.rb +0 -36
- data/spec/grape/api_remount_spec.rb +0 -509
- data/spec/grape/api_spec.rb +0 -4356
- data/spec/grape/dsl/callbacks_spec.rb +0 -45
- data/spec/grape/dsl/desc_spec.rb +0 -98
- data/spec/grape/dsl/headers_spec.rb +0 -62
- data/spec/grape/dsl/helpers_spec.rb +0 -100
- data/spec/grape/dsl/inside_route_spec.rb +0 -531
- data/spec/grape/dsl/logger_spec.rb +0 -24
- data/spec/grape/dsl/middleware_spec.rb +0 -60
- data/spec/grape/dsl/parameters_spec.rb +0 -180
- data/spec/grape/dsl/request_response_spec.rb +0 -225
- data/spec/grape/dsl/routing_spec.rb +0 -275
- data/spec/grape/dsl/settings_spec.rb +0 -261
- data/spec/grape/dsl/validations_spec.rb +0 -55
- data/spec/grape/endpoint/declared_spec.rb +0 -846
- data/spec/grape/endpoint_spec.rb +0 -1085
- data/spec/grape/entity_spec.rb +0 -336
- data/spec/grape/exceptions/base_spec.rb +0 -81
- data/spec/grape/exceptions/body_parse_errors_spec.rb +0 -185
- data/spec/grape/exceptions/invalid_accept_header_spec.rb +0 -358
- data/spec/grape/exceptions/invalid_formatter_spec.rb +0 -15
- data/spec/grape/exceptions/invalid_response_spec.rb +0 -11
- data/spec/grape/exceptions/invalid_versioner_option_spec.rb +0 -15
- data/spec/grape/exceptions/missing_group_type_spec.rb +0 -17
- data/spec/grape/exceptions/missing_mime_type_spec.rb +0 -17
- data/spec/grape/exceptions/missing_option_spec.rb +0 -15
- data/spec/grape/exceptions/unknown_options_spec.rb +0 -15
- data/spec/grape/exceptions/unknown_validator_spec.rb +0 -15
- data/spec/grape/exceptions/unsupported_group_type_spec.rb +0 -19
- data/spec/grape/exceptions/validation_errors_spec.rb +0 -92
- data/spec/grape/exceptions/validation_spec.rb +0 -19
- data/spec/grape/extensions/param_builders/hash_spec.rb +0 -83
- data/spec/grape/extensions/param_builders/hash_with_indifferent_access_spec.rb +0 -105
- data/spec/grape/extensions/param_builders/hashie/mash_spec.rb +0 -79
- data/spec/grape/grape_spec.rb +0 -9
- data/spec/grape/integration/global_namespace_function_spec.rb +0 -29
- data/spec/grape/integration/rack_sendfile_spec.rb +0 -48
- data/spec/grape/integration/rack_spec.rb +0 -51
- data/spec/grape/loading_spec.rb +0 -44
- data/spec/grape/middleware/auth/base_spec.rb +0 -31
- data/spec/grape/middleware/auth/dsl_spec.rb +0 -60
- data/spec/grape/middleware/auth/strategies_spec.rb +0 -120
- data/spec/grape/middleware/base_spec.rb +0 -221
- data/spec/grape/middleware/error_spec.rb +0 -85
- data/spec/grape/middleware/exception_spec.rb +0 -294
- data/spec/grape/middleware/formatter_spec.rb +0 -461
- data/spec/grape/middleware/globals_spec.rb +0 -30
- data/spec/grape/middleware/stack_spec.rb +0 -155
- data/spec/grape/middleware/versioner/accept_version_header_spec.rb +0 -122
- data/spec/grape/middleware/versioner/header_spec.rb +0 -345
- data/spec/grape/middleware/versioner/param_spec.rb +0 -171
- data/spec/grape/middleware/versioner/path_spec.rb +0 -62
- data/spec/grape/middleware/versioner_spec.rb +0 -21
- data/spec/grape/named_api_spec.rb +0 -19
- data/spec/grape/parser_spec.rb +0 -86
- data/spec/grape/path_spec.rb +0 -252
- data/spec/grape/presenters/presenter_spec.rb +0 -71
- data/spec/grape/request_spec.rb +0 -126
- data/spec/grape/util/inheritable_setting_spec.rb +0 -242
- data/spec/grape/util/inheritable_values_spec.rb +0 -79
- data/spec/grape/util/reverse_stackable_values_spec.rb +0 -134
- data/spec/grape/util/stackable_values_spec.rb +0 -128
- data/spec/grape/util/strict_hash_configuration_spec.rb +0 -38
- data/spec/grape/validations/attributes_doc_spec.rb +0 -153
- data/spec/grape/validations/instance_behaivour_spec.rb +0 -43
- data/spec/grape/validations/multiple_attributes_iterator_spec.rb +0 -38
- data/spec/grape/validations/params_scope_spec.rb +0 -1420
- data/spec/grape/validations/single_attribute_iterator_spec.rb +0 -56
- data/spec/grape/validations/types/array_coercer_spec.rb +0 -33
- data/spec/grape/validations/types/primitive_coercer_spec.rb +0 -150
- data/spec/grape/validations/types/set_coercer_spec.rb +0 -32
- data/spec/grape/validations/types_spec.rb +0 -111
- data/spec/grape/validations/validators/all_or_none_spec.rb +0 -162
- data/spec/grape/validations/validators/allow_blank_spec.rb +0 -575
- data/spec/grape/validations/validators/at_least_one_of_spec.rb +0 -205
- data/spec/grape/validations/validators/base_spec.rb +0 -38
- data/spec/grape/validations/validators/coerce_spec.rb +0 -1261
- data/spec/grape/validations/validators/default_spec.rb +0 -463
- data/spec/grape/validations/validators/exactly_one_of_spec.rb +0 -233
- data/spec/grape/validations/validators/except_values_spec.rb +0 -192
- data/spec/grape/validations/validators/mutual_exclusion_spec.rb +0 -214
- data/spec/grape/validations/validators/presence_spec.rb +0 -315
- data/spec/grape/validations/validators/regexp_spec.rb +0 -161
- data/spec/grape/validations/validators/same_as_spec.rb +0 -57
- data/spec/grape/validations/validators/values_spec.rb +0 -733
- data/spec/grape/validations/validators/zh-CN.yml +0 -10
- data/spec/grape/validations_spec.rb +0 -2030
- data/spec/integration/eager_load/eager_load_spec.rb +0 -15
- data/spec/integration/multi_json/json_spec.rb +0 -7
- data/spec/integration/multi_xml/xml_spec.rb +0 -7
- data/spec/shared/deprecated_class_examples.rb +0 -16
- data/spec/shared/versioning_examples.rb +0 -215
- data/spec/spec_helper.rb +0 -52
- data/spec/support/basic_auth_encode_helpers.rb +0 -11
- data/spec/support/chunks.rb +0 -14
- data/spec/support/content_type_helpers.rb +0 -15
- data/spec/support/endpoint_faker.rb +0 -25
- data/spec/support/file_streamer.rb +0 -13
- data/spec/support/integer_helpers.rb +0 -13
- data/spec/support/versioned_helpers.rb +0 -55
data/spec/grape/endpoint_spec.rb
DELETED
@@ -1,1085 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
describe Grape::Endpoint do
|
4
|
-
subject { Class.new(Grape::API) }
|
5
|
-
|
6
|
-
def app
|
7
|
-
subject
|
8
|
-
end
|
9
|
-
|
10
|
-
describe '.before_each' do
|
11
|
-
after { described_class.before_each.clear }
|
12
|
-
|
13
|
-
it 'is settable via block' do
|
14
|
-
block = ->(_endpoint) { 'noop' }
|
15
|
-
described_class.before_each(&block)
|
16
|
-
expect(described_class.before_each.first).to eq(block)
|
17
|
-
end
|
18
|
-
|
19
|
-
it 'is settable via reference' do
|
20
|
-
block = ->(_endpoint) { 'noop' }
|
21
|
-
described_class.before_each block
|
22
|
-
expect(described_class.before_each.first).to eq(block)
|
23
|
-
end
|
24
|
-
|
25
|
-
it 'is able to override a helper' do
|
26
|
-
subject.get('/') { current_user }
|
27
|
-
expect { get '/' }.to raise_error(NameError)
|
28
|
-
|
29
|
-
described_class.before_each do |endpoint|
|
30
|
-
allow(endpoint).to receive(:current_user).and_return('Bob')
|
31
|
-
end
|
32
|
-
|
33
|
-
get '/'
|
34
|
-
expect(last_response.body).to eq('Bob')
|
35
|
-
|
36
|
-
described_class.before_each(nil)
|
37
|
-
expect { get '/' }.to raise_error(NameError)
|
38
|
-
end
|
39
|
-
|
40
|
-
it 'is able to stack helper' do
|
41
|
-
subject.get('/') do
|
42
|
-
authenticate_user!
|
43
|
-
current_user
|
44
|
-
end
|
45
|
-
expect { get '/' }.to raise_error(NameError)
|
46
|
-
|
47
|
-
described_class.before_each do |endpoint|
|
48
|
-
allow(endpoint).to receive(:current_user).and_return('Bob')
|
49
|
-
end
|
50
|
-
|
51
|
-
described_class.before_each do |endpoint|
|
52
|
-
allow(endpoint).to receive(:authenticate_user!).and_return(true)
|
53
|
-
end
|
54
|
-
|
55
|
-
get '/'
|
56
|
-
expect(last_response.body).to eq('Bob')
|
57
|
-
|
58
|
-
described_class.before_each(nil)
|
59
|
-
expect { get '/' }.to raise_error(NameError)
|
60
|
-
end
|
61
|
-
end
|
62
|
-
|
63
|
-
describe '#initialize' do
|
64
|
-
it 'takes a settings stack, options, and a block' do
|
65
|
-
p = proc {}
|
66
|
-
expect do
|
67
|
-
described_class.new(Grape::Util::InheritableSetting.new, {
|
68
|
-
path: '/',
|
69
|
-
method: :get
|
70
|
-
}, &p)
|
71
|
-
end.not_to raise_error
|
72
|
-
end
|
73
|
-
end
|
74
|
-
|
75
|
-
it 'sets itself in the env upon call' do
|
76
|
-
subject.get('/') { 'Hello world.' }
|
77
|
-
get '/'
|
78
|
-
expect(last_request.env['api.endpoint']).to be_a(described_class)
|
79
|
-
end
|
80
|
-
|
81
|
-
describe '#status' do
|
82
|
-
it 'is callable from within a block' do
|
83
|
-
subject.get('/home') do
|
84
|
-
status 206
|
85
|
-
'Hello'
|
86
|
-
end
|
87
|
-
|
88
|
-
get '/home'
|
89
|
-
expect(last_response.status).to eq(206)
|
90
|
-
expect(last_response.body).to eq('Hello')
|
91
|
-
end
|
92
|
-
|
93
|
-
it 'is set as default to 200 for get' do
|
94
|
-
memoized_status = nil
|
95
|
-
subject.get('/home') do
|
96
|
-
memoized_status = status
|
97
|
-
'Hello'
|
98
|
-
end
|
99
|
-
|
100
|
-
get '/home'
|
101
|
-
expect(last_response.status).to eq(200)
|
102
|
-
expect(memoized_status).to eq(200)
|
103
|
-
expect(last_response.body).to eq('Hello')
|
104
|
-
end
|
105
|
-
|
106
|
-
it 'is set as default to 201 for post' do
|
107
|
-
memoized_status = nil
|
108
|
-
subject.post('/home') do
|
109
|
-
memoized_status = status
|
110
|
-
'Hello'
|
111
|
-
end
|
112
|
-
|
113
|
-
post '/home'
|
114
|
-
expect(last_response.status).to eq(201)
|
115
|
-
expect(memoized_status).to eq(201)
|
116
|
-
expect(last_response.body).to eq('Hello')
|
117
|
-
end
|
118
|
-
end
|
119
|
-
|
120
|
-
describe '#header' do
|
121
|
-
it 'is callable from within a block' do
|
122
|
-
subject.get('/hey') do
|
123
|
-
header 'X-Awesome', 'true'
|
124
|
-
'Awesome'
|
125
|
-
end
|
126
|
-
|
127
|
-
get '/hey'
|
128
|
-
expect(last_response.headers['X-Awesome']).to eq('true')
|
129
|
-
end
|
130
|
-
end
|
131
|
-
|
132
|
-
describe '#headers' do
|
133
|
-
before do
|
134
|
-
subject.get('/headers') do
|
135
|
-
headers.to_json
|
136
|
-
end
|
137
|
-
end
|
138
|
-
|
139
|
-
it 'includes request headers' do
|
140
|
-
get '/headers'
|
141
|
-
expect(JSON.parse(last_response.body)).to include(
|
142
|
-
'Host' => 'example.org',
|
143
|
-
'Cookie' => ''
|
144
|
-
)
|
145
|
-
end
|
146
|
-
|
147
|
-
it 'includes additional request headers' do
|
148
|
-
get '/headers', nil, 'HTTP_X_GRAPE_CLIENT' => '1'
|
149
|
-
expect(JSON.parse(last_response.body)['X-Grape-Client']).to eq('1')
|
150
|
-
end
|
151
|
-
|
152
|
-
it 'includes headers passed as symbols' do
|
153
|
-
env = Rack::MockRequest.env_for('/headers')
|
154
|
-
env[:HTTP_SYMBOL_HEADER] = 'Goliath passes symbols'
|
155
|
-
body = read_chunks(subject.call(env)[2]).join
|
156
|
-
expect(JSON.parse(body)['Symbol-Header']).to eq('Goliath passes symbols')
|
157
|
-
end
|
158
|
-
end
|
159
|
-
|
160
|
-
describe '#cookies' do
|
161
|
-
it 'is callable from within a block' do
|
162
|
-
subject.get('/get/cookies') do
|
163
|
-
cookies['my-awesome-cookie1'] = 'is cool'
|
164
|
-
cookies['my-awesome-cookie2'] = {
|
165
|
-
value: 'is cool too',
|
166
|
-
domain: 'my.example.com',
|
167
|
-
path: '/',
|
168
|
-
secure: true
|
169
|
-
}
|
170
|
-
cookies[:cookie3] = 'symbol'
|
171
|
-
cookies['cookie4'] = 'secret code here'
|
172
|
-
end
|
173
|
-
|
174
|
-
get('/get/cookies')
|
175
|
-
|
176
|
-
expect(Array(last_response.headers['Set-Cookie']).flat_map { |h| h.split("\n") }.sort).to eql [
|
177
|
-
'cookie3=symbol',
|
178
|
-
'cookie4=secret+code+here',
|
179
|
-
'my-awesome-cookie1=is+cool',
|
180
|
-
'my-awesome-cookie2=is+cool+too; domain=my.example.com; path=/; secure'
|
181
|
-
]
|
182
|
-
end
|
183
|
-
|
184
|
-
it 'sets browser cookies and does not set response cookies' do
|
185
|
-
subject.get('/username') do
|
186
|
-
cookies[:username]
|
187
|
-
end
|
188
|
-
get('/username', {}, 'HTTP_COOKIE' => 'username=mrplum; sandbox=true')
|
189
|
-
|
190
|
-
expect(last_response.body).to eq('mrplum')
|
191
|
-
expect(last_response.headers['Set-Cookie']).to be_nil
|
192
|
-
end
|
193
|
-
|
194
|
-
it 'sets and update browser cookies' do
|
195
|
-
subject.get('/username') do
|
196
|
-
cookies[:sandbox] = true if cookies[:sandbox] == 'false'
|
197
|
-
cookies[:username] += '_test'
|
198
|
-
end
|
199
|
-
get('/username', {}, 'HTTP_COOKIE' => 'username=user; sandbox=false')
|
200
|
-
expect(last_response.body).to eq('user_test')
|
201
|
-
cookies = Array(last_response.headers['Set-Cookie']).flat_map { |h| h.split("\n") }
|
202
|
-
expect(cookies.first).to match(/username=user_test/)
|
203
|
-
expect(cookies.second).to match(/sandbox=true/)
|
204
|
-
end
|
205
|
-
|
206
|
-
it 'deletes cookie' do
|
207
|
-
subject.get('/test') do
|
208
|
-
sum = 0
|
209
|
-
cookies.each do |name, val|
|
210
|
-
sum += val.to_i
|
211
|
-
cookies.delete name
|
212
|
-
end
|
213
|
-
sum
|
214
|
-
end
|
215
|
-
get '/test', {}, 'HTTP_COOKIE' => 'delete_this_cookie=1; and_this=2'
|
216
|
-
expect(last_response.body).to eq('3')
|
217
|
-
cookies = Array(last_response.headers['Set-Cookie']).flat_map { |h| h.split("\n") }.to_h do |set_cookie|
|
218
|
-
cookie = CookieJar::Cookie.from_set_cookie 'http://localhost/test', set_cookie
|
219
|
-
[cookie.name, cookie]
|
220
|
-
end
|
221
|
-
expect(cookies.size).to eq(2)
|
222
|
-
%w[and_this delete_this_cookie].each do |cookie_name|
|
223
|
-
cookie = cookies[cookie_name]
|
224
|
-
expect(cookie).not_to be_nil
|
225
|
-
expect(cookie.value).to eq('deleted')
|
226
|
-
expect(cookie.expired?).to be true
|
227
|
-
end
|
228
|
-
end
|
229
|
-
|
230
|
-
it 'deletes cookies with path' do
|
231
|
-
subject.get('/test') do
|
232
|
-
sum = 0
|
233
|
-
cookies.each do |name, val|
|
234
|
-
sum += val.to_i
|
235
|
-
cookies.delete name, path: '/test'
|
236
|
-
end
|
237
|
-
sum
|
238
|
-
end
|
239
|
-
get('/test', {}, 'HTTP_COOKIE' => 'delete_this_cookie=1; and_this=2')
|
240
|
-
expect(last_response.body).to eq('3')
|
241
|
-
cookies = Array(last_response.headers['Set-Cookie']).flat_map { |h| h.split("\n") }.to_h do |set_cookie|
|
242
|
-
cookie = CookieJar::Cookie.from_set_cookie 'http://localhost/test', set_cookie
|
243
|
-
[cookie.name, cookie]
|
244
|
-
end
|
245
|
-
expect(cookies.size).to eq(2)
|
246
|
-
%w[and_this delete_this_cookie].each do |cookie_name|
|
247
|
-
cookie = cookies[cookie_name]
|
248
|
-
expect(cookie).not_to be_nil
|
249
|
-
expect(cookie.value).to eq('deleted')
|
250
|
-
expect(cookie.path).to eq('/test')
|
251
|
-
expect(cookie.expired?).to be true
|
252
|
-
end
|
253
|
-
end
|
254
|
-
end
|
255
|
-
|
256
|
-
describe '#params' do
|
257
|
-
context 'default class' do
|
258
|
-
it 'is a ActiveSupport::HashWithIndifferentAccess' do
|
259
|
-
subject.get '/foo' do
|
260
|
-
params.class
|
261
|
-
end
|
262
|
-
|
263
|
-
get '/foo'
|
264
|
-
expect(last_response.status).to eq(200)
|
265
|
-
expect(last_response.body).to eq('ActiveSupport::HashWithIndifferentAccess')
|
266
|
-
end
|
267
|
-
end
|
268
|
-
|
269
|
-
context 'sets a value to params' do
|
270
|
-
it 'params' do
|
271
|
-
subject.params do
|
272
|
-
requires :a, type: String
|
273
|
-
end
|
274
|
-
subject.get '/foo' do
|
275
|
-
params[:a] = 'bar'
|
276
|
-
end
|
277
|
-
|
278
|
-
get '/foo', a: 'foo'
|
279
|
-
expect(last_response.status).to eq(200)
|
280
|
-
expect(last_response.body).to eq('bar')
|
281
|
-
end
|
282
|
-
end
|
283
|
-
end
|
284
|
-
|
285
|
-
describe '#params' do
|
286
|
-
it 'is available to the caller' do
|
287
|
-
subject.get('/hey') do
|
288
|
-
params[:howdy]
|
289
|
-
end
|
290
|
-
|
291
|
-
get '/hey?howdy=hey'
|
292
|
-
expect(last_response.body).to eq('hey')
|
293
|
-
end
|
294
|
-
|
295
|
-
it 'parses from path segments' do
|
296
|
-
subject.get('/hey/:id') do
|
297
|
-
params[:id]
|
298
|
-
end
|
299
|
-
|
300
|
-
get '/hey/12'
|
301
|
-
expect(last_response.body).to eq('12')
|
302
|
-
end
|
303
|
-
|
304
|
-
it 'deeply converts nested params' do
|
305
|
-
subject.get '/location' do
|
306
|
-
params[:location][:city]
|
307
|
-
end
|
308
|
-
get '/location?location[city]=Dallas'
|
309
|
-
expect(last_response.body).to eq('Dallas')
|
310
|
-
end
|
311
|
-
|
312
|
-
context 'with special requirements' do
|
313
|
-
it 'parses email param with provided requirements for params' do
|
314
|
-
subject.get('/:person_email', requirements: { person_email: /.*/ }) do
|
315
|
-
params[:person_email]
|
316
|
-
end
|
317
|
-
|
318
|
-
get '/someone@example.com'
|
319
|
-
expect(last_response.body).to eq('someone@example.com')
|
320
|
-
|
321
|
-
get 'someone@example.com.pl'
|
322
|
-
expect(last_response.body).to eq('someone@example.com.pl')
|
323
|
-
end
|
324
|
-
|
325
|
-
it 'parses many params with provided regexps' do
|
326
|
-
subject.get('/:person_email/test/:number', requirements: { person_email: /someone@(.*).com/, number: /[0-9]/ }) do
|
327
|
-
params[:person_email] << params[:number]
|
328
|
-
end
|
329
|
-
|
330
|
-
get '/someone@example.com/test/1'
|
331
|
-
expect(last_response.body).to eq('someone@example.com1')
|
332
|
-
|
333
|
-
get '/someone@testing.wrong/test/1'
|
334
|
-
expect(last_response.status).to eq(404)
|
335
|
-
|
336
|
-
get 'someone@test.com/test/wrong_number'
|
337
|
-
expect(last_response.status).to eq(404)
|
338
|
-
|
339
|
-
get 'someone@test.com/wrong_middle/1'
|
340
|
-
expect(last_response.status).to eq(404)
|
341
|
-
end
|
342
|
-
|
343
|
-
context 'namespace requirements' do
|
344
|
-
before do
|
345
|
-
subject.namespace :outer, requirements: { person_email: /abc@(.*).com/ } do
|
346
|
-
get('/:person_email') do
|
347
|
-
params[:person_email]
|
348
|
-
end
|
349
|
-
|
350
|
-
namespace :inner, requirements: { number: /[0-9]/, person_email: /someone@(.*).com/ } do
|
351
|
-
get '/:person_email/test/:number' do
|
352
|
-
params[:person_email] << params[:number]
|
353
|
-
end
|
354
|
-
end
|
355
|
-
end
|
356
|
-
end
|
357
|
-
|
358
|
-
it 'parse email param with provided requirements for params' do
|
359
|
-
get '/outer/abc@example.com'
|
360
|
-
expect(last_response.body).to eq('abc@example.com')
|
361
|
-
end
|
362
|
-
|
363
|
-
it "overrides outer namespace's requirements" do
|
364
|
-
get '/outer/inner/someone@testing.wrong/test/1'
|
365
|
-
expect(last_response.status).to eq(404)
|
366
|
-
|
367
|
-
get '/outer/inner/someone@testing.com/test/1'
|
368
|
-
expect(last_response.status).to eq(200)
|
369
|
-
expect(last_response.body).to eq('someone@testing.com1')
|
370
|
-
end
|
371
|
-
end
|
372
|
-
end
|
373
|
-
|
374
|
-
context 'from body parameters' do
|
375
|
-
before do
|
376
|
-
subject.post '/request_body' do
|
377
|
-
params[:user]
|
378
|
-
end
|
379
|
-
subject.put '/request_body' do
|
380
|
-
params[:user]
|
381
|
-
end
|
382
|
-
end
|
383
|
-
|
384
|
-
it 'converts JSON bodies to params' do
|
385
|
-
post '/request_body', ::Grape::Json.dump(user: 'Bobby T.'), 'CONTENT_TYPE' => 'application/json'
|
386
|
-
expect(last_response.body).to eq('Bobby T.')
|
387
|
-
end
|
388
|
-
|
389
|
-
it 'does not convert empty JSON bodies to params' do
|
390
|
-
put '/request_body', '', 'CONTENT_TYPE' => 'application/json'
|
391
|
-
expect(last_response.body).to eq('')
|
392
|
-
end
|
393
|
-
|
394
|
-
if Object.const_defined? :MultiXml
|
395
|
-
it 'converts XML bodies to params' do
|
396
|
-
post '/request_body', '<user>Bobby T.</user>', 'CONTENT_TYPE' => 'application/xml'
|
397
|
-
expect(last_response.body).to eq('Bobby T.')
|
398
|
-
end
|
399
|
-
|
400
|
-
it 'converts XML bodies to params' do
|
401
|
-
put '/request_body', '<user>Bobby T.</user>', 'CONTENT_TYPE' => 'application/xml'
|
402
|
-
expect(last_response.body).to eq('Bobby T.')
|
403
|
-
end
|
404
|
-
else
|
405
|
-
it 'converts XML bodies to params' do
|
406
|
-
post '/request_body', '<user>Bobby T.</user>', 'CONTENT_TYPE' => 'application/xml'
|
407
|
-
expect(last_response.body).to eq('{"__content__"=>"Bobby T."}')
|
408
|
-
end
|
409
|
-
|
410
|
-
it 'converts XML bodies to params' do
|
411
|
-
put '/request_body', '<user>Bobby T.</user>', 'CONTENT_TYPE' => 'application/xml'
|
412
|
-
expect(last_response.body).to eq('{"__content__"=>"Bobby T."}')
|
413
|
-
end
|
414
|
-
end
|
415
|
-
|
416
|
-
it 'does not include parameters not defined by the body' do
|
417
|
-
subject.post '/omitted_params' do
|
418
|
-
error! 400, 'expected nil' if params[:version]
|
419
|
-
params[:user]
|
420
|
-
end
|
421
|
-
post '/omitted_params', ::Grape::Json.dump(user: 'Bob'), 'CONTENT_TYPE' => 'application/json'
|
422
|
-
expect(last_response.status).to eq(201)
|
423
|
-
expect(last_response.body).to eq('Bob')
|
424
|
-
end
|
425
|
-
|
426
|
-
# Rack swallowed this error until v2.2.0
|
427
|
-
it 'returns a 400 if given an invalid multipart body', if: Gem::Version.new(Rack.release) >= Gem::Version.new('2.2.0') do
|
428
|
-
subject.params do
|
429
|
-
requires :file, type: Rack::Multipart::UploadedFile
|
430
|
-
end
|
431
|
-
subject.post '/upload' do
|
432
|
-
params[:file][:filename]
|
433
|
-
end
|
434
|
-
post '/upload', { file: '' }, 'CONTENT_TYPE' => 'multipart/form-data; boundary=foobar'
|
435
|
-
expect(last_response.status).to eq(400)
|
436
|
-
expect(last_response.body).to eq('file is invalid')
|
437
|
-
end
|
438
|
-
end
|
439
|
-
|
440
|
-
context 'when the limit on multipart files is exceeded' do
|
441
|
-
around do |example|
|
442
|
-
limit = Rack::Utils.multipart_part_limit
|
443
|
-
Rack::Utils.multipart_part_limit = 1
|
444
|
-
example.run
|
445
|
-
Rack::Utils.multipart_part_limit = limit
|
446
|
-
end
|
447
|
-
|
448
|
-
it 'returns a 413 if given too many multipart files' do
|
449
|
-
subject.params do
|
450
|
-
requires :file, type: Rack::Multipart::UploadedFile
|
451
|
-
end
|
452
|
-
subject.post '/upload' do
|
453
|
-
params[:file][:filename]
|
454
|
-
end
|
455
|
-
post '/upload', { file: Rack::Test::UploadedFile.new(__FILE__, 'text/plain'), extra: Rack::Test::UploadedFile.new(__FILE__, 'text/plain') }
|
456
|
-
expect(last_response.status).to eq(413)
|
457
|
-
expect(last_response.body).to eq("the number of uploaded files exceeded the system's configured limit (1)")
|
458
|
-
end
|
459
|
-
end
|
460
|
-
|
461
|
-
it 'responds with a 415 for an unsupported content-type' do
|
462
|
-
subject.format :json
|
463
|
-
# subject.content_type :json, "application/json"
|
464
|
-
subject.put '/request_body' do
|
465
|
-
params[:user]
|
466
|
-
end
|
467
|
-
put '/request_body', '<user>Bobby T.</user>', 'CONTENT_TYPE' => 'application/xml'
|
468
|
-
expect(last_response.status).to eq(415)
|
469
|
-
expect(last_response.body).to eq('{"error":"The provided content-type \'application/xml\' is not supported."}')
|
470
|
-
end
|
471
|
-
|
472
|
-
it 'does not accept text/plain in JSON format if application/json is specified as content type' do
|
473
|
-
subject.format :json
|
474
|
-
subject.default_format :json
|
475
|
-
subject.put '/request_body' do
|
476
|
-
params[:user]
|
477
|
-
end
|
478
|
-
put '/request_body', ::Grape::Json.dump(user: 'Bob'), 'CONTENT_TYPE' => 'text/plain'
|
479
|
-
|
480
|
-
expect(last_response.status).to eq(415)
|
481
|
-
expect(last_response.body).to eq('{"error":"The provided content-type \'text/plain\' is not supported."}')
|
482
|
-
end
|
483
|
-
|
484
|
-
context 'content type with params' do
|
485
|
-
before do
|
486
|
-
subject.format :json
|
487
|
-
subject.content_type :json, 'application/json; charset=utf-8'
|
488
|
-
|
489
|
-
subject.post do
|
490
|
-
params[:data]
|
491
|
-
end
|
492
|
-
post '/', ::Grape::Json.dump(data: { some: 'payload' }), 'CONTENT_TYPE' => 'application/json'
|
493
|
-
end
|
494
|
-
|
495
|
-
it 'does not response with 406 for same type without params' do
|
496
|
-
expect(last_response.status).not_to be 406
|
497
|
-
end
|
498
|
-
|
499
|
-
it 'responses with given content type in headers' do
|
500
|
-
expect(last_response.headers['Content-Type']).to eq 'application/json; charset=utf-8'
|
501
|
-
end
|
502
|
-
end
|
503
|
-
|
504
|
-
context 'precedence' do
|
505
|
-
before do
|
506
|
-
subject.format :json
|
507
|
-
subject.namespace '/:id' do
|
508
|
-
get do
|
509
|
-
{
|
510
|
-
params: params[:id]
|
511
|
-
}
|
512
|
-
end
|
513
|
-
post do
|
514
|
-
{
|
515
|
-
params: params[:id]
|
516
|
-
}
|
517
|
-
end
|
518
|
-
put do
|
519
|
-
{
|
520
|
-
params: params[:id]
|
521
|
-
}
|
522
|
-
end
|
523
|
-
end
|
524
|
-
end
|
525
|
-
|
526
|
-
it 'route string params have higher precedence than body params' do
|
527
|
-
post '/123', { id: 456 }.to_json
|
528
|
-
expect(JSON.parse(last_response.body)['params']).to eq '123'
|
529
|
-
put '/123', { id: 456 }.to_json
|
530
|
-
expect(JSON.parse(last_response.body)['params']).to eq '123'
|
531
|
-
end
|
532
|
-
|
533
|
-
it 'route string params have higher precedence than URL params' do
|
534
|
-
get '/123?id=456'
|
535
|
-
expect(JSON.parse(last_response.body)['params']).to eq '123'
|
536
|
-
post '/123?id=456'
|
537
|
-
expect(JSON.parse(last_response.body)['params']).to eq '123'
|
538
|
-
end
|
539
|
-
end
|
540
|
-
|
541
|
-
context 'sets a value to params' do
|
542
|
-
it 'params' do
|
543
|
-
subject.params do
|
544
|
-
requires :a, type: String
|
545
|
-
end
|
546
|
-
subject.get '/foo' do
|
547
|
-
params[:a] = 'bar'
|
548
|
-
end
|
549
|
-
|
550
|
-
get '/foo', a: 'foo'
|
551
|
-
expect(last_response.status).to eq(200)
|
552
|
-
expect(last_response.body).to eq('bar')
|
553
|
-
end
|
554
|
-
end
|
555
|
-
end
|
556
|
-
|
557
|
-
describe '#error!' do
|
558
|
-
it 'accepts a message' do
|
559
|
-
subject.get('/hey') do
|
560
|
-
error! 'This is not valid.'
|
561
|
-
'This is valid.'
|
562
|
-
end
|
563
|
-
|
564
|
-
get '/hey'
|
565
|
-
expect(last_response.status).to eq(500)
|
566
|
-
expect(last_response.body).to eq('This is not valid.')
|
567
|
-
end
|
568
|
-
|
569
|
-
it 'accepts a code' do
|
570
|
-
subject.get('/hey') do
|
571
|
-
error! 'Unauthorized.', 401
|
572
|
-
end
|
573
|
-
|
574
|
-
get '/hey'
|
575
|
-
expect(last_response.status).to eq(401)
|
576
|
-
expect(last_response.body).to eq('Unauthorized.')
|
577
|
-
end
|
578
|
-
|
579
|
-
it 'accepts an object and render it in format' do
|
580
|
-
subject.get '/hey' do
|
581
|
-
error!({ 'dude' => 'rad' }, 403)
|
582
|
-
end
|
583
|
-
|
584
|
-
get '/hey.json'
|
585
|
-
expect(last_response.status).to eq(403)
|
586
|
-
expect(last_response.body).to eq('{"dude":"rad"}')
|
587
|
-
end
|
588
|
-
|
589
|
-
it 'accepts a frozen object' do
|
590
|
-
subject.get '/hey' do
|
591
|
-
error!({ 'dude' => 'rad' }.freeze, 403)
|
592
|
-
end
|
593
|
-
|
594
|
-
get '/hey.json'
|
595
|
-
expect(last_response.status).to eq(403)
|
596
|
-
expect(last_response.body).to eq('{"dude":"rad"}')
|
597
|
-
end
|
598
|
-
|
599
|
-
it 'can specifiy headers' do
|
600
|
-
subject.get '/hey' do
|
601
|
-
error!({ 'dude' => 'rad' }, 403, 'X-Custom' => 'value')
|
602
|
-
end
|
603
|
-
|
604
|
-
get '/hey.json'
|
605
|
-
expect(last_response.status).to eq(403)
|
606
|
-
expect(last_response.headers['X-Custom']).to eq('value')
|
607
|
-
end
|
608
|
-
|
609
|
-
it 'merges additional headers with headers set before call' do
|
610
|
-
subject.before do
|
611
|
-
header 'X-Before-Test', 'before-sample'
|
612
|
-
end
|
613
|
-
|
614
|
-
subject.get '/hey' do
|
615
|
-
header 'X-Test', 'test-sample'
|
616
|
-
error!({ 'dude' => 'rad' }, 403, 'X-Error' => 'error')
|
617
|
-
end
|
618
|
-
|
619
|
-
get '/hey.json'
|
620
|
-
expect(last_response.headers['X-Before-Test']).to eq('before-sample')
|
621
|
-
expect(last_response.headers['X-Test']).to eq('test-sample')
|
622
|
-
expect(last_response.headers['X-Error']).to eq('error')
|
623
|
-
end
|
624
|
-
|
625
|
-
it 'does not merges additional headers with headers set after call' do
|
626
|
-
subject.after do
|
627
|
-
header 'X-After-Test', 'after-sample'
|
628
|
-
end
|
629
|
-
|
630
|
-
subject.get '/hey' do
|
631
|
-
error!({ 'dude' => 'rad' }, 403, 'X-Error' => 'error')
|
632
|
-
end
|
633
|
-
|
634
|
-
get '/hey.json'
|
635
|
-
expect(last_response.headers['X-Error']).to eq('error')
|
636
|
-
expect(last_response.headers['X-After-Test']).to be_nil
|
637
|
-
end
|
638
|
-
|
639
|
-
it 'sets the status code for the endpoint' do
|
640
|
-
memoized_endpoint = nil
|
641
|
-
|
642
|
-
subject.get '/hey' do
|
643
|
-
memoized_endpoint = self
|
644
|
-
error!({ 'dude' => 'rad' }, 403, 'X-Custom' => 'value')
|
645
|
-
end
|
646
|
-
|
647
|
-
get '/hey.json'
|
648
|
-
|
649
|
-
expect(memoized_endpoint.status).to eq(403)
|
650
|
-
end
|
651
|
-
end
|
652
|
-
|
653
|
-
describe '#redirect' do
|
654
|
-
it 'redirects to a url with status 302' do
|
655
|
-
subject.get('/hey') do
|
656
|
-
redirect '/ha'
|
657
|
-
end
|
658
|
-
get '/hey'
|
659
|
-
expect(last_response.status).to eq 302
|
660
|
-
expect(last_response.headers['Location']).to eq '/ha'
|
661
|
-
expect(last_response.body).to eq 'This resource has been moved temporarily to /ha.'
|
662
|
-
end
|
663
|
-
|
664
|
-
it 'has status code 303 if it is not get request and it is http 1.1' do
|
665
|
-
subject.post('/hey') do
|
666
|
-
redirect '/ha'
|
667
|
-
end
|
668
|
-
post '/hey', {}, 'HTTP_VERSION' => 'HTTP/1.1'
|
669
|
-
expect(last_response.status).to eq 303
|
670
|
-
expect(last_response.headers['Location']).to eq '/ha'
|
671
|
-
expect(last_response.body).to eq 'An alternate resource is located at /ha.'
|
672
|
-
end
|
673
|
-
|
674
|
-
it 'support permanent redirect' do
|
675
|
-
subject.get('/hey') do
|
676
|
-
redirect '/ha', permanent: true
|
677
|
-
end
|
678
|
-
get '/hey'
|
679
|
-
expect(last_response.status).to eq 301
|
680
|
-
expect(last_response.headers['Location']).to eq '/ha'
|
681
|
-
expect(last_response.body).to eq 'This resource has been moved permanently to /ha.'
|
682
|
-
end
|
683
|
-
|
684
|
-
it 'allows for an optional redirect body override' do
|
685
|
-
subject.get('/hey') do
|
686
|
-
redirect '/ha', body: 'test body'
|
687
|
-
end
|
688
|
-
get '/hey'
|
689
|
-
expect(last_response.body).to eq 'test body'
|
690
|
-
end
|
691
|
-
end
|
692
|
-
|
693
|
-
it 'does not persist params between calls' do
|
694
|
-
subject.post('/new') do
|
695
|
-
params[:text]
|
696
|
-
end
|
697
|
-
|
698
|
-
post '/new', text: 'abc'
|
699
|
-
expect(last_response.body).to eq('abc')
|
700
|
-
|
701
|
-
post '/new', text: 'def'
|
702
|
-
expect(last_response.body).to eq('def')
|
703
|
-
end
|
704
|
-
|
705
|
-
it 'resets all instance variables (except block) between calls' do
|
706
|
-
subject.helpers do
|
707
|
-
def memoized
|
708
|
-
@memoized ||= params[:howdy]
|
709
|
-
end
|
710
|
-
end
|
711
|
-
|
712
|
-
subject.get('/hello') do
|
713
|
-
memoized
|
714
|
-
end
|
715
|
-
|
716
|
-
get '/hello?howdy=hey'
|
717
|
-
expect(last_response.body).to eq('hey')
|
718
|
-
get '/hello?howdy=yo'
|
719
|
-
expect(last_response.body).to eq('yo')
|
720
|
-
end
|
721
|
-
|
722
|
-
it 'allows explicit return calls' do
|
723
|
-
subject.get('/home') do
|
724
|
-
return 'Hello'
|
725
|
-
end
|
726
|
-
|
727
|
-
get '/home'
|
728
|
-
expect(last_response.status).to eq(200)
|
729
|
-
expect(last_response.body).to eq('Hello')
|
730
|
-
end
|
731
|
-
|
732
|
-
describe '.generate_api_method' do
|
733
|
-
it 'raises NameError if the method name is already in use' do
|
734
|
-
expect do
|
735
|
-
described_class.generate_api_method('version', &proc {})
|
736
|
-
end.to raise_error(NameError)
|
737
|
-
end
|
738
|
-
|
739
|
-
it 'raises ArgumentError if a block is not given' do
|
740
|
-
expect do
|
741
|
-
described_class.generate_api_method('GET without a block method')
|
742
|
-
end.to raise_error(ArgumentError)
|
743
|
-
end
|
744
|
-
|
745
|
-
it 'returns a Proc' do
|
746
|
-
expect(described_class.generate_api_method('GET test for a proc', &proc {})).to be_a Proc
|
747
|
-
end
|
748
|
-
end
|
749
|
-
|
750
|
-
context 'filters' do
|
751
|
-
describe 'before filters' do
|
752
|
-
it 'runs the before filter if set' do
|
753
|
-
subject.before { env['before_test'] = 'OK' }
|
754
|
-
subject.get('/before_test') { env['before_test'] }
|
755
|
-
|
756
|
-
get '/before_test'
|
757
|
-
expect(last_response.body).to eq('OK')
|
758
|
-
end
|
759
|
-
end
|
760
|
-
|
761
|
-
describe 'after filters' do
|
762
|
-
it 'overrides the response body if it sets it' do
|
763
|
-
subject.after { body 'after' }
|
764
|
-
subject.get('/after_test') { 'during' }
|
765
|
-
get '/after_test'
|
766
|
-
expect(last_response.body).to eq('after')
|
767
|
-
end
|
768
|
-
|
769
|
-
it 'does not override the response body with its return' do
|
770
|
-
subject.after { 'after' }
|
771
|
-
subject.get('/after_test') { 'body' }
|
772
|
-
get '/after_test'
|
773
|
-
expect(last_response.body).to eq('body')
|
774
|
-
end
|
775
|
-
end
|
776
|
-
|
777
|
-
it 'allows adding to response with present' do
|
778
|
-
subject.format :json
|
779
|
-
subject.before { present :before, 'before' }
|
780
|
-
subject.before_validation { present :before_validation, 'before_validation' }
|
781
|
-
subject.after_validation { present :after_validation, 'after_validation' }
|
782
|
-
subject.after { present :after, 'after' }
|
783
|
-
subject.get :all_filters do
|
784
|
-
present :endpoint, 'endpoint'
|
785
|
-
end
|
786
|
-
|
787
|
-
get '/all_filters'
|
788
|
-
json = JSON.parse(last_response.body)
|
789
|
-
expect(json.keys).to match_array %w[before before_validation after_validation endpoint after]
|
790
|
-
end
|
791
|
-
|
792
|
-
context 'when terminating the response with error!' do
|
793
|
-
it 'breaks normal call chain' do
|
794
|
-
called = []
|
795
|
-
subject.before { called << 'before' }
|
796
|
-
subject.before_validation { called << 'before_validation' }
|
797
|
-
subject.after_validation { error! :oops, 500 }
|
798
|
-
subject.after { called << 'after' }
|
799
|
-
subject.get :error_filters do
|
800
|
-
called << 'endpoint'
|
801
|
-
''
|
802
|
-
end
|
803
|
-
|
804
|
-
get '/error_filters'
|
805
|
-
expect(last_response.status).to be 500
|
806
|
-
expect(called).to match_array %w[before before_validation]
|
807
|
-
end
|
808
|
-
|
809
|
-
it 'allows prior and parent filters of same type to run' do
|
810
|
-
called = []
|
811
|
-
subject.before { called << 'parent' }
|
812
|
-
subject.namespace :parent do
|
813
|
-
before { called << 'prior' }
|
814
|
-
|
815
|
-
before { error! :oops, 500 }
|
816
|
-
|
817
|
-
before { called << 'subsequent' }
|
818
|
-
|
819
|
-
get :hello do
|
820
|
-
called << :endpoint
|
821
|
-
'Hello!'
|
822
|
-
end
|
823
|
-
end
|
824
|
-
|
825
|
-
get '/parent/hello'
|
826
|
-
expect(last_response.status).to be 500
|
827
|
-
expect(called).to match_array %w[parent prior]
|
828
|
-
end
|
829
|
-
end
|
830
|
-
end
|
831
|
-
|
832
|
-
context 'anchoring' do
|
833
|
-
describe 'delete 204' do
|
834
|
-
it 'allows for the anchoring option with a delete method' do
|
835
|
-
subject.send(:delete, '/example', anchor: true) {}
|
836
|
-
send(:delete, '/example/and/some/more')
|
837
|
-
expect(last_response.status).to be 404
|
838
|
-
end
|
839
|
-
|
840
|
-
it 'anchors paths by default for the delete method' do
|
841
|
-
subject.send(:delete, '/example') {}
|
842
|
-
send(:delete, '/example/and/some/more')
|
843
|
-
expect(last_response.status).to be 404
|
844
|
-
end
|
845
|
-
|
846
|
-
it 'responds to /example/and/some/more for the non-anchored delete method' do
|
847
|
-
subject.send(:delete, '/example', anchor: false) {}
|
848
|
-
send(:delete, '/example/and/some/more')
|
849
|
-
expect(last_response.status).to be 204
|
850
|
-
expect(last_response.body).to be_empty
|
851
|
-
end
|
852
|
-
end
|
853
|
-
|
854
|
-
describe 'delete 200, with response body' do
|
855
|
-
it 'responds to /example/and/some/more for the non-anchored delete method' do
|
856
|
-
subject.send(:delete, '/example', anchor: false) do
|
857
|
-
status 200
|
858
|
-
body 'deleted'
|
859
|
-
end
|
860
|
-
send(:delete, '/example/and/some/more')
|
861
|
-
expect(last_response.status).to be 200
|
862
|
-
expect(last_response.body).not_to be_empty
|
863
|
-
end
|
864
|
-
end
|
865
|
-
|
866
|
-
describe 'delete 200, with a return value (no explicit body)' do
|
867
|
-
it 'responds to /example delete method' do
|
868
|
-
subject.delete(:example) { 'deleted' }
|
869
|
-
delete '/example'
|
870
|
-
expect(last_response.status).to be 200
|
871
|
-
expect(last_response.body).not_to be_empty
|
872
|
-
end
|
873
|
-
end
|
874
|
-
|
875
|
-
describe 'delete 204, with nil has return value (no explicit body)' do
|
876
|
-
it 'responds to /example delete method' do
|
877
|
-
subject.delete(:example) { nil }
|
878
|
-
delete '/example'
|
879
|
-
expect(last_response.status).to be 204
|
880
|
-
expect(last_response.body).to be_empty
|
881
|
-
end
|
882
|
-
end
|
883
|
-
|
884
|
-
describe 'delete 204, with empty array has return value (no explicit body)' do
|
885
|
-
it 'responds to /example delete method' do
|
886
|
-
subject.delete(:example) { '' }
|
887
|
-
delete '/example'
|
888
|
-
expect(last_response.status).to be 204
|
889
|
-
expect(last_response.body).to be_empty
|
890
|
-
end
|
891
|
-
end
|
892
|
-
|
893
|
-
describe 'all other' do
|
894
|
-
%w[post get head put options patch].each do |verb|
|
895
|
-
it "allows for the anchoring option with a #{verb.upcase} method" do
|
896
|
-
subject.send(verb, '/example', anchor: true) do
|
897
|
-
verb
|
898
|
-
end
|
899
|
-
send(verb, '/example/and/some/more')
|
900
|
-
expect(last_response.status).to be 404
|
901
|
-
end
|
902
|
-
|
903
|
-
it "anchors paths by default for the #{verb.upcase} method" do
|
904
|
-
subject.send(verb, '/example') do
|
905
|
-
verb
|
906
|
-
end
|
907
|
-
send(verb, '/example/and/some/more')
|
908
|
-
expect(last_response.status).to be 404
|
909
|
-
end
|
910
|
-
|
911
|
-
it "responds to /example/and/some/more for the non-anchored #{verb.upcase} method" do
|
912
|
-
subject.send(verb, '/example', anchor: false) do
|
913
|
-
verb
|
914
|
-
end
|
915
|
-
send(verb, '/example/and/some/more')
|
916
|
-
expect(last_response.status).to eql verb == 'post' ? 201 : 200
|
917
|
-
expect(last_response.body).to eql verb == 'head' ? '' : verb
|
918
|
-
end
|
919
|
-
end
|
920
|
-
end
|
921
|
-
end
|
922
|
-
|
923
|
-
context 'request' do
|
924
|
-
it 'is set to the url requested' do
|
925
|
-
subject.get('/url') do
|
926
|
-
request.url
|
927
|
-
end
|
928
|
-
get '/url'
|
929
|
-
expect(last_response.body).to eq('http://example.org/url')
|
930
|
-
end
|
931
|
-
|
932
|
-
['v1', :v1].each do |version|
|
933
|
-
it "includes version #{version}" do
|
934
|
-
subject.version version, using: :path
|
935
|
-
subject.get('/url') do
|
936
|
-
request.url
|
937
|
-
end
|
938
|
-
get "/#{version}/url"
|
939
|
-
expect(last_response.body).to eq("http://example.org/#{version}/url")
|
940
|
-
end
|
941
|
-
end
|
942
|
-
it 'includes prefix' do
|
943
|
-
subject.version 'v1', using: :path
|
944
|
-
subject.prefix 'api'
|
945
|
-
subject.get('/url') do
|
946
|
-
request.url
|
947
|
-
end
|
948
|
-
get '/api/v1/url'
|
949
|
-
expect(last_response.body).to eq('http://example.org/api/v1/url')
|
950
|
-
end
|
951
|
-
end
|
952
|
-
|
953
|
-
context 'version headers' do
|
954
|
-
before do
|
955
|
-
# NOTE: a 404 is returned instead of the 406 if cascade: false is not set.
|
956
|
-
subject.version 'v1', using: :header, vendor: 'ohanapi', cascade: false
|
957
|
-
subject.get '/test' do
|
958
|
-
'Hello!'
|
959
|
-
end
|
960
|
-
end
|
961
|
-
|
962
|
-
it 'result in a 406 response if they are invalid' do
|
963
|
-
get '/test', {}, 'HTTP_ACCEPT' => 'application/vnd.ohanapi.v1+json'
|
964
|
-
expect(last_response.status).to eq(406)
|
965
|
-
end
|
966
|
-
|
967
|
-
it 'result in a 406 response if they cannot be parsed by rack-accept' do
|
968
|
-
get '/test', {}, 'HTTP_ACCEPT' => 'application/vnd.ohanapi.v1+json; version=1'
|
969
|
-
expect(last_response.status).to eq(406)
|
970
|
-
end
|
971
|
-
end
|
972
|
-
|
973
|
-
context 'binary' do
|
974
|
-
before do
|
975
|
-
subject.get do
|
976
|
-
file FileStreamer.new(__FILE__)
|
977
|
-
end
|
978
|
-
end
|
979
|
-
|
980
|
-
it 'suports stream objects in response' do
|
981
|
-
get '/'
|
982
|
-
expect(last_response.status).to eq 200
|
983
|
-
expect(last_response.body).to eq File.read(__FILE__)
|
984
|
-
end
|
985
|
-
end
|
986
|
-
|
987
|
-
context 'validation errors' do
|
988
|
-
before do
|
989
|
-
subject.before do
|
990
|
-
header['Access-Control-Allow-Origin'] = '*'
|
991
|
-
end
|
992
|
-
subject.params do
|
993
|
-
requires :id, type: String
|
994
|
-
end
|
995
|
-
subject.get do
|
996
|
-
'should not get here'
|
997
|
-
end
|
998
|
-
end
|
999
|
-
|
1000
|
-
it 'returns the errors, and passes headers' do
|
1001
|
-
get '/'
|
1002
|
-
expect(last_response.status).to eq 400
|
1003
|
-
expect(last_response.body).to eq 'id is missing'
|
1004
|
-
expect(last_response.headers['Access-Control-Allow-Origin']).to eq('*')
|
1005
|
-
end
|
1006
|
-
end
|
1007
|
-
|
1008
|
-
context 'instrumentation' do
|
1009
|
-
before do
|
1010
|
-
subject.before do
|
1011
|
-
# Placeholder
|
1012
|
-
end
|
1013
|
-
subject.get do
|
1014
|
-
'hello'
|
1015
|
-
end
|
1016
|
-
|
1017
|
-
@events = []
|
1018
|
-
@subscriber = ActiveSupport::Notifications.subscribe(/grape/) do |*args|
|
1019
|
-
@events << ActiveSupport::Notifications::Event.new(*args)
|
1020
|
-
end
|
1021
|
-
end
|
1022
|
-
|
1023
|
-
after do
|
1024
|
-
ActiveSupport::Notifications.unsubscribe(@subscriber)
|
1025
|
-
end
|
1026
|
-
|
1027
|
-
it 'notifies AS::N' do
|
1028
|
-
get '/'
|
1029
|
-
|
1030
|
-
# In order that the events finalized (time each block ended)
|
1031
|
-
expect(@events).to contain_exactly(
|
1032
|
-
have_attributes(name: 'endpoint_run_filters.grape', payload: { endpoint: a_kind_of(described_class),
|
1033
|
-
filters: a_collection_containing_exactly(an_instance_of(Proc)),
|
1034
|
-
type: :before }),
|
1035
|
-
have_attributes(name: 'endpoint_run_filters.grape', payload: { endpoint: a_kind_of(described_class),
|
1036
|
-
filters: [],
|
1037
|
-
type: :before_validation }),
|
1038
|
-
have_attributes(name: 'endpoint_run_validators.grape', payload: { endpoint: a_kind_of(described_class),
|
1039
|
-
validators: [],
|
1040
|
-
request: a_kind_of(Grape::Request) }),
|
1041
|
-
have_attributes(name: 'endpoint_run_filters.grape', payload: { endpoint: a_kind_of(described_class),
|
1042
|
-
filters: [],
|
1043
|
-
type: :after_validation }),
|
1044
|
-
have_attributes(name: 'endpoint_render.grape', payload: { endpoint: a_kind_of(described_class) }),
|
1045
|
-
have_attributes(name: 'endpoint_run_filters.grape', payload: { endpoint: a_kind_of(described_class),
|
1046
|
-
filters: [],
|
1047
|
-
type: :after }),
|
1048
|
-
have_attributes(name: 'endpoint_run_filters.grape', payload: { endpoint: a_kind_of(described_class),
|
1049
|
-
filters: [],
|
1050
|
-
type: :finally }),
|
1051
|
-
have_attributes(name: 'endpoint_run.grape', payload: { endpoint: a_kind_of(described_class),
|
1052
|
-
env: an_instance_of(Hash) }),
|
1053
|
-
have_attributes(name: 'format_response.grape', payload: { env: an_instance_of(Hash),
|
1054
|
-
formatter: a_kind_of(Module) })
|
1055
|
-
)
|
1056
|
-
|
1057
|
-
# In order that events were initialized
|
1058
|
-
expect(@events.sort_by(&:time)).to contain_exactly(
|
1059
|
-
have_attributes(name: 'endpoint_run.grape', payload: { endpoint: a_kind_of(described_class),
|
1060
|
-
env: an_instance_of(Hash) }),
|
1061
|
-
have_attributes(name: 'endpoint_run_filters.grape', payload: { endpoint: a_kind_of(described_class),
|
1062
|
-
filters: a_collection_containing_exactly(an_instance_of(Proc)),
|
1063
|
-
type: :before }),
|
1064
|
-
have_attributes(name: 'endpoint_run_filters.grape', payload: { endpoint: a_kind_of(described_class),
|
1065
|
-
filters: [],
|
1066
|
-
type: :before_validation }),
|
1067
|
-
have_attributes(name: 'endpoint_run_validators.grape', payload: { endpoint: a_kind_of(described_class),
|
1068
|
-
validators: [],
|
1069
|
-
request: a_kind_of(Grape::Request) }),
|
1070
|
-
have_attributes(name: 'endpoint_run_filters.grape', payload: { endpoint: a_kind_of(described_class),
|
1071
|
-
filters: [],
|
1072
|
-
type: :after_validation }),
|
1073
|
-
have_attributes(name: 'endpoint_render.grape', payload: { endpoint: a_kind_of(described_class) }),
|
1074
|
-
have_attributes(name: 'endpoint_run_filters.grape', payload: { endpoint: a_kind_of(described_class),
|
1075
|
-
filters: [],
|
1076
|
-
type: :after }),
|
1077
|
-
have_attributes(name: 'endpoint_run_filters.grape', payload: { endpoint: a_kind_of(described_class),
|
1078
|
-
filters: [],
|
1079
|
-
type: :finally }),
|
1080
|
-
have_attributes(name: 'format_response.grape', payload: { env: an_instance_of(Hash),
|
1081
|
-
formatter: a_kind_of(Module) })
|
1082
|
-
)
|
1083
|
-
end
|
1084
|
-
end
|
1085
|
-
end
|