committee 0.4.2 → 0.4.3
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/committee/errors.rb +9 -0
- data/lib/committee/param_validator.rb +16 -66
- data/lib/committee/response_validator.rb +16 -49
- data/lib/committee/test/methods.rb +36 -0
- data/lib/committee/validation.rb +81 -0
- data/lib/committee.rb +3 -0
- data/test/param_validator_test.rb +48 -22
- data/test/response_validator_test.rb +55 -5
- data/test/test/methods_test.rb +69 -0
- data/test/test_helper.rb +12 -0
- metadata +5 -2
data/lib/committee/errors.rb
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
module Committee
|
2
2
|
class ParamValidator
|
3
|
+
include Validation
|
4
|
+
|
3
5
|
def initialize(params, schema, link_schema)
|
4
6
|
@params = params
|
5
7
|
@schema = schema
|
@@ -31,79 +33,27 @@ module Committee
|
|
31
33
|
try_match(key, @params[key], definitions)
|
32
34
|
else
|
33
35
|
# only assume one possible array definition for now
|
34
|
-
|
36
|
+
definitions = find_definitions(value["items"]["$ref"])
|
37
|
+
array_definition = definitions[0]
|
35
38
|
@params[key].each do |item|
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
39
|
+
# separate logic for a complex object that includes properties
|
40
|
+
if array_definition.key?("properties")
|
41
|
+
array_definition["properties"].each do |array_key, array_value|
|
42
|
+
return unless item.key?(array_key)
|
43
|
+
|
44
|
+
# @todo: this should really be recursive; only one array level is
|
45
|
+
# supported for now
|
46
|
+
item_definitions = find_definitions(array_value["$ref"])
|
47
|
+
try_match(array_key, item[array_key], item_definitions)
|
48
|
+
end
|
49
|
+
else
|
50
|
+
try_match(key, item, definitions)
|
43
51
|
end
|
44
52
|
end
|
45
53
|
end
|
46
54
|
end
|
47
55
|
end
|
48
56
|
|
49
|
-
def check_format(format, value, key)
|
50
|
-
case format
|
51
|
-
when "date-time"
|
52
|
-
value =~ /^(\d{4})-(\d{2})-(\d{2})T(\d{2})\:(\d{2})\:(\d{2})(Z|[+-](\d{2})\:(\d{2}))$/
|
53
|
-
when "email"
|
54
|
-
value =~ /^[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,4}$/i
|
55
|
-
when "uuid"
|
56
|
-
value =~ /^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/
|
57
|
-
else
|
58
|
-
true
|
59
|
-
end
|
60
|
-
end
|
61
|
-
|
62
|
-
def check_format!(format, value, key)
|
63
|
-
unless check_format(format, value, key)
|
64
|
-
raise InvalidParams,
|
65
|
-
%{Invalid format for key "#{key}": expected "#{value}" to be "#{format}".}
|
66
|
-
end
|
67
|
-
end
|
68
|
-
|
69
|
-
def check_pattern(pattern, value, key)
|
70
|
-
!pattern || value =~ pattern
|
71
|
-
end
|
72
|
-
|
73
|
-
def check_pattern!(pattern, value, key)
|
74
|
-
unless check_pattern(pattern, value, key)
|
75
|
-
raise InvalidParams,
|
76
|
-
%{Invalid pattern for key "#{key}": expected #{value} to match "#{pattern}".}
|
77
|
-
end
|
78
|
-
end
|
79
|
-
|
80
|
-
def check_type(allowed_types, value, key)
|
81
|
-
types = case value
|
82
|
-
when NilClass
|
83
|
-
["null"]
|
84
|
-
when TrueClass, FalseClass
|
85
|
-
["boolean"]
|
86
|
-
when Bignum, Fixnum
|
87
|
-
["integer", "number"]
|
88
|
-
when Float
|
89
|
-
["number"]
|
90
|
-
when Hash
|
91
|
-
["object"]
|
92
|
-
when String
|
93
|
-
["string"]
|
94
|
-
else
|
95
|
-
["unknown"]
|
96
|
-
end
|
97
|
-
!(allowed_types & types).empty?
|
98
|
-
end
|
99
|
-
|
100
|
-
def check_type!(types, value, key)
|
101
|
-
unless check_type(types, value, key)
|
102
|
-
raise InvalidParams,
|
103
|
-
%{Invalid type for key "#{key}": expected #{value} to be #{types}.}
|
104
|
-
end
|
105
|
-
end
|
106
|
-
|
107
57
|
def detect_extra!
|
108
58
|
extra = @params.keys - all_keys
|
109
59
|
if extra.count > 0
|
@@ -1,6 +1,9 @@
|
|
1
1
|
module Committee
|
2
2
|
class ResponseValidator
|
3
|
+
include Validation
|
4
|
+
|
3
5
|
def initialize(data, schema, link_schema, type_schema)
|
6
|
+
|
4
7
|
@data = data
|
5
8
|
@schema = schema
|
6
9
|
@link_schema = link_schema
|
@@ -8,7 +11,7 @@ module Committee
|
|
8
11
|
end
|
9
12
|
|
10
13
|
def call
|
11
|
-
data = if @link_schema["
|
14
|
+
data = if @link_schema["rel"] == "instances"
|
12
15
|
if !@data.is_a?(Array)
|
13
16
|
raise InvalidResponse, "List endpoints must return an array of objects."
|
14
17
|
end
|
@@ -45,6 +48,16 @@ module Committee
|
|
45
48
|
schema["properties"].each do |key, value|
|
46
49
|
if value["properties"]
|
47
50
|
check_data!(value, data[key], path + [key])
|
51
|
+
elsif value["type"] == ["array"]
|
52
|
+
definition = @schema.find(value["items"]["$ref"])
|
53
|
+
data[key].each do |datum|
|
54
|
+
check_type!(definition["type"], datum, path + [key])
|
55
|
+
unless definition["type"].include?("null") && datum.nil?
|
56
|
+
check_format!(definition["format"], datum, path + [key])
|
57
|
+
check_pattern!(definition["pattern"], datum, path + [key])
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
48
61
|
else
|
49
62
|
definition = @schema.find(value["$ref"])
|
50
63
|
check_type!(definition["type"], data[key], path + [key])
|
@@ -56,54 +69,6 @@ module Committee
|
|
56
69
|
end
|
57
70
|
end
|
58
71
|
|
59
|
-
def check_format!(format, value, path)
|
60
|
-
return if !format
|
61
|
-
valid = case format
|
62
|
-
when "date-time"
|
63
|
-
value =~ /^(\d{4})-(\d{2})-(\d{2})T(\d{2})\:(\d{2})\:(\d{2})(Z|[+-](\d{2})\:(\d{2}))$/
|
64
|
-
when "email"
|
65
|
-
value =~ /^[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,4}$/i
|
66
|
-
when "uuid"
|
67
|
-
value =~ /^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/
|
68
|
-
else
|
69
|
-
true
|
70
|
-
end
|
71
|
-
unless valid
|
72
|
-
raise InvalidResponse,
|
73
|
-
%{Invalid format at "#{path.join(":")}": expected "#{value}" to be "#{format}".}
|
74
|
-
end
|
75
|
-
end
|
76
|
-
|
77
|
-
def check_pattern!(pattern, value, path)
|
78
|
-
if pattern && !(value =~ pattern)
|
79
|
-
raise InvalidResponse,
|
80
|
-
%{Invalid pattern at "#{path.join(":")}": expected #{value} to match "#{pattern}".}
|
81
|
-
end
|
82
|
-
end
|
83
|
-
|
84
|
-
def check_type!(allowed_types, value, path)
|
85
|
-
types = case value
|
86
|
-
when NilClass
|
87
|
-
["null"]
|
88
|
-
when TrueClass, FalseClass
|
89
|
-
["boolean"]
|
90
|
-
when Bignum, Fixnum
|
91
|
-
["integer", "number"]
|
92
|
-
when Float
|
93
|
-
["number"]
|
94
|
-
when Hash
|
95
|
-
["object"]
|
96
|
-
when String
|
97
|
-
["string"]
|
98
|
-
else
|
99
|
-
["unknown"]
|
100
|
-
end
|
101
|
-
if (allowed_types & types).empty?
|
102
|
-
raise InvalidResponse,
|
103
|
-
%{Invalid type at "#{path.join(":")}": expected #{value} to be #{allowed_types} (was: #{types}).}
|
104
|
-
end
|
105
|
-
end
|
106
|
-
|
107
72
|
private
|
108
73
|
|
109
74
|
def build_data_keys(data)
|
@@ -123,6 +88,8 @@ module Committee
|
|
123
88
|
@type_schema["properties"].each do |key, info|
|
124
89
|
data = if info["properties"]
|
125
90
|
info
|
91
|
+
elsif info["type"] == ["array"]
|
92
|
+
@schema.find(info["items"]["$ref"])
|
126
93
|
elsif info["$ref"]
|
127
94
|
@schema.find(info["$ref"])
|
128
95
|
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module Committee::Test
|
2
|
+
module Methods
|
3
|
+
def assert_schema_conform
|
4
|
+
assert_schema_content_type
|
5
|
+
|
6
|
+
@schema ||= Committee::Schema.new(File.read(schema_path))
|
7
|
+
@router ||= Committee::Router.new(@schema)
|
8
|
+
|
9
|
+
link_schema, type_schema = @router.routes_request?(last_request)
|
10
|
+
|
11
|
+
unless link_schema
|
12
|
+
response = "`#{last_request.request_method} #{last_request.path_info}` undefined in schema."
|
13
|
+
raise Committee::InvalidResponse.new(response)
|
14
|
+
end
|
15
|
+
|
16
|
+
data = MultiJson.decode(last_response.body)
|
17
|
+
Committee::ResponseValidator.new(
|
18
|
+
data,
|
19
|
+
@schema,
|
20
|
+
link_schema,
|
21
|
+
type_schema
|
22
|
+
).call
|
23
|
+
end
|
24
|
+
|
25
|
+
def assert_schema_content_type
|
26
|
+
unless last_response.headers["Content-Type"] =~ %r{application/json}
|
27
|
+
raise Committee::InvalidResponse,
|
28
|
+
%{"Content-Type" response header must be set to "application/json".}
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def schema_path
|
33
|
+
raise "Please override #schema_path."
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
module Committee
|
2
|
+
module Validation
|
3
|
+
def check_format!(format, value, identifier)
|
4
|
+
return if !format
|
5
|
+
return if check_format(format, value, identifier)
|
6
|
+
|
7
|
+
description = case identifier
|
8
|
+
when String
|
9
|
+
%{Invalid format for key "#{identifier}": expected "#{value}" to be "#{format}".}
|
10
|
+
when Array
|
11
|
+
%{Invalid format at "#{identifier.join(":")}": expected "#{value}" to be "#{format}".}
|
12
|
+
end
|
13
|
+
|
14
|
+
raise InvalidFormat, description
|
15
|
+
end
|
16
|
+
|
17
|
+
def check_format(format, value, identifier)
|
18
|
+
case format
|
19
|
+
when "date-time"
|
20
|
+
value =~ /^(\d{4})-(\d{2})-(\d{2})T(\d{2})\:(\d{2})\:(\d{2})(\.\d{1,})?(Z|[+-](\d{2})\:(\d{2}))$/
|
21
|
+
when "email"
|
22
|
+
value =~ /^[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,4}$/i
|
23
|
+
when "uuid"
|
24
|
+
value =~ /^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/
|
25
|
+
else
|
26
|
+
true
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def check_type!(allowed_types, value, identifier)
|
31
|
+
return if check_type(allowed_types, value, identifier)
|
32
|
+
|
33
|
+
description = case identifier
|
34
|
+
when String
|
35
|
+
%{Invalid type for key "#{identifier}": expected #{value} to be #{allowed_types}.}
|
36
|
+
when Array
|
37
|
+
%{Invalid type at "#{identifier.join(":")}": expected #{value} to be #{allowed_types}.}
|
38
|
+
end
|
39
|
+
|
40
|
+
raise InvalidType, description
|
41
|
+
end
|
42
|
+
|
43
|
+
def check_type(allowed_types, value, identifier)
|
44
|
+
types = case value
|
45
|
+
when NilClass
|
46
|
+
["null"]
|
47
|
+
when TrueClass, FalseClass
|
48
|
+
["boolean"]
|
49
|
+
when Bignum, Fixnum
|
50
|
+
["integer", "number"]
|
51
|
+
when Float
|
52
|
+
["number"]
|
53
|
+
when Hash
|
54
|
+
["object"]
|
55
|
+
when String
|
56
|
+
["string"]
|
57
|
+
else
|
58
|
+
["unknown"]
|
59
|
+
end
|
60
|
+
|
61
|
+
!(allowed_types & types).empty?
|
62
|
+
end
|
63
|
+
|
64
|
+
def check_pattern!(pattern, value, identifier)
|
65
|
+
return if check_pattern(pattern, value, identifier)
|
66
|
+
|
67
|
+
description = case identifier
|
68
|
+
when String
|
69
|
+
%{Invalid pattern for key "#{identifier}": expected #{value} to match "#{pattern}".}
|
70
|
+
when Array
|
71
|
+
%{Invalid pattern at "#{identifier.join(":")}": expected #{value} to match "#{pattern}".}
|
72
|
+
end
|
73
|
+
|
74
|
+
raise InvalidPattern, description
|
75
|
+
end
|
76
|
+
|
77
|
+
def check_pattern(pattern, value, identifier)
|
78
|
+
!pattern || value =~ pattern
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
data/lib/committee.rb
CHANGED
@@ -2,6 +2,7 @@ require "multi_json"
|
|
2
2
|
require "rack"
|
3
3
|
|
4
4
|
require_relative "committee/errors"
|
5
|
+
require_relative "committee/validation"
|
5
6
|
require_relative "committee/param_validator"
|
6
7
|
require_relative "committee/request_unpacker"
|
7
8
|
require_relative "committee/response_generator"
|
@@ -13,3 +14,5 @@ require_relative "committee/middleware/base"
|
|
13
14
|
require_relative "committee/middleware/request_validation"
|
14
15
|
require_relative "committee/middleware/response_validation"
|
15
16
|
require_relative "committee/middleware/stub"
|
17
|
+
|
18
|
+
require_relative "committee/test/methods"
|
@@ -41,7 +41,7 @@ describe Committee::ParamValidator do
|
|
41
41
|
"app" => "heroku-api",
|
42
42
|
"recipient" => 123,
|
43
43
|
}
|
44
|
-
e = assert_raises(Committee::
|
44
|
+
e = assert_raises(Committee::InvalidType) do
|
45
45
|
Committee::ParamValidator.new(params, @schema, @link_schema).call
|
46
46
|
end
|
47
47
|
message = %{Invalid type for key "recipient": expected 123 to be ["string"].}
|
@@ -53,7 +53,7 @@ describe Committee::ParamValidator do
|
|
53
53
|
"app" => "heroku-api",
|
54
54
|
"recipient" => "not-email",
|
55
55
|
}
|
56
|
-
e = assert_raises(Committee::
|
56
|
+
e = assert_raises(Committee::InvalidFormat) do
|
57
57
|
Committee::ParamValidator.new(params, @schema, @link_schema).call
|
58
58
|
end
|
59
59
|
message = %{Invalid format for key "recipient": expected "not-email" to be "email".}
|
@@ -65,35 +65,61 @@ describe Committee::ParamValidator do
|
|
65
65
|
"name" => "%@!"
|
66
66
|
}
|
67
67
|
link_schema = @schema["app"]["links"][0]
|
68
|
-
e = assert_raises(Committee::
|
68
|
+
e = assert_raises(Committee::InvalidPattern) do
|
69
69
|
Committee::ParamValidator.new(params, @schema, link_schema).call
|
70
70
|
end
|
71
71
|
message = %{Invalid pattern for key "name": expected %@! to match "(?-mix:^[a-z][a-z0-9-]{3,30}$)".}
|
72
72
|
assert_equal message, e.message
|
73
73
|
end
|
74
74
|
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
75
|
+
describe "complex arrays" do
|
76
|
+
it "passes an array parameter" do
|
77
|
+
params = {
|
78
|
+
"updates" => [
|
79
|
+
{ "name" => "bamboo", "state" => "private" },
|
80
|
+
{ "name" => "cedar", "state" => "public" },
|
81
|
+
]
|
82
|
+
}
|
83
|
+
link_schema = @schema["stack"]["links"][2]
|
84
|
+
Committee::ParamValidator.new(params, @schema, link_schema).call
|
85
|
+
end
|
86
|
+
|
87
|
+
it "detects an array item with a parameter of the wrong type" do
|
88
|
+
params = {
|
89
|
+
"updates" => [
|
90
|
+
{ "name" => "bamboo", "state" => 123 },
|
91
|
+
]
|
92
|
+
}
|
93
|
+
link_schema = @schema["stack"]["links"][2]
|
94
|
+
e = assert_raises(Committee::InvalidType) do
|
95
|
+
Committee::ParamValidator.new(params, @schema, link_schema).call
|
96
|
+
end
|
97
|
+
message = %{Invalid type for key "state": expected 123 to be ["string"].}
|
98
|
+
assert_equal message, e.message
|
99
|
+
end
|
84
100
|
end
|
85
101
|
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
e = assert_raises(Committee::InvalidParams) do
|
102
|
+
describe "simple arrays" do
|
103
|
+
it "passes an array parameter" do
|
104
|
+
params = {
|
105
|
+
"password" => "1234",
|
106
|
+
"flags" => [ "vip", "customer" ]
|
107
|
+
}
|
108
|
+
link_schema = @schema["account"]["links"][1]
|
94
109
|
Committee::ParamValidator.new(params, @schema, link_schema).call
|
95
110
|
end
|
96
|
-
|
97
|
-
|
111
|
+
|
112
|
+
it "detects an array item with a parameter of the wrong type" do
|
113
|
+
params = {
|
114
|
+
"password" => "1234",
|
115
|
+
"flags" => [ "vip", "customer", 999 ]
|
116
|
+
}
|
117
|
+
link_schema = @schema["account"]["links"][1]
|
118
|
+
e = assert_raises(Committee::InvalidType) do
|
119
|
+
Committee::ParamValidator.new(params, @schema, link_schema).call
|
120
|
+
end
|
121
|
+
message = %{Invalid type for key "flags": expected 999 to be ["string"].}
|
122
|
+
assert_equal message, e.message
|
123
|
+
end
|
98
124
|
end
|
99
125
|
end
|
@@ -6,6 +6,7 @@ describe Committee::ResponseValidator do
|
|
6
6
|
@schema = Committee::Schema.new(File.read("./test/data/schema.json"))
|
7
7
|
# GET /apps/:id
|
8
8
|
@link_schema = @schema["app"]["links"][2]
|
9
|
+
@type_schema = @schema["app"]
|
9
10
|
end
|
10
11
|
|
11
12
|
it "passes through a valid response" do
|
@@ -19,6 +20,12 @@ describe Committee::ResponseValidator do
|
|
19
20
|
call
|
20
21
|
end
|
21
22
|
|
23
|
+
it "passes through a valid list response with non-default title" do
|
24
|
+
@data = [@data]
|
25
|
+
@link_schema = @schema["app"]["links"][4]
|
26
|
+
call
|
27
|
+
end
|
28
|
+
|
22
29
|
it "detects an improperly formatted list response" do
|
23
30
|
# GET /apps
|
24
31
|
@link_schema = @schema["app"]["links"][3]
|
@@ -43,25 +50,68 @@ describe Committee::ResponseValidator do
|
|
43
50
|
|
44
51
|
it "detects mismatched types" do
|
45
52
|
@data.merge!("maintenance" => "not-bool")
|
46
|
-
e = assert_raises(Committee::
|
47
|
-
message = %{Invalid type at "maintenance": expected not-bool to be ["boolean"]
|
53
|
+
e = assert_raises(Committee::InvalidType) { call }
|
54
|
+
message = %{Invalid type at "maintenance": expected not-bool to be ["boolean"].}
|
48
55
|
assert_equal message, e.message
|
49
56
|
end
|
50
57
|
|
51
58
|
it "detects bad formats" do
|
52
59
|
@data.merge!("id" => "123")
|
53
|
-
e = assert_raises(Committee::
|
60
|
+
e = assert_raises(Committee::InvalidFormat) { call }
|
54
61
|
message = %{Invalid format at "id": expected "123" to be "uuid".}
|
55
62
|
assert_equal message, e.message
|
56
63
|
end
|
57
64
|
|
58
65
|
it "detects bad patterns" do
|
59
66
|
@data.merge!("name" => "%@!")
|
60
|
-
e = assert_raises(Committee::
|
67
|
+
e = assert_raises(Committee::InvalidPattern) { call }
|
61
68
|
message = %{Invalid pattern at "name": expected %@! to match "(?-mix:^[a-z][a-z0-9-]{3,30}$)".}
|
62
69
|
assert_equal message, e.message
|
63
70
|
end
|
64
71
|
|
72
|
+
it "accepts date-time format without milliseconds" do
|
73
|
+
validator = Committee::ResponseValidator.new(
|
74
|
+
@data,
|
75
|
+
@schema,
|
76
|
+
@link_schema,
|
77
|
+
@schema["app"])
|
78
|
+
|
79
|
+
value = "2014-03-10T00:00:00Z"
|
80
|
+
assert_nil validator.check_format!("date-time", value, ["example"])
|
81
|
+
end
|
82
|
+
|
83
|
+
it "accepts date-time format with milliseconds" do
|
84
|
+
validator = Committee::ResponseValidator.new(
|
85
|
+
@data,
|
86
|
+
@schema,
|
87
|
+
@link_schema,
|
88
|
+
@schema["app"])
|
89
|
+
|
90
|
+
value = "2014-03-10T00:00:00.123Z"
|
91
|
+
assert_nil validator.check_format!("date-time", value, ["example"])
|
92
|
+
end
|
93
|
+
|
94
|
+
it "accepts a simple array" do
|
95
|
+
@data = ValidAccount.dup
|
96
|
+
@data["flags"] = @data["flags"].dup
|
97
|
+
@link_schema = @schema["account"]["links"][1]
|
98
|
+
@type_schema = @schema["account"]
|
99
|
+
|
100
|
+
call
|
101
|
+
end
|
102
|
+
|
103
|
+
it "detects a simple array with an item of the wrong type" do
|
104
|
+
@data = ValidAccount.dup
|
105
|
+
@data["flags"] = @data["flags"].dup
|
106
|
+
@data["flags"] << false
|
107
|
+
@link_schema = @schema["account"]["links"][1]
|
108
|
+
@type_schema = @schema["account"]
|
109
|
+
|
110
|
+
e = assert_raises(Committee::InvalidType) { call }
|
111
|
+
message = %{Invalid type at "flags": expected false to be ["string"].}
|
112
|
+
assert_equal message, e.message
|
113
|
+
end
|
114
|
+
|
65
115
|
private
|
66
116
|
|
67
117
|
def call
|
@@ -69,7 +119,7 @@ describe Committee::ResponseValidator do
|
|
69
119
|
@data,
|
70
120
|
@schema,
|
71
121
|
@link_schema,
|
72
|
-
@
|
122
|
+
@type_schema
|
73
123
|
).call
|
74
124
|
end
|
75
125
|
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
require_relative "../test_helper"
|
2
|
+
|
3
|
+
describe Committee::Middleware::Stub do
|
4
|
+
include Committee::Test::Methods
|
5
|
+
include Rack::Test::Methods
|
6
|
+
|
7
|
+
def app
|
8
|
+
@app
|
9
|
+
end
|
10
|
+
|
11
|
+
def schema_path
|
12
|
+
"./test/data/schema.json"
|
13
|
+
end
|
14
|
+
|
15
|
+
describe "#assert_schema_content_type" do
|
16
|
+
it "passes through a valid response" do
|
17
|
+
@app = new_rack_app(MultiJson.encode([ValidApp]))
|
18
|
+
get "/apps"
|
19
|
+
assert_schema_content_type
|
20
|
+
end
|
21
|
+
|
22
|
+
it "detects an invalid response Content-Type" do
|
23
|
+
@app = new_rack_app(MultiJson.encode([ValidApp]), {})
|
24
|
+
get "/apps"
|
25
|
+
e = assert_raises(Committee::InvalidResponse) do
|
26
|
+
assert_schema_content_type
|
27
|
+
end
|
28
|
+
assert_match /response header must be set to/i, e.message
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
describe "#assert_schema_conform" do
|
33
|
+
it "passes through a valid response" do
|
34
|
+
@app = new_rack_app(MultiJson.encode([ValidApp]))
|
35
|
+
get "/apps"
|
36
|
+
assert_schema_conform
|
37
|
+
end
|
38
|
+
|
39
|
+
it "detects an invalid response Content-Type" do
|
40
|
+
@app = new_rack_app(MultiJson.encode([ValidApp]), {})
|
41
|
+
get "/apps"
|
42
|
+
e = assert_raises(Committee::InvalidResponse) do
|
43
|
+
assert_schema_conform
|
44
|
+
end
|
45
|
+
assert_match /response header must be set to/i, e.message
|
46
|
+
end
|
47
|
+
|
48
|
+
it "detects missing keys in response" do
|
49
|
+
data = ValidApp.dup
|
50
|
+
data.delete("name")
|
51
|
+
@app = new_rack_app(MultiJson.encode([data]))
|
52
|
+
get "/apps"
|
53
|
+
e = assert_raises(Committee::InvalidResponse) do
|
54
|
+
assert_schema_conform
|
55
|
+
end
|
56
|
+
assert_match /missing keys/i, e.message
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
private
|
61
|
+
|
62
|
+
def new_rack_app(response, headers={ "Content-Type" => "application/json" })
|
63
|
+
Rack::Builder.new {
|
64
|
+
run lambda { |_|
|
65
|
+
[200, headers, [response]]
|
66
|
+
}
|
67
|
+
}
|
68
|
+
end
|
69
|
+
end
|
data/test/test_helper.rb
CHANGED
@@ -34,3 +34,15 @@ ValidApp = {
|
|
34
34
|
"updated_at" => "2012-01-01T12:00:00Z",
|
35
35
|
"web_url" => "http://example.herokuapp.com"
|
36
36
|
}.freeze
|
37
|
+
|
38
|
+
ValidAccount = {
|
39
|
+
"allow_tracking" => true,
|
40
|
+
"beta" => true,
|
41
|
+
"created_at" => "2012-01-01T12:00:00Z",
|
42
|
+
"email" => "username@example.com",
|
43
|
+
"id" => "01234567-89ab-cdef-0123-456789abcdef",
|
44
|
+
"last_login" => "2012-01-01T12:00:00Z",
|
45
|
+
"updated_at" => "2012-01-01T12:00:00Z",
|
46
|
+
"verified" => true,
|
47
|
+
"flags" => ["foo", "bar"]
|
48
|
+
}.freeze
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: committee
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.4.
|
4
|
+
version: 0.4.3
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -10,7 +10,7 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
date: 2014-
|
13
|
+
date: 2014-04-20 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: multi_json
|
@@ -112,6 +112,8 @@ files:
|
|
112
112
|
- lib/committee/response_validator.rb
|
113
113
|
- lib/committee/router.rb
|
114
114
|
- lib/committee/schema.rb
|
115
|
+
- lib/committee/test/methods.rb
|
116
|
+
- lib/committee/validation.rb
|
115
117
|
- lib/committee.rb
|
116
118
|
- test/middleware/request_validation_test.rb
|
117
119
|
- test/middleware/response_validation_test.rb
|
@@ -122,6 +124,7 @@ files:
|
|
122
124
|
- test/response_validator_test.rb
|
123
125
|
- test/router_test.rb
|
124
126
|
- test/schema_test.rb
|
127
|
+
- test/test/methods_test.rb
|
125
128
|
- test/test_helper.rb
|
126
129
|
- bin/committee-stub
|
127
130
|
homepage: https://github.com/heroku/rack-committee
|