grape 0.2.1.1 → 0.2.2

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of grape might be problematic. Click here for more details.

@@ -108,6 +108,57 @@ describe Grape::Endpoint do
108
108
  end
109
109
  end
110
110
 
111
+ describe '#declared' do
112
+ before do
113
+ subject.params do
114
+ requires :first
115
+ optional :second
116
+ end
117
+
118
+
119
+ end
120
+
121
+ it 'should have as many keys as there are declared params' do
122
+ subject.get "/declared" do
123
+ declared(params).keys.size.should == 2
124
+ ""
125
+ end
126
+
127
+ get '/declared?first=present'
128
+ last_response.status.should == 200
129
+ end
130
+
131
+ it 'should filter out any additional params that are given' do
132
+ subject.get "/declared" do
133
+ declared(params).key?(:other).should == false
134
+ ""
135
+ end
136
+
137
+ get '/declared?first=one&other=two'
138
+ last_response.status.should == 200
139
+ end
140
+
141
+ it 'should stringify if that option is passed' do
142
+ subject.get "/declared" do
143
+ declared(params, :stringify => true)["first"].should == "one"
144
+ ""
145
+ end
146
+
147
+ get '/declared?first=one&other=two'
148
+ last_response.status.should == 200
149
+ end
150
+
151
+ it 'should not include missing attributes if that option is passed' do
152
+ subject.get "/declared" do
153
+ declared(params, :include_missing => false)[:second].should == nil
154
+ ""
155
+ end
156
+
157
+ get '/declared?first=one&other=two'
158
+ last_response.status.should == 200
159
+ end
160
+ end
161
+
111
162
  describe '#params' do
112
163
  it 'should be available to the caller' do
113
164
  subject.get('/hey') do
@@ -186,9 +237,9 @@ describe Grape::Endpoint do
186
237
  last_response.body.should == 'Bobby T.'
187
238
  end
188
239
 
189
- it 'should convert JSON bodies to params' do
190
- put '/request_body', MultiJson.encode(:user => 'Bobby T.'), {'CONTENT_TYPE' => 'application/json'}
191
- last_response.body.should == 'Bobby T.'
240
+ it 'should not convert empty JSON bodies to params' do
241
+ put '/request_body', '', {'CONTENT_TYPE' => 'application/json'}
242
+ last_response.body.should == ''
192
243
  end
193
244
 
194
245
  it 'should convert XML bodies to params' do
@@ -268,7 +319,7 @@ describe Grape::Endpoint do
268
319
  redirect "/ha", :permanent => true
269
320
  end
270
321
  get '/hey'
271
- last_response.status.should eq 304
322
+ last_response.status.should eq 301
272
323
  last_response.headers['Location'].should eq "/ha"
273
324
  last_response.body.should eq ""
274
325
  end
@@ -347,6 +398,20 @@ describe Grape::Endpoint do
347
398
  last_response.body.should == 'Hiya'
348
399
  end
349
400
 
401
+ it 'should automatically use Klass::Entity if that exists' do
402
+ some_model = Class.new
403
+ entity = Class.new(Grape::Entity)
404
+ entity.stub!(:represent).and_return("Auto-detect!")
405
+
406
+ some_model.const_set :Entity, entity
407
+
408
+ subject.get '/example' do
409
+ present some_model.new
410
+ end
411
+ get '/example'
412
+ last_response.body.should == 'Auto-detect!'
413
+ end
414
+
350
415
  it 'should add a root key to the output if one is given' do
351
416
  subject.get '/example' do
352
417
  present({:abc => 'def'}, :root => :root)
@@ -1,10 +1,10 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe Grape::Entity do
4
- let(:fresh_class){ Class.new(Grape::Entity) }
4
+ let(:fresh_class) { Class.new(Grape::Entity) }
5
5
 
6
6
  context 'class methods' do
7
- subject{ fresh_class }
7
+ subject { fresh_class }
8
8
 
9
9
  describe '.expose' do
10
10
  context 'multiple attributes' do
@@ -235,20 +235,24 @@ describe Grape::Entity do
235
235
  end
236
236
 
237
237
  context 'instance methods' do
238
+
238
239
  let(:model){ mock(attributes) }
239
- let(:attributes){ {
240
- :name => 'Bob Bobson',
240
+
241
+ let(:attributes) { {
242
+ :name => 'Bob Bobson',
241
243
  :email => 'bob@example.com',
242
244
  :birthday => Time.gm(2012, 2, 27),
243
245
  :fantasies => ['Unicorns', 'Double Rainbows', 'Nessy'],
244
246
  :friends => [
245
- mock(:name => "Friend 1", :email => 'friend1@example.com', :fantasies => [], :birthday => Time.gm(2012, 2, 27), :friends => []),
247
+ mock(:name => "Friend 1", :email => 'friend1@example.com', :fantasies => [], :birthday => Time.gm(2012, 2, 27), :friends => []),
246
248
  mock(:name => "Friend 2", :email => 'friend2@example.com', :fantasies => [], :birthday => Time.gm(2012, 2, 27), :friends => [])
247
249
  ]
248
250
  } }
251
+
249
252
  subject{ fresh_class.new(model) }
250
253
 
251
254
  describe '#serializable_hash' do
255
+
252
256
  it 'should not throw an exception if a nil options object is passed' do
253
257
  expect{ fresh_class.new(model).serializable_hash(nil) }.not_to raise_error
254
258
  end
@@ -257,6 +261,88 @@ describe Grape::Entity do
257
261
  fresh_class.expose :name
258
262
  expect{ fresh_class.new(nil).serializable_hash }.not_to raise_error
259
263
  end
264
+
265
+ it 'should not throw an exception when an attribute is not found on the object' do
266
+ fresh_class.expose :name, :non_existant_attribute
267
+ expect{ fresh_class.new(model).serializable_hash }.not_to raise_error
268
+ end
269
+
270
+ it "should not expose attributes that don't exist on the object" do
271
+ fresh_class.expose :email, :non_existant_attribute, :name
272
+
273
+ res = fresh_class.new(model).serializable_hash
274
+ res.should have_key :email
275
+ res.should_not have_key :non_existant_attribute
276
+ res.should have_key :name
277
+ end
278
+
279
+ it "should not expose attributes that don't exist on the object, even with criteria" do
280
+ fresh_class.expose :email
281
+ fresh_class.expose :non_existant_attribute, :if => lambda { false }
282
+ fresh_class.expose :non_existant_attribute2, :if => lambda { true }
283
+
284
+ res = fresh_class.new(model).serializable_hash
285
+ res.should have_key :email
286
+ res.should_not have_key :non_existant_attribute
287
+ res.should_not have_key :non_existant_attribute2
288
+ end
289
+
290
+ it "should expose attributes that don't exist on the object only when they are generated by a block" do
291
+ fresh_class.expose :non_existant_attribute do |model, options|
292
+ "well, I do exist after all"
293
+ end
294
+ res = fresh_class.new(model).serializable_hash
295
+ res.should have_key :non_existant_attribute
296
+ end
297
+
298
+ it "should not expose attributes that are generated by a block but have not passed criteria" do
299
+ fresh_class.expose :non_existant_attribute, :proc => lambda {|model, options|
300
+ "I exist, but it is not yet my time to shine"
301
+ }, :if => lambda { |model, options| false }
302
+ res = fresh_class.new(model).serializable_hash
303
+ res.should_not have_key :non_existant_attribute
304
+ end
305
+
306
+ context "#serializable_hash" do
307
+
308
+ module EntitySpec
309
+ class EmbeddedExample
310
+ def serializable_hash(opts = {})
311
+ { :abc => 'def' }
312
+ end
313
+ end
314
+ class EmbeddedExampleWithMany
315
+ def name
316
+ "abc"
317
+ end
318
+ def embedded
319
+ [ EmbeddedExample.new, EmbeddedExample.new ]
320
+ end
321
+ end
322
+ class EmbeddedExampleWithOne
323
+ def name
324
+ "abc"
325
+ end
326
+ def embedded
327
+ EmbeddedExample.new
328
+ end
329
+ end
330
+ end
331
+
332
+ it 'should serialize embedded objects which respond to #serializable_hash' do
333
+ fresh_class.expose :name, :embedded
334
+ presenter = fresh_class.new(EntitySpec::EmbeddedExampleWithOne.new)
335
+ presenter.serializable_hash.should == {:name => "abc", :embedded => {:abc => "def"}}
336
+ end
337
+
338
+ it 'should serialize embedded arrays of objects which respond to #serializable_hash' do
339
+ fresh_class.expose :name, :embedded
340
+ presenter = fresh_class.new(EntitySpec::EmbeddedExampleWithMany.new)
341
+ presenter.serializable_hash.should == {:name => "abc", :embedded => [{:abc => "def"}, {:abc => "def"}]}
342
+ end
343
+
344
+ end
345
+
260
346
  end
261
347
 
262
348
  describe '#value_for' do
@@ -289,19 +375,73 @@ describe Grape::Entity do
289
375
  rep.last.serializable_hash[:name].should == 'Friend 2'
290
376
  end
291
377
 
292
- it 'should disable root key name for child representations' do
293
- class FriendEntity < Grape::Entity
294
- root 'friends', 'friend'
295
- expose :name, :email
378
+ context 'child representations' do
379
+ it 'should disable root key name for child representations' do
380
+
381
+ module EntitySpec
382
+ class FriendEntity < Grape::Entity
383
+ root 'friends', 'friend'
384
+ expose :name, :email
385
+ end
386
+ end
387
+
388
+ fresh_class.class_eval do
389
+ expose :friends, :using => EntitySpec::FriendEntity
390
+ end
391
+
392
+ rep = subject.send(:value_for, :friends)
393
+ rep.should be_kind_of(Array)
394
+ rep.reject{|r| r.is_a?(EntitySpec::FriendEntity)}.should be_empty
395
+ rep.first.serializable_hash[:name].should == 'Friend 1'
396
+ rep.last.serializable_hash[:name].should == 'Friend 2'
296
397
  end
297
- fresh_class.class_eval do
298
- expose :friends, :using => FriendEntity
398
+
399
+ it 'should pass through custom options' do
400
+ module EntitySpec
401
+ class FriendEntity < Grape::Entity
402
+ root 'friends', 'friend'
403
+ expose :name
404
+ expose :email, :if => { :user_type => :admin }
405
+ end
406
+ end
407
+
408
+ fresh_class.class_eval do
409
+ expose :friends, :using => EntitySpec::FriendEntity
410
+ end
411
+
412
+ rep = subject.send(:value_for, :friends)
413
+ rep.should be_kind_of(Array)
414
+ rep.reject{|r| r.is_a?(EntitySpec::FriendEntity)}.should be_empty
415
+ rep.first.serializable_hash[:email].should be_nil
416
+ rep.last.serializable_hash[:email].should be_nil
417
+
418
+ rep = subject.send(:value_for, :friends, { :user_type => :admin })
419
+ rep.should be_kind_of(Array)
420
+ rep.reject{|r| r.is_a?(EntitySpec::FriendEntity)}.should be_empty
421
+ rep.first.serializable_hash[:email].should == 'friend1@example.com'
422
+ rep.last.serializable_hash[:email].should == 'friend2@example.com'
299
423
  end
300
- rep = subject.send(:value_for, :friends)
301
- rep.should be_kind_of(Array)
302
- rep.reject{|r| r.is_a?(FriendEntity)}.should be_empty
303
- rep.first.serializable_hash[:name].should == 'Friend 1'
304
- rep.last.serializable_hash[:name].should == 'Friend 2'
424
+
425
+ it 'should ignore the :collection parameter in the source options' do
426
+ module EntitySpec
427
+ class FriendEntity < Grape::Entity
428
+ root 'friends', 'friend'
429
+ expose :name
430
+ expose :email, :if => { :collection => true }
431
+ end
432
+ end
433
+
434
+ fresh_class.class_eval do
435
+ expose :friends, :using => EntitySpec::FriendEntity
436
+ end
437
+
438
+ rep = subject.send(:value_for, :friends, { :collection => false })
439
+ rep.should be_kind_of(Array)
440
+ rep.reject{|r| r.is_a?(EntitySpec::FriendEntity)}.should be_empty
441
+ rep.first.serializable_hash[:email].should == 'friend1@example.com'
442
+ rep.last.serializable_hash[:email].should == 'friend2@example.com'
443
+ end
444
+
305
445
  end
306
446
 
307
447
  it 'should call through to the proc if there is one' do
@@ -387,5 +527,53 @@ describe Grape::Entity do
387
527
  subject.send(:conditions_met?, exposure_options, :true => true).should be_false
388
528
  end
389
529
  end
530
+
531
+ describe "::DSL" do
532
+ subject{ Class.new }
533
+
534
+ it 'should create an Entity class when called' do
535
+ subject.should_not be_const_defined(:Entity)
536
+ subject.send(:include, Grape::Entity::DSL)
537
+ subject.should be_const_defined(:Entity)
538
+ end
539
+
540
+ context 'pre-mixed' do
541
+ before{ subject.send(:include, Grape::Entity::DSL) }
542
+
543
+ it 'should be able to define entity traits through DSL' do
544
+ subject.entity do
545
+ expose :name
546
+ end
547
+
548
+ subject.entity_class.exposures.should_not be_empty
549
+ end
550
+
551
+ it 'should be able to expose straight from the class' do
552
+ subject.entity :name, :email
553
+ subject.entity_class.exposures.size.should == 2
554
+ end
555
+
556
+ it 'should be able to mix field and advanced exposures' do
557
+ subject.entity :name, :email do
558
+ expose :third
559
+ end
560
+ subject.entity_class.exposures.size.should == 3
561
+ end
562
+
563
+ context 'instance' do
564
+ let(:instance){ subject.new }
565
+
566
+ describe '#entity' do
567
+ it 'should be an instance of the entity class' do
568
+ instance.entity.should be_kind_of(subject.entity_class)
569
+ end
570
+
571
+ it 'should have an object of itself' do
572
+ instance.entity.object.should == instance
573
+ end
574
+ end
575
+ end
576
+ end
577
+ end
390
578
  end
391
579
  end
@@ -34,6 +34,16 @@ describe Grape::Middleware::Error do
34
34
  end
35
35
  end
36
36
  end
37
+
38
+ # raises a custom error
39
+ class CustomError < Grape::Exceptions::Base; end
40
+ class CustomErrorApp
41
+ class << self
42
+ def call(env)
43
+ raise CustomError, :status => 400, :message => 'failed validation'
44
+ end
45
+ end
46
+ end
37
47
 
38
48
  def app
39
49
  @app
@@ -116,6 +126,17 @@ describe Grape::Middleware::Error do
116
126
  get '/'
117
127
  last_response.status.should == 401
118
128
  end
129
+
130
+ it 'should respond to custom Grape exceptions appropriately' do
131
+ @app ||= Rack::Builder.app do
132
+ use Grape::Middleware::Error, :rescue_all => false
133
+ run CustomErrorApp
134
+ end
135
+
136
+ get '/'
137
+ last_response.status.should == 400
138
+ last_response.body.should == 'failed validation'
139
+ end
119
140
 
120
141
  end
121
142
  end
@@ -48,6 +48,18 @@ describe Grape::Middleware::Formatter do
48
48
  subject.call({'PATH_INFO' => '/somewhere', 'HTTP_ACCEPT' => 'application/json'}).last.each{|b| b.should == '{"abc":"def"}'}
49
49
  end
50
50
 
51
+ it 'should serialize objects that respond to #serializable_hash if there is a root element' do
52
+ class SimpleExample
53
+ def serializable_hash
54
+ {:abc => 'def'}
55
+ end
56
+ end
57
+
58
+ @body = {"root" => SimpleExample.new}
59
+
60
+ subject.call({'PATH_INFO' => '/somewhere', 'HTTP_ACCEPT' => 'application/json'}).last.each{|b| b.should == '{"root":{"abc":"def"}}'}
61
+ end
62
+
51
63
  it 'should call #to_xml if the content type is xml' do
52
64
  @body = "string"
53
65
  @body.instance_eval do
@@ -68,6 +80,13 @@ describe Grape::Middleware::Formatter do
68
80
  subject.env['api.format'].should == :json
69
81
  end
70
82
 
83
+ it 'should use the format parameter if one is provided' do
84
+ subject.call({'PATH_INFO' => '/somewhere','QUERY_STRING' => 'format=json'})
85
+ subject.env['api.format'].should == :json
86
+ subject.call({'PATH_INFO' => '/somewhere','QUERY_STRING' => 'format=xml'})
87
+ subject.env['api.format'].should == :xml
88
+ end
89
+
71
90
  it 'should use the default format if none is provided' do
72
91
  subject.call({'PATH_INFO' => '/info'})
73
92
  subject.env['api.format'].should == :txt
@@ -2,147 +2,218 @@ require 'spec_helper'
2
2
 
3
3
  describe Grape::Middleware::Versioner::Header do
4
4
  let(:app) { lambda{|env| [200, env, env]} }
5
- let(:accept) { 'application/vnd.vendor-v1+json' }
6
5
  subject { Grape::Middleware::Versioner::Header.new(app, @options || {}) }
7
6
 
7
+ before do
8
+ @options = {
9
+ :version_options => {
10
+ :using => :header,
11
+ :vendor => 'vendor',
12
+ },
13
+ }
14
+ end
15
+
8
16
  context 'api.type and api.subtype' do
9
- it 'should set any type and any subtype' do
10
- env = subject.call('HTTP_ACCEPT' => '*/*').last
11
- env['api.type'].should eql '*'
12
- env['api.subtype'].should eql '*'
17
+ it 'should set type and subtype to first choice of content type if no preference given' do
18
+ status, _, env = subject.call('HTTP_ACCEPT' => '*/*')
19
+ env['api.type'].should eql 'application'
20
+ env['api.subtype'].should eql 'vnd.vendor+xml'
21
+ status.should == 200
22
+ end
23
+
24
+ it 'should set preferred type' do
25
+ status, _, env = subject.call('HTTP_ACCEPT' => 'application/*')
26
+ env['api.type'].should eql 'application'
27
+ env['api.subtype'].should eql 'vnd.vendor+xml'
28
+ status.should == 200
13
29
  end
14
30
 
15
31
  it 'should set preferred type and subtype' do
16
- env = subject.call('HTTP_ACCEPT' => 'text/html').last
32
+ status, _, env = subject.call('HTTP_ACCEPT' => 'text/plain')
17
33
  env['api.type'].should eql 'text'
18
- env['api.subtype'].should eql 'html'
34
+ env['api.subtype'].should eql 'plain'
35
+ status.should == 200
19
36
  end
20
37
  end
21
38
 
22
39
  context 'api.format' do
23
40
  it 'should be set' do
24
- env = subject.call('HTTP_ACCEPT' => accept).last
41
+ status, _, env = subject.call('HTTP_ACCEPT' => 'application/vnd.vendor+json')
25
42
  env['api.format'].should eql 'json'
43
+ status.should == 200
26
44
  end
27
45
 
28
46
  it 'should be nil if not provided' do
29
- env = subject.call('HTTP_ACCEPT' => 'application/vnd.vendor-v1').last
47
+ status, _, env = subject.call('HTTP_ACCEPT' => 'application/vnd.vendor')
30
48
  env['api.format'].should eql nil
49
+ status.should == 200
50
+ end
51
+
52
+ context 'when version is set' do
53
+ before do
54
+ @options[:versions] = ['v1']
55
+ end
56
+
57
+ it 'should be set' do
58
+ status, _, env = subject.call('HTTP_ACCEPT' => 'application/vnd.vendor-v1+json')
59
+ env['api.format'].should eql 'json'
60
+ status.should == 200
61
+ end
62
+
63
+ it 'should be nil if not provided' do
64
+ status, _, env = subject.call('HTTP_ACCEPT' => 'application/vnd.vendor-v1')
65
+ env['api.format'].should eql nil
66
+ status.should == 200
67
+ end
31
68
  end
32
69
  end
33
70
 
34
- context 'matched version' do
35
- before do
36
- @options = {
37
- :versions => ['v1'],
38
- :version_options => {:using => :header}
39
- }
71
+ context 'api.vendor' do
72
+ it 'should be set' do
73
+ status, _, env = subject.call('HTTP_ACCEPT' => 'application/vnd.vendor')
74
+ env['api.vendor'].should eql 'vendor'
75
+ status.should == 200
40
76
  end
41
77
 
42
- it 'should set api.vendor' do
43
- env = subject.call('HTTP_ACCEPT' => accept).last
78
+ it 'should be set if format provided' do
79
+ status, _, env = subject.call('HTTP_ACCEPT' => 'application/vnd.vendor+json')
44
80
  env['api.vendor'].should eql 'vendor'
81
+ status.should == 200
45
82
  end
46
83
 
47
- it 'should set api.version' do
48
- env = subject.call('HTTP_ACCEPT' => accept).last
49
- env['api.version'].should eql 'v1'
84
+ it 'should fail with 406 Not Acceptable if vendor is invalid' do
85
+ expect {
86
+ env = subject.call('HTTP_ACCEPT' => 'application/vnd.othervendor+json').last
87
+ }.to throw_symbol(
88
+ :error,
89
+ :status => 406,
90
+ :headers => {'X-Cascade' => 'pass'},
91
+ :message => 'API vendor or version not found'
92
+ )
50
93
  end
51
- end
52
94
 
53
- context 'no header' do
54
- it 'should return a 200 when no header is set and no strict setting is done' do
55
- @options = {
56
- :versions => ['v1'],
57
- :version_options => {:using => :header}
58
- }
59
- subject.call('HTTP_ACCEPT' => '').first.should == 200
60
- subject.call({}).first.should == 200
61
- end
62
-
63
- it 'should return a 200 when no header is set but strict header based versioning is disabled' do
64
- @options = {
65
- :versions => ['v1'],
66
- :version_options => {:using => :header, :strict => false}
67
- }
68
- subject.call('HTTP_ACCEPT' => '').first.should == 200
69
- subject.call({}).first.should == 200
70
- end
71
-
72
- context 'when strict header versioning is used' do
73
- it 'should return a 406 when no header' do
74
- @options = {
75
- :versions => ['v1'],
76
- :version_options => {:using => :header, :strict => true}
77
- }
78
- expect {
79
- env = subject.call('HTTP_ACCEPT' => '').last
80
- }.to throw_symbol(
81
- :error,
82
- :status => 406,
83
- :headers => {'X-Cascade' => 'pass'},
84
- :message => "406 API Version Not Found"
85
- )
95
+ context 'when version is set' do
96
+ before do
97
+ @options[:versions] = ['v1']
98
+ end
99
+
100
+ it 'should be set' do
101
+ status, _, env = subject.call('HTTP_ACCEPT' => 'application/vnd.vendor-v1')
102
+ env['api.vendor'].should eql 'vendor'
103
+ status.should == 200
104
+ end
105
+
106
+ it 'should be set if format provided' do
107
+ status, _, env = subject.call('HTTP_ACCEPT' => 'application/vnd.vendor-v1+json')
108
+ env['api.vendor'].should eql 'vendor'
109
+ status.should == 200
86
110
  end
87
111
 
88
- it 'should return a 406 when incorrect header format is used' do
89
- @options = {
90
- :versions => ['v1'],
91
- :version_options => {:using => :header, :strict => true}
92
- }
112
+ it 'should fail with 406 Not Acceptable if vendor is invalid' do
93
113
  expect {
94
- env = subject.call('HTTP_ACCEPT' => '*/*').last
114
+ env = subject.call('HTTP_ACCEPT' => 'application/vnd.othervendor-v1+json').last
95
115
  }.to throw_symbol(
96
116
  :error,
97
117
  :status => 406,
98
118
  :headers => {'X-Cascade' => 'pass'},
99
- :message => "406 API Version Not Found"
119
+ :message => 'API vendor or version not found'
100
120
  )
101
121
  end
102
-
103
- it 'should return a 200 when proper header is set' do
104
- @options = {
105
- :versions => ['v1'],
106
- :version_options => {:using => :header, :strict => true}
107
- }
108
- subject.call('HTTP_ACCEPT' => 'application/vnd.testing-v1+json').first.should == 200
109
- end
110
122
  end
111
-
112
123
  end
113
124
 
114
- context 'vendors' do
125
+ context 'api.version' do
115
126
  before do
116
- @options = {
117
- :version => ['v1'],
118
- :version_options => {:using => :header, :vendor => 'vendor'}
119
- }
127
+ @options[:versions] = ['v1']
120
128
  end
121
129
 
122
- it 'should match with correct vendor' do
123
- status = subject.call('HTTP_ACCEPT' => accept).first
130
+ it 'should be set' do
131
+ status, _, env = subject.call('HTTP_ACCEPT' => 'application/vnd.vendor-v1')
132
+ env['api.version'].should eql 'v1'
124
133
  status.should == 200
125
134
  end
126
135
 
127
- it 'should not match with an incorrect vendor' do
136
+ it 'should be set if format provided' do
137
+ status, _, env = subject.call('HTTP_ACCEPT' => 'application/vnd.vendor-v1+json')
138
+ env['api.version'].should eql 'v1'
139
+ status.should == 200
140
+ end
141
+
142
+ it 'should fail with 406 Not Acceptable if version is invalid' do
128
143
  expect {
129
- env = subject.call('HTTP_ACCEPT' => 'application/vnd.othervendor-v1+json').last
130
- }.to throw_symbol(:error, :status => 406, :headers => {'X-Cascade' => 'pass'}, :message => "406 API Version Not Found")
144
+ env = subject.call('HTTP_ACCEPT' => 'application/vnd.vendor-v2+json').last
145
+ }.to throw_symbol(
146
+ :error,
147
+ :status => 406,
148
+ :headers => {'X-Cascade' => 'pass'},
149
+ :message => 'API vendor or version not found'
150
+ )
131
151
  end
132
152
  end
133
153
 
134
- context 'no matched version' do
154
+ it 'should succeed if :strict is not set' do
155
+ subject.call('HTTP_ACCEPT' => '').first.should == 200
156
+ subject.call({}).first.should == 200
157
+ end
158
+
159
+ it 'should succeed if :strict is set to false' do
160
+ @options[:version_options][:strict] = false
161
+ subject.call('HTTP_ACCEPT' => '').first.should == 200
162
+ subject.call({}).first.should == 200
163
+ end
164
+
165
+ context 'when :strict is set' do
135
166
  before do
136
- @options = {
137
- :versions => ['unknown_version'],
138
- :version_options => {:using => :header}
139
- }
167
+ @options[:versions] = ['v1']
168
+ @options[:version_options][:strict] = true
169
+ end
170
+
171
+ it 'should fail with 406 Not Acceptable if header is not set' do
172
+ expect {
173
+ env = subject.call({}).last
174
+ }.to throw_symbol(
175
+ :error,
176
+ :status => 406,
177
+ :headers => {'X-Cascade' => 'pass'},
178
+ :message => 'Accept header must be set'
179
+ )
180
+ end
181
+
182
+ it 'should fail with 406 Not Acceptable if header is empty' do
183
+ expect {
184
+ env = subject.call('HTTP_ACCEPT' => '').last
185
+ }.to throw_symbol(
186
+ :error,
187
+ :status => 406,
188
+ :headers => {'X-Cascade' => 'pass'},
189
+ :message => 'Accept header must be set'
190
+ )
140
191
  end
141
192
 
142
- it 'should throw 406 error with X-Cascade header set to pass' do
193
+ it 'should fail with 406 Not Acceptable if type is a range' do
143
194
  expect {
144
- env = subject.call('HTTP_ACCEPT' => accept).last
145
- }.to throw_symbol(:error, :status => 406, :headers => {'X-Cascade' => 'pass'}, :message => "406 API Version Not Found")
195
+ env = subject.call('HTTP_ACCEPT' => '*/*').last
196
+ }.to throw_symbol(
197
+ :error,
198
+ :status => 406,
199
+ :headers => {'X-Cascade' => 'pass'},
200
+ :message => 'Accept header must not contain ranges ("*")'
201
+ )
202
+ end
203
+
204
+ it 'should fail with 406 Not Acceptable if subtype is a range' do
205
+ expect {
206
+ env = subject.call('HTTP_ACCEPT' => 'application/*').last
207
+ }.to throw_symbol(
208
+ :error,
209
+ :status => 406,
210
+ :headers => {'X-Cascade' => 'pass'},
211
+ :message => 'Accept header must not contain ranges ("*")'
212
+ )
213
+ end
214
+
215
+ it 'should succeed if proper header is set' do
216
+ subject.call('HTTP_ACCEPT' => 'application/vnd.vendor-v1+json').first.should == 200
146
217
  end
147
218
  end
148
219
  end