stitches 3.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +10 -0
- data/.ruby-gemset +1 -0
- data/.ruby-version +1 -0
- data/Gemfile +3 -0
- data/Gemfile.lock +126 -0
- data/README.md +220 -0
- data/Rakefile +13 -0
- data/lib/stitches.rb +3 -0
- data/lib/stitches/api_generator.rb +97 -0
- data/lib/stitches/api_key.rb +59 -0
- data/lib/stitches/api_version_constraint.rb +32 -0
- data/lib/stitches/configuration.rb +77 -0
- data/lib/stitches/error.rb +9 -0
- data/lib/stitches/errors.rb +101 -0
- data/lib/stitches/generator_files/app/controllers/api.rb +2 -0
- data/lib/stitches/generator_files/app/controllers/api/api_controller.rb +19 -0
- data/lib/stitches/generator_files/app/controllers/api/v1.rb +2 -0
- data/lib/stitches/generator_files/app/controllers/api/v1/pings_controller.rb +20 -0
- data/lib/stitches/generator_files/app/controllers/api/v2.rb +2 -0
- data/lib/stitches/generator_files/app/controllers/api/v2/pings_controller.rb +20 -0
- data/lib/stitches/generator_files/app/models/api_client.rb +2 -0
- data/lib/stitches/generator_files/config/initializers/stitches.rb +14 -0
- data/lib/stitches/generator_files/db/migrate/create_api_clients.rb +11 -0
- data/lib/stitches/generator_files/db/migrate/enable_uuid_ossp_extension.rb +5 -0
- data/lib/stitches/generator_files/lib/tasks/generate_api_key.rake +10 -0
- data/lib/stitches/generator_files/spec/acceptance/ping_v1_spec.rb +46 -0
- data/lib/stitches/generator_files/spec/features/api_spec.rb +96 -0
- data/lib/stitches/railtie.rb +9 -0
- data/lib/stitches/render_timestamps_in_iso8601_in_json.rb +9 -0
- data/lib/stitches/spec.rb +4 -0
- data/lib/stitches/spec/api_clients.rb +5 -0
- data/lib/stitches/spec/be_iso_8601_utc_encoded.rb +10 -0
- data/lib/stitches/spec/have_api_error.rb +50 -0
- data/lib/stitches/spec/test_headers.rb +51 -0
- data/lib/stitches/valid_mime_type.rb +32 -0
- data/lib/stitches/version.rb +3 -0
- data/lib/stitches/whitelisting_middleware.rb +29 -0
- data/lib/stitches_norailtie.rb +17 -0
- data/spec/api_key_spec.rb +200 -0
- data/spec/api_version_constraint_spec.rb +33 -0
- data/spec/configuration_spec.rb +105 -0
- data/spec/errors_spec.rb +99 -0
- data/spec/spec/have_api_error_spec.rb +78 -0
- data/spec/spec_helper.rb +10 -0
- data/spec/valid_mime_type_spec.rb +166 -0
- data/stitches.gemspec +24 -0
- metadata +168 -0
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'spec_helper.rb'
|
2
|
+
|
3
|
+
describe Stitches::ApiVersionConstraint do
|
4
|
+
let(:version) { 2 }
|
5
|
+
let(:request) { double("request", headers: headers) }
|
6
|
+
|
7
|
+
subject(:constraint) { described_class.new(version) }
|
8
|
+
|
9
|
+
context "no accept header" do
|
10
|
+
let(:headers) { {} }
|
11
|
+
it "doesn't match" do
|
12
|
+
expect(constraint.matches?(request)).to eq(false)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
context "accept header missing version" do
|
16
|
+
let(:headers) { { accept: "application/json" } }
|
17
|
+
it "doesn't match" do
|
18
|
+
expect(constraint.matches?(request)).to eq(false)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
context "accept header has wrong version" do
|
22
|
+
let(:headers) { { accept: "application/json; version=1" } }
|
23
|
+
it "doesn't match" do
|
24
|
+
expect(constraint.matches?(request)).to eq(false)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
context "accept header has correct version" do
|
28
|
+
let(:headers) { { accept: "application/json; version=2" } }
|
29
|
+
it "matcheds" do
|
30
|
+
expect(constraint.matches?(request)).to eq(true)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,105 @@
|
|
1
|
+
require 'spec_helper.rb'
|
2
|
+
|
3
|
+
describe Stitches::Configuration do
|
4
|
+
before do
|
5
|
+
Stitches.configuration.reset_to_defaults!
|
6
|
+
end
|
7
|
+
|
8
|
+
describe "global configuration" do
|
9
|
+
let(:whitelist_regexp) { %r{foo} }
|
10
|
+
let(:custom_http_auth_scheme) { "Blah" }
|
11
|
+
let(:env_var_to_hold_api_client_primary_key) { "FOOBAR" }
|
12
|
+
|
13
|
+
it "can be configured globally" do
|
14
|
+
Stitches.configure do |config|
|
15
|
+
config.whitelist_regexp = whitelist_regexp
|
16
|
+
config.custom_http_auth_scheme = custom_http_auth_scheme
|
17
|
+
config.env_var_to_hold_api_client_primary_key = env_var_to_hold_api_client_primary_key
|
18
|
+
end
|
19
|
+
|
20
|
+
expect(Stitches.configuration.whitelist_regexp).to eq(whitelist_regexp)
|
21
|
+
expect(Stitches.configuration.custom_http_auth_scheme).to eq(custom_http_auth_scheme)
|
22
|
+
expect(Stitches.configuration.env_var_to_hold_api_client_primary_key).to eq(env_var_to_hold_api_client_primary_key)
|
23
|
+
end
|
24
|
+
|
25
|
+
it "defaults to nil for whitelist_regexp" do
|
26
|
+
expect(Stitches.configuration.whitelist_regexp).to be_nil
|
27
|
+
end
|
28
|
+
|
29
|
+
it "sets a default for env_var_to_hold_api_client_primary_key" do
|
30
|
+
expect(Stitches.configuration.env_var_to_hold_api_client_primary_key).to eq("STITCHES_API_CLIENT_ID")
|
31
|
+
end
|
32
|
+
|
33
|
+
it "blows up if you try to use custom_http_auth_scheme without having set it" do
|
34
|
+
expect {
|
35
|
+
Stitches.configuration.custom_http_auth_scheme
|
36
|
+
}.to raise_error(/you must set a value for custom_http_auth_scheme/i)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
describe "whitelist_regexp" do
|
40
|
+
let(:config) { Stitches::Configuration.new }
|
41
|
+
it "must be a regexp" do
|
42
|
+
expect {
|
43
|
+
config.whitelist_regexp = "foo"
|
44
|
+
}.to raise_error(/whitelist_regexp must be a Regexp/i)
|
45
|
+
end
|
46
|
+
it "may be nil" do
|
47
|
+
expect {
|
48
|
+
config.whitelist_regexp = nil
|
49
|
+
}.not_to raise_error
|
50
|
+
end
|
51
|
+
it "may be a regexp" do
|
52
|
+
expect {
|
53
|
+
config.whitelist_regexp = /foo/
|
54
|
+
}.not_to raise_error
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
describe "custom_http_auth_scheme" do
|
59
|
+
let(:config) { Stitches::Configuration.new }
|
60
|
+
it "must be a string" do
|
61
|
+
expect {
|
62
|
+
config.custom_http_auth_scheme = 42
|
63
|
+
}.to raise_error(/custom_http_auth_scheme must be a String/i)
|
64
|
+
end
|
65
|
+
it "may not be nil" do
|
66
|
+
expect {
|
67
|
+
config.custom_http_auth_scheme = nil
|
68
|
+
}.to raise_error(/custom_http_auth_scheme may not be blank/i)
|
69
|
+
end
|
70
|
+
it "may not be a blank string" do
|
71
|
+
expect {
|
72
|
+
config.custom_http_auth_scheme = " "
|
73
|
+
}.to raise_error(/custom_http_auth_scheme may not be blank/i)
|
74
|
+
end
|
75
|
+
it "may be a String" do
|
76
|
+
expect {
|
77
|
+
config.custom_http_auth_scheme = "Foobar"
|
78
|
+
}.not_to raise_error
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
describe "env_var_to_hold_api_client_primary_key" do
|
83
|
+
let(:config) { Stitches::Configuration.new }
|
84
|
+
it "must be a string" do
|
85
|
+
expect {
|
86
|
+
config.env_var_to_hold_api_client_primary_key = 42
|
87
|
+
}.to raise_error(/env_var_to_hold_api_client_primary_key must be a String/i)
|
88
|
+
end
|
89
|
+
it "may not be nil" do
|
90
|
+
expect {
|
91
|
+
config.env_var_to_hold_api_client_primary_key = nil
|
92
|
+
}.to raise_error(/env_var_to_hold_api_client_primary_key may not be blank/i)
|
93
|
+
end
|
94
|
+
it "may not be a blank string" do
|
95
|
+
expect {
|
96
|
+
config.env_var_to_hold_api_client_primary_key = " "
|
97
|
+
}.to raise_error(/env_var_to_hold_api_client_primary_key may not be blank/i)
|
98
|
+
end
|
99
|
+
it "may be a String" do
|
100
|
+
expect {
|
101
|
+
config.env_var_to_hold_api_client_primary_key = "Foobar"
|
102
|
+
}.not_to raise_error
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
data/spec/errors_spec.rb
ADDED
@@ -0,0 +1,99 @@
|
|
1
|
+
require 'spec_helper.rb'
|
2
|
+
|
3
|
+
class MyFakeError < StandardError
|
4
|
+
end
|
5
|
+
|
6
|
+
class FakePersonHolder
|
7
|
+
include ActiveModel::Validations
|
8
|
+
attr_accessor :name, :person
|
9
|
+
|
10
|
+
validates_presence_of :name
|
11
|
+
|
12
|
+
def valid?
|
13
|
+
# doing this because we can't use validates_associated on a non-AR object, and
|
14
|
+
# our logic doesn't depend on validates_associated, per se
|
15
|
+
super.tap {
|
16
|
+
unless person.valid?
|
17
|
+
errors.add(:person,"is not valid")
|
18
|
+
end
|
19
|
+
}
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
class FakePerson
|
24
|
+
include ActiveModel::Validations
|
25
|
+
|
26
|
+
attr_accessor :first_name, :last_name, :age
|
27
|
+
|
28
|
+
validates_each :first_name, :last_name do |record, attr, value|
|
29
|
+
record.errors.add attr, 'starts with z.' if value.to_s[0] == ?z
|
30
|
+
end
|
31
|
+
|
32
|
+
validates_numericality_of :age
|
33
|
+
validates_presence_of :first_name
|
34
|
+
end
|
35
|
+
|
36
|
+
describe Stitches::Errors do
|
37
|
+
it "can be created from an exception" do
|
38
|
+
exception = MyFakeError.new("OH NOES!")
|
39
|
+
errors = Stitches::Errors.from_exception(exception)
|
40
|
+
|
41
|
+
expect(errors.size).to eq(1)
|
42
|
+
expect(errors.first.code).to eq("my_fake")
|
43
|
+
expect(errors.first.message).to eq("OH NOES!")
|
44
|
+
end
|
45
|
+
|
46
|
+
it "renders useful JSON" do
|
47
|
+
errors = Stitches::Errors.new([
|
48
|
+
Stitches::Error.new(code: "not_found", message: "Was not found, yo"),
|
49
|
+
Stitches::Error.new(code: "and_you_should_feel_bad", message: "And you should feel bad about even asking"),
|
50
|
+
])
|
51
|
+
|
52
|
+
expect(errors.to_json).to eq(
|
53
|
+
[
|
54
|
+
{
|
55
|
+
"code" => "not_found",
|
56
|
+
"message" => "Was not found, yo",
|
57
|
+
},
|
58
|
+
{
|
59
|
+
"code" => "and_you_should_feel_bad",
|
60
|
+
"message" => "And you should feel bad about even asking",
|
61
|
+
}
|
62
|
+
].to_json
|
63
|
+
)
|
64
|
+
end
|
65
|
+
|
66
|
+
context "creation from an active record object" do
|
67
|
+
let(:object) { FakePerson.new.tap { |person|
|
68
|
+
person.age = "asdfasdf"
|
69
|
+
person.last_name = "zjohnson"
|
70
|
+
}}
|
71
|
+
it "sets reasonable messages for the fields of the object" do
|
72
|
+
object.valid?
|
73
|
+
errors = Stitches::Errors.from_active_record_object(object)
|
74
|
+
errors_hash = JSON.parse(errors.to_json).sort_by {|_| _["code"] }
|
75
|
+
expect(errors_hash[0]["code"]).to eq("age_invalid")
|
76
|
+
expect(errors_hash[0]["message"]).to eq("Age is not a number")
|
77
|
+
expect(errors_hash[1]["code"]).to eq("first_name_invalid")
|
78
|
+
expect(errors_hash[1]["message"]).to eq("First name can't be blank")
|
79
|
+
expect(errors_hash[2]["code"]).to eq("last_name_invalid")
|
80
|
+
expect(errors_hash[2]["message"]).to eq("Last name starts with z.")
|
81
|
+
end
|
82
|
+
|
83
|
+
it "digs one level deep into the object for associated active-records" do
|
84
|
+
holder = FakePersonHolder.new
|
85
|
+
holder.name = nil
|
86
|
+
holder.person = object
|
87
|
+
|
88
|
+
holder.valid?
|
89
|
+
|
90
|
+
errors = Stitches::Errors.from_active_record_object(holder)
|
91
|
+
errors_hash = JSON.parse(errors.to_json).sort_by {|_| _["code"] }
|
92
|
+
expect(errors_hash[0]["code"]).to eq("name_invalid")
|
93
|
+
expect(errors_hash[0]["message"]).to eq("Name can't be blank")
|
94
|
+
expect(errors_hash[1]["code"]).to eq("person_invalid")
|
95
|
+
expect(errors_hash[1]["message"]).to eq("Age is not a number, First name can't be blank, Last name starts with z.")
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
@@ -0,0 +1,78 @@
|
|
1
|
+
require 'spec_helper.rb'
|
2
|
+
require 'stitches/spec'
|
3
|
+
|
4
|
+
describe "have_api_error" do
|
5
|
+
let(:errors) {
|
6
|
+
[
|
7
|
+
{ code: "foo", message: "bar" },
|
8
|
+
{ code: "baz", message: "quux" }
|
9
|
+
]
|
10
|
+
}
|
11
|
+
let(:response) {
|
12
|
+
double(
|
13
|
+
response_code: response_code,
|
14
|
+
body: { errors: errors }.to_json)
|
15
|
+
}
|
16
|
+
context "missing required arguments from expectation" do
|
17
|
+
let(:response_code) { 422 }
|
18
|
+
it "blows up with a decent message" do
|
19
|
+
expect {
|
20
|
+
expect(response).to have_api_error(message: errors.first[:message])
|
21
|
+
}.to raise_error(/key not found: :code/)
|
22
|
+
expect {
|
23
|
+
expect(response).to have_api_error(code: errors.first[:code])
|
24
|
+
}.to raise_error(/key not found: :message/)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
context "no expected status specified" do
|
28
|
+
context "status is 422" do
|
29
|
+
let(:response_code) { 422 }
|
30
|
+
context "an error in the expectation exists" do
|
31
|
+
it "indicates there is an error" do
|
32
|
+
expect(response).to have_api_error(errors.first)
|
33
|
+
end
|
34
|
+
it "indicates there is an error for another error in the errors list" do
|
35
|
+
expect(response).to have_api_error(errors.second)
|
36
|
+
end
|
37
|
+
it "indicates there is an error via regexp" do
|
38
|
+
expect(response).to have_api_error(code: errors.second[:code], message: /^.*$/)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
context "no error from the expectation exists" do
|
42
|
+
it "indicates there is no error" do
|
43
|
+
expect(response).not_to have_api_error(code: "blah", message: "crud")
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
context "status is 200" do
|
48
|
+
let(:response_code) { 200 }
|
49
|
+
it "indicates there is no error" do
|
50
|
+
expect(response).not_to have_api_error(errors.first)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
context "status is e.g. 404" do
|
54
|
+
let(:response_code) { 404 }
|
55
|
+
it "indicates there is no error" do
|
56
|
+
expect(response).not_to have_api_error(errors.first)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
context "expected status is specified" do
|
61
|
+
context "status is the expected status" do
|
62
|
+
let(:response_code) { 404 }
|
63
|
+
it "indicates there is an error" do
|
64
|
+
expect(response).to have_api_error(status: 404,
|
65
|
+
code: errors.first[:code],
|
66
|
+
message: errors.first[:message])
|
67
|
+
end
|
68
|
+
end
|
69
|
+
context "status is not the expected status" do
|
70
|
+
let(:response_code) { 422 }
|
71
|
+
it "indicates there is no error" do
|
72
|
+
expect(response).not_to have_api_error(status: 404,
|
73
|
+
code: errors.first[:code],
|
74
|
+
message: errors.first[:bar])
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,10 @@
|
|
1
|
+
GEM_ROOT = File.expand_path(File.join(File.dirname(__FILE__),'..'))
|
2
|
+
Dir["#{GEM_ROOT}/spec/support/**/*.rb"].sort.each {|f| require f}
|
3
|
+
|
4
|
+
require 'rails/all'
|
5
|
+
require 'stitches'
|
6
|
+
|
7
|
+
RSpec.configure do |config|
|
8
|
+
config.order = "random"
|
9
|
+
end
|
10
|
+
I18n.enforce_available_locales = false # situps
|
@@ -0,0 +1,166 @@
|
|
1
|
+
require 'spec_helper.rb'
|
2
|
+
|
3
|
+
describe Stitches::ValidMimeType do
|
4
|
+
let(:app) { double("rack app") }
|
5
|
+
|
6
|
+
before do
|
7
|
+
allow(app).to receive(:call).with(env)
|
8
|
+
end
|
9
|
+
|
10
|
+
subject(:middleware) { described_class.new(app, namespace: "/api") }
|
11
|
+
|
12
|
+
shared_examples "an unacceptable response" do
|
13
|
+
it "returns a 406" do
|
14
|
+
expect(@response.status).to eq(406)
|
15
|
+
end
|
16
|
+
it "stops the call chain preventing anything from happening" do
|
17
|
+
expect(app).not_to have_received(:call)
|
18
|
+
end
|
19
|
+
it "sends a reasonable message" do
|
20
|
+
expect(@response.body.first).to match(/didn't have the right mime type or version number. We only accept application\/json/)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
describe "#call" do
|
25
|
+
context "not in namespace" do
|
26
|
+
context "not in whitelist" do
|
27
|
+
let(:env) {
|
28
|
+
{
|
29
|
+
"PATH_INFO" => "/index/home",
|
30
|
+
}
|
31
|
+
}
|
32
|
+
|
33
|
+
before do
|
34
|
+
@response = middleware.call(env)
|
35
|
+
end
|
36
|
+
|
37
|
+
it_behaves_like "an unacceptable response"
|
38
|
+
end
|
39
|
+
context "whitelisting" do
|
40
|
+
let(:env) {
|
41
|
+
{
|
42
|
+
"PATH_INFO" => "/index/home",
|
43
|
+
}
|
44
|
+
}
|
45
|
+
|
46
|
+
context "whitelist is explicit in middleware usage" do
|
47
|
+
before do
|
48
|
+
@response = middleware.call(env)
|
49
|
+
end
|
50
|
+
|
51
|
+
context "passes the whitelist" do
|
52
|
+
subject(:middleware) { described_class.new(app, except: %r{\A/resque\/.*\Z}) }
|
53
|
+
let(:env) {
|
54
|
+
{
|
55
|
+
"PATH_INFO" => "/resque/overview"
|
56
|
+
}
|
57
|
+
}
|
58
|
+
it "calls through to the rest of the chain" do
|
59
|
+
expect(app).to have_received(:call).with(env)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
context "fails the whitelist" do
|
64
|
+
subject(:middleware) { described_class.new(app, except: %r{\A/resque\/.*\Z}) }
|
65
|
+
let(:env) {
|
66
|
+
{
|
67
|
+
"PATH_INFO" => "//resque/overview" # subtle
|
68
|
+
}
|
69
|
+
}
|
70
|
+
it_behaves_like "an unacceptable response"
|
71
|
+
end
|
72
|
+
context "except: is not given a regexp" do
|
73
|
+
let(:env) {
|
74
|
+
{
|
75
|
+
"PATH_INFO" => "//resque/overview"
|
76
|
+
}
|
77
|
+
}
|
78
|
+
it "blows up" do
|
79
|
+
expect {
|
80
|
+
described_class.new(app, except: "/resque")
|
81
|
+
}.to raise_error(/must be a Regexp/i)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
context "whitelist is implicit from the configuration" do
|
86
|
+
|
87
|
+
before do
|
88
|
+
Stitches.configuration.whitelist_regexp = %r{\A/resque/.*\Z}
|
89
|
+
@response = middleware.call(env)
|
90
|
+
end
|
91
|
+
|
92
|
+
context "passes the whitelist" do
|
93
|
+
subject(:middleware) { described_class.new(app) }
|
94
|
+
let(:env) {
|
95
|
+
{
|
96
|
+
"PATH_INFO" => "/resque/overview"
|
97
|
+
}
|
98
|
+
}
|
99
|
+
it "calls through to the rest of the chain" do
|
100
|
+
expect(app).to have_received(:call).with(env)
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
context "fails the whitelist" do
|
105
|
+
subject(:middleware) { described_class.new(app) }
|
106
|
+
let(:env) {
|
107
|
+
{
|
108
|
+
"PATH_INFO" => "//resque/overview" # subtle
|
109
|
+
}
|
110
|
+
}
|
111
|
+
it_behaves_like "an unacceptable response"
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
context "valid header" do
|
118
|
+
let(:env) {
|
119
|
+
{
|
120
|
+
"PATH_INFO" => "/api/ping",
|
121
|
+
"HTTP_ACCEPT" => "application/json; version=99",
|
122
|
+
}
|
123
|
+
}
|
124
|
+
|
125
|
+
before do
|
126
|
+
@response = middleware.call(env)
|
127
|
+
end
|
128
|
+
it "calls through to the rest of the chain" do
|
129
|
+
expect(app).to have_received(:call).with(env)
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
context "unacceptable responses" do
|
134
|
+
before do
|
135
|
+
@response = middleware.call(env)
|
136
|
+
@response.finish
|
137
|
+
end
|
138
|
+
context "no header" do
|
139
|
+
let(:env) {
|
140
|
+
{
|
141
|
+
"PATH_INFO" => "/api/ping",
|
142
|
+
}
|
143
|
+
}
|
144
|
+
it_behaves_like "an unacceptable response"
|
145
|
+
end
|
146
|
+
context "bad mime type" do
|
147
|
+
let(:env) {
|
148
|
+
{
|
149
|
+
"PATH_INFO" => "/api/ping",
|
150
|
+
"HTTP_ACCEPT" => "application/json; version=bleorgh",
|
151
|
+
}
|
152
|
+
}
|
153
|
+
it_behaves_like "an unacceptable response"
|
154
|
+
end
|
155
|
+
context "bad version" do
|
156
|
+
let(:env) {
|
157
|
+
{
|
158
|
+
"PATH_INFO" => "/api/ping",
|
159
|
+
"HTTP_ACCEPT" => "application/xml; version=1",
|
160
|
+
}
|
161
|
+
}
|
162
|
+
it_behaves_like "an unacceptable response"
|
163
|
+
end
|
164
|
+
end
|
165
|
+
end
|
166
|
+
end
|