/
request_body_validator_spec.rb
196 lines (163 loc) · 5.99 KB
/
request_body_validator_spec.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
require 'fast_spec_helper'
require 'rack/test'
require 'interpol/request_body_validator'
module Interpol
describe RequestBodyValidator do
include Rack::Test::Methods
let_without_indentation(:endpoint_definition_yml) do <<-EOF
---
name: parsed_body
route: /parsed_body
method: POST
definitions:
- versions: ["1.0"]
message_type: request
schema:
type: object
properties:
id:
type: integer
name:
type: string
examples: []
EOF
end
before do
Interpol.default_configuration do |config|
config.endpoints = [Endpoint.new(YAML.load endpoint_definition_yml)]
config.request_version '1.0'
end
end
def override_config(&block)
@override_config = block
end
let(:app) do
_override_config = @override_config || Proc.new { }
Rack::Builder.new do
use(Interpol::RequestBodyValidator, &_override_config)
use Rack::ContentLength
map('/parsed_body') do
run lambda { |env|
body = if env['HTTP_READ_BODY']
env.fetch('rack.input').read
else
parsed = env.fetch('interpol.parsed_body')
"id: #{parsed.id}; name: #{parsed.name}"
end
[ 200, { 'Content-Type' => 'text/plain' }, [body]]
}
end
end
end
let(:valid_json_body) { JSON.dump("id" => 3, "name" => "foo") }
let(:invalid_json_body) { JSON.dump("id" => "not a number", "name" => "foo") }
it 'responds with a 400 by default when it fails validation' do
header 'Content-Type', 'application/json'
post '/parsed_body', invalid_json_body
last_response.status.should eq(400)
last_response.body.should include("validating", "parsed_body")
end
it 'uses the configured on_invalid_request_body hook' do
Interpol.default_configuration do |config|
config.on_invalid_request_body do |env, error|
[412, { 'Content-Type' => 'text/plain' }, ["abc"]]
end
end
header 'Content-Type', 'application/json'
post '/parsed_body', invalid_json_body
last_response.status.should eq(412)
last_response.body.should eq("abc")
end
it 'makes the parsed body object available as `interpol.parsed_body`' do
header 'Content-Type', 'application/json'
post '/parsed_body', valid_json_body
last_response.body.should eq("id: 3; name: foo")
end
it 'allows the default config to be overriden' do
Interpol.default_configuration do |config|
config.request_version '2000.0'
end
override_config do |config|
config.request_version '1.0'
end
header 'Content-Type', 'application/json'
post '/parsed_body', valid_json_body
last_response.body.should eq("id: 3; name: foo")
end
it 'rewinds the input stream after reading it' do
header 'Read-Body', 'true'
header 'Content-Type', 'application/json'
post '/parsed_body', valid_json_body
last_response.body.should eq(valid_json_body)
end
it 'does not attempt to validate a GET or DELETE request by default' do
header 'Read-Body', 'true'
header 'Content-Type', 'application/json'
get '/parsed_body', invalid_json_body
last_response.status.should eq(200)
delete '/parsed_body', invalid_json_body
last_response.status.should eq(200)
end
it 'does not blow up if given no content type' do
header 'Read-Body', 'true'
get '/parsed_body', invalid_json_body
last_response.status.should eq(200)
end
it 'does not attempt to validate non-JSON by default' do
header 'Read-Body', 'true'
header 'Content-Type', 'text/plain'
post '/parsed_body', "some content"
last_response.body.should eq("some content")
end
it 'allows users to override the validate_request_if config' do
override_config do |config|
config.validate_request_if do |env|
true
end
end
header 'Content-Type', 'text/plain'
post '/parsed_body', valid_json_body
last_response.body.should eq("id: 3; name: foo")
end
it 'responds with a 406 by default when no matching version can be found' do
wrong_version_yaml = endpoint_definition_yml.gsub('1.0', '2.0')
override_config do |config|
config.endpoints = [Endpoint.new(YAML.load wrong_version_yaml)]
end
header 'Content-Type', 'application/json'
post '/parsed_body', valid_json_body
last_response.status.should eq(406)
end
it 'uses the on_unavailable_request_version hook to respond in ' +
'cases where no version can be found' do
wrong_version_yaml = endpoint_definition_yml.gsub('1.0', '2.0')
override_config do |config|
config.endpoints = [Endpoint.new(YAML.load wrong_version_yaml)]
config.on_unavailable_request_version do |env, requested, available|
[315, { 'Content-Type' => 'text/plain' },
["Method: #{env.fetch('REQUEST_METHOD')}; ",
"Requested: #{requested.inspect}; ",
"Available: #{available.inspect}"]]
end
end
header 'Content-Type', 'application/json'
post '/parsed_body', valid_json_body
last_response.status.should eq(315)
last_response.body.should eq('Method: POST; Requested: "1.0"; Available: ["2.0"]')
end
it 'uses the request_version_for callback to select the version' do
wrong_version_yaml = endpoint_definition_yml.gsub('1.0', '2.0')
override_config do |config|
config.endpoints = [Endpoint.new(YAML.load wrong_version_yaml)]
config.request_version do |env, endpoint|
env.fetch('REQUEST_METHOD').should eq('POST')
endpoint.should be_a(Interpol::Endpoint)
'2.0'
end
end
header 'Content-Type', 'application/json'
post '/parsed_body', valid_json_body
last_response.status.should eq(200)
end
end
end