json_matchers 0.9.0 → 0.10.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/.travis.yml +3 -1
- data/NEWS.md +9 -0
- data/README.md +104 -81
- data/json_matchers.gemspec +1 -1
- data/lib/json_matchers.rb +2 -2
- data/lib/json_matchers/assertion.rb +5 -5
- data/lib/json_matchers/matcher.rb +17 -20
- data/lib/json_matchers/parser.rb +21 -0
- data/lib/json_matchers/payload.rb +4 -0
- data/lib/json_matchers/rspec.rb +2 -14
- data/lib/json_matchers/validator.rb +17 -20
- data/lib/json_matchers/version.rb +1 -1
- data/spec/factories.rb +53 -17
- data/spec/json_matchers/match_json_schema_spec.rb +53 -77
- data/spec/support/fake_response.rb +1 -1
- data/spec/support/file_helpers.rb +0 -1
- data/test/json_matchers/minitest/assertions_test.rb +55 -29
- metadata +9 -13
- data/lib/json_matchers/configuration.rb +0 -27
- data/spec/json_matchers/configuration_spec.rb +0 -9
- data/spec/support/configuration.rb +0 -20
data/lib/json_matchers/rspec.rb
CHANGED
@@ -5,20 +5,8 @@ module JsonMatchers
|
|
5
5
|
self.schema_root = File.join("spec", "support", "api", "schemas")
|
6
6
|
end
|
7
7
|
|
8
|
-
RSpec::Matchers.define :match_json_schema do |schema_name
|
9
|
-
|
10
|
-
warn <<-WARN
|
11
|
-
DEPRECATION:
|
12
|
-
|
13
|
-
After `json_matchers@0.9.x`, calls to `match_json_schema` and
|
14
|
-
`match_response_schema` will no longer accept options.
|
15
|
-
|
16
|
-
See https://github.com/thoughtbot/json_matchers/pull/31 for more information.
|
17
|
-
|
18
|
-
WARN
|
19
|
-
end
|
20
|
-
|
21
|
-
assertion = JsonMatchers::Assertion.new(schema_name.to_s, options)
|
8
|
+
RSpec::Matchers.define :match_json_schema do |schema_name|
|
9
|
+
assertion = JsonMatchers::Assertion.new(schema_name.to_s)
|
22
10
|
|
23
11
|
match do |json|
|
24
12
|
assertion.valid?(json)
|
@@ -1,37 +1,34 @@
|
|
1
|
-
require "
|
1
|
+
require "json_matchers/parser"
|
2
2
|
|
3
3
|
module JsonMatchers
|
4
4
|
class Validator
|
5
|
-
def initialize(
|
6
|
-
@
|
7
|
-
@
|
8
|
-
@schema_path = schema_path.to_s
|
5
|
+
def initialize(document_store:, schema_path:)
|
6
|
+
@document_store = document_store
|
7
|
+
@schema_path = schema_path
|
9
8
|
end
|
10
9
|
|
11
|
-
def validate
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
10
|
+
def validate(payload)
|
11
|
+
json_schema.validate!(payload.as_json, fail_fast: true)
|
12
|
+
|
13
|
+
[]
|
14
|
+
rescue JsonSchema::Error => error
|
15
|
+
[error.message]
|
17
16
|
end
|
18
17
|
|
19
18
|
private
|
20
19
|
|
21
|
-
attr_reader :
|
20
|
+
attr_reader :document_store, :schema_path
|
22
21
|
|
23
|
-
def
|
24
|
-
|
22
|
+
def json_schema
|
23
|
+
@json_schema ||= build_json_schema_with_expanded_references
|
25
24
|
end
|
26
25
|
|
27
|
-
def
|
28
|
-
|
29
|
-
end
|
26
|
+
def build_json_schema_with_expanded_references
|
27
|
+
json_schema = Parser.new(schema_path).parse
|
30
28
|
|
31
|
-
|
32
|
-
JSON::Validator.validate!(schema_path, payload, options)
|
29
|
+
json_schema.expand_references!(store: document_store)
|
33
30
|
|
34
|
-
|
31
|
+
json_schema
|
35
32
|
end
|
36
33
|
end
|
37
34
|
end
|
data/spec/factories.rb
CHANGED
@@ -6,12 +6,12 @@ FactoryBot.define do
|
|
6
6
|
factory :response, class: FakeResponse do
|
7
7
|
skip_create
|
8
8
|
|
9
|
-
trait :
|
10
|
-
body { { "
|
9
|
+
trait :location do
|
10
|
+
body { { "latitude": 1, "longitude": 1 } }
|
11
11
|
end
|
12
12
|
|
13
|
-
trait :
|
14
|
-
body { { "
|
13
|
+
trait :invalid_location do
|
14
|
+
body { { "latitude": "1", "longitude": "1" } }
|
15
15
|
end
|
16
16
|
|
17
17
|
initialize_with do
|
@@ -24,29 +24,32 @@ FactoryBot.define do
|
|
24
24
|
end
|
25
25
|
|
26
26
|
factory :schema, class: FakeSchema do
|
27
|
-
skip_create
|
28
|
-
|
29
27
|
sequence(:name) { |n| "json_schema-#{n}" }
|
30
28
|
|
31
29
|
trait :invalid do
|
32
30
|
json { "" }
|
33
31
|
end
|
34
32
|
|
35
|
-
trait :
|
33
|
+
trait :location do
|
36
34
|
json do
|
37
35
|
{
|
36
|
+
"id": "file:/#{name}.json#",
|
37
|
+
"description": "An object containing some #{name} data",
|
38
38
|
"type": "object",
|
39
|
-
"required": [
|
40
|
-
"id",
|
41
|
-
],
|
39
|
+
"required": ["latitude", "longitude"],
|
42
40
|
"properties": {
|
43
|
-
"
|
41
|
+
"latitude": {
|
42
|
+
"type": "number",
|
43
|
+
},
|
44
|
+
"longitude": {
|
45
|
+
"type": "number",
|
46
|
+
},
|
44
47
|
},
|
45
48
|
"additionalProperties": false,
|
46
49
|
}
|
47
50
|
end
|
48
51
|
end
|
49
|
-
trait(:
|
52
|
+
trait(:locations) { location }
|
50
53
|
|
51
54
|
trait :array_of do
|
52
55
|
initialize_with do
|
@@ -60,17 +63,49 @@ FactoryBot.define do
|
|
60
63
|
end
|
61
64
|
end
|
62
65
|
|
63
|
-
trait :
|
64
|
-
association :items, factory: [:schema, :
|
66
|
+
trait :referencing_locations do
|
67
|
+
association :items, factory: [:schema, :location]
|
65
68
|
|
66
69
|
initialize_with do
|
67
70
|
FakeSchema.new(name, {
|
71
|
+
"$schema": "https://json-schema.org/draft-04/schema#",
|
68
72
|
"type": "array",
|
69
|
-
"items": { "$ref": "
|
73
|
+
"items": { "$ref": "file:/#{items.name}.json#" },
|
70
74
|
})
|
71
75
|
end
|
72
76
|
end
|
73
77
|
|
78
|
+
trait :referencing_definitions do
|
79
|
+
association :items, factory: [:schema, :location], name: "location"
|
80
|
+
association :example, factory: [:response, :location]
|
81
|
+
|
82
|
+
transient do
|
83
|
+
plural { items.name.pluralize }
|
84
|
+
end
|
85
|
+
|
86
|
+
json do
|
87
|
+
{
|
88
|
+
"$schema": "https://json-schema.org/draft-04/schema#",
|
89
|
+
"id": "file:/#{name}.json#",
|
90
|
+
"type": "object",
|
91
|
+
"definitions": {
|
92
|
+
plural => {
|
93
|
+
"type": "array",
|
94
|
+
"items": { "$ref": "file:/#{items.name}.json#" },
|
95
|
+
"description": "A collection of #{plural}",
|
96
|
+
"example": example,
|
97
|
+
},
|
98
|
+
},
|
99
|
+
"required": [plural],
|
100
|
+
"properties": {
|
101
|
+
plural => {
|
102
|
+
"$ref": "#/definitions/#{plural}",
|
103
|
+
},
|
104
|
+
},
|
105
|
+
}
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
74
109
|
initialize_with do
|
75
110
|
schema_body_as_json = attributes.fetch(:json, nil)
|
76
111
|
schema_body = attributes.except(:json, :name)
|
@@ -78,10 +113,11 @@ FactoryBot.define do
|
|
78
113
|
FakeSchema.new(name, schema_body_as_json || schema_body)
|
79
114
|
end
|
80
115
|
|
81
|
-
|
82
|
-
path =
|
116
|
+
to_create do |schema|
|
117
|
+
path = JsonMatchers.path_to_schema(schema.name)
|
83
118
|
payload = JsonMatchers::Payload.new(schema.json)
|
84
119
|
|
120
|
+
path.dirname.mkpath
|
85
121
|
IO.write(path, payload)
|
86
122
|
end
|
87
123
|
end
|
@@ -20,51 +20,59 @@ describe JsonMatchers, "#match_json_schema" do
|
|
20
20
|
end
|
21
21
|
|
22
22
|
it "supports asserting with the match_response_schema alias" do
|
23
|
-
schema = create(:schema, :
|
23
|
+
schema = create(:schema, :location)
|
24
24
|
|
25
|
-
json = build(:response, :
|
25
|
+
json = build(:response, :invalid_location)
|
26
26
|
|
27
27
|
expect(json).not_to match_response_schema(schema)
|
28
28
|
end
|
29
29
|
|
30
30
|
it "supports refuting with the match_response_schema alias" do
|
31
|
-
schema = create(:schema, :
|
31
|
+
schema = create(:schema, :location)
|
32
32
|
|
33
|
-
json = build(:response, :
|
33
|
+
json = build(:response, :invalid_location)
|
34
34
|
|
35
35
|
expect(json).not_to match_response_schema(schema)
|
36
36
|
end
|
37
37
|
|
38
38
|
it "fails when the body contains a property with the wrong type" do
|
39
|
-
schema = create(:schema, :
|
39
|
+
schema = create(:schema, :location)
|
40
40
|
|
41
|
-
json = build(:response, :
|
41
|
+
json = build(:response, :invalid_location)
|
42
42
|
|
43
43
|
expect(json).not_to match_json_schema(schema)
|
44
44
|
end
|
45
45
|
|
46
46
|
it "fails when the body is missing a required property" do
|
47
|
-
schema = create(:schema, :
|
47
|
+
schema = create(:schema, :location)
|
48
48
|
|
49
49
|
json = build(:response, {})
|
50
50
|
|
51
51
|
expect(json).not_to match_json_schema(schema)
|
52
52
|
end
|
53
53
|
|
54
|
+
it "can reference a schema in a directory" do
|
55
|
+
create(:schema, :location, name: "api/v1/schema")
|
56
|
+
|
57
|
+
json = build(:response, :location)
|
58
|
+
|
59
|
+
expect(json).to match_json_schema("api/v1/schema")
|
60
|
+
end
|
61
|
+
|
54
62
|
context "when passed a Hash" do
|
55
63
|
it "validates that the schema matches" do
|
56
|
-
schema = create(:schema, :
|
64
|
+
schema = create(:schema, :location)
|
57
65
|
|
58
|
-
json = build(:response, :
|
66
|
+
json = build(:response, :location)
|
59
67
|
json_as_hash = json.to_h
|
60
68
|
|
61
69
|
expect(json_as_hash).to match_json_schema(schema)
|
62
70
|
end
|
63
71
|
|
64
72
|
it "fails with message when negated" do
|
65
|
-
schema = create(:schema, :
|
73
|
+
schema = create(:schema, :location)
|
66
74
|
|
67
|
-
json = build(:response, :
|
75
|
+
json = build(:response, :invalid_location)
|
68
76
|
json_as_hash = json.to_h
|
69
77
|
|
70
78
|
expect {
|
@@ -75,27 +83,27 @@ describe JsonMatchers, "#match_json_schema" do
|
|
75
83
|
|
76
84
|
context "when passed a Array" do
|
77
85
|
it "validates a root-level Array in the JSON" do
|
78
|
-
schema = create(:schema, :array_of, :
|
86
|
+
schema = create(:schema, :array_of, :locations)
|
79
87
|
|
80
|
-
json = build(:response, :
|
88
|
+
json = build(:response, :location)
|
81
89
|
json_as_array = [json.to_h]
|
82
90
|
|
83
91
|
expect(json_as_array).to match_json_schema(schema)
|
84
92
|
end
|
85
93
|
|
86
94
|
it "refutes a root-level Array in the JSON" do
|
87
|
-
schema = create(:schema, :array_of, :
|
95
|
+
schema = create(:schema, :array_of, :locations)
|
88
96
|
|
89
|
-
json = build(:response, :
|
97
|
+
json = build(:response, :invalid_location)
|
90
98
|
json_as_array = [json.to_h]
|
91
99
|
|
92
100
|
expect(json_as_array).not_to match_json_schema(schema)
|
93
101
|
end
|
94
102
|
|
95
103
|
it "fails with message when negated" do
|
96
|
-
schema = create(:schema, :array_of, :
|
104
|
+
schema = create(:schema, :array_of, :location)
|
97
105
|
|
98
|
-
json = build(:response, :
|
106
|
+
json = build(:response, :invalid_location)
|
99
107
|
json_as_array = [json.to_h]
|
100
108
|
|
101
109
|
expect {
|
@@ -106,18 +114,18 @@ describe JsonMatchers, "#match_json_schema" do
|
|
106
114
|
|
107
115
|
context "when JSON is a string" do
|
108
116
|
it "validates that the schema matches" do
|
109
|
-
schema = create(:schema, :
|
117
|
+
schema = create(:schema, :location)
|
110
118
|
|
111
|
-
json = build(:response, :
|
119
|
+
json = build(:response, :location)
|
112
120
|
json_as_string = json.to_json
|
113
121
|
|
114
122
|
expect(json_as_string).to match_json_schema(schema)
|
115
123
|
end
|
116
124
|
|
117
125
|
it "fails with message when negated" do
|
118
|
-
schema = create(:schema, :
|
126
|
+
schema = create(:schema, :location)
|
119
127
|
|
120
|
-
json = build(:response, :
|
128
|
+
json = build(:response, :invalid_location)
|
121
129
|
json_as_string = json.to_json
|
122
130
|
|
123
131
|
expect {
|
@@ -127,18 +135,18 @@ describe JsonMatchers, "#match_json_schema" do
|
|
127
135
|
end
|
128
136
|
|
129
137
|
it "fails when the body contains a property with the wrong type" do
|
130
|
-
schema = create(:schema, :
|
138
|
+
schema = create(:schema, :location)
|
131
139
|
|
132
|
-
json = build(:response, :
|
140
|
+
json = build(:response, :invalid_location)
|
133
141
|
|
134
142
|
expect(json).not_to match_json_schema(schema)
|
135
143
|
end
|
136
144
|
|
137
145
|
describe "the failure message" do
|
138
146
|
it "contains the body" do
|
139
|
-
schema = create(:schema, :
|
147
|
+
schema = create(:schema, :location)
|
140
148
|
|
141
|
-
json = build(:response, :
|
149
|
+
json = build(:response, :invalid_location)
|
142
150
|
|
143
151
|
expect {
|
144
152
|
expect(json).to match_json_schema(schema)
|
@@ -146,9 +154,9 @@ describe JsonMatchers, "#match_json_schema" do
|
|
146
154
|
end
|
147
155
|
|
148
156
|
it "contains the schema" do
|
149
|
-
schema = create(:schema, :
|
157
|
+
schema = create(:schema, :location)
|
150
158
|
|
151
|
-
json = build(:response, :
|
159
|
+
json = build(:response, :invalid_location)
|
152
160
|
|
153
161
|
expect {
|
154
162
|
expect(json).to match_json_schema(schema)
|
@@ -156,9 +164,9 @@ describe JsonMatchers, "#match_json_schema" do
|
|
156
164
|
end
|
157
165
|
|
158
166
|
it "when negated, contains the body" do
|
159
|
-
schema = create(:schema, :
|
167
|
+
schema = create(:schema, :location)
|
160
168
|
|
161
|
-
json = build(:response, :
|
169
|
+
json = build(:response, :location)
|
162
170
|
|
163
171
|
expect {
|
164
172
|
expect(json).not_to match_json_schema(schema)
|
@@ -166,9 +174,9 @@ describe JsonMatchers, "#match_json_schema" do
|
|
166
174
|
end
|
167
175
|
|
168
176
|
it "when negated, contains the schema" do
|
169
|
-
schema = create(:schema, :
|
177
|
+
schema = create(:schema, :location)
|
170
178
|
|
171
|
-
json = build(:response, :
|
179
|
+
json = build(:response, :location)
|
172
180
|
|
173
181
|
expect {
|
174
182
|
expect(json).not_to match_json_schema(schema)
|
@@ -177,71 +185,39 @@ describe JsonMatchers, "#match_json_schema" do
|
|
177
185
|
end
|
178
186
|
|
179
187
|
it "validates against a schema that uses $ref" do
|
180
|
-
schema = create(:schema, :
|
188
|
+
schema = create(:schema, :referencing_locations)
|
181
189
|
|
182
|
-
json = build(:response, :
|
190
|
+
json = build(:response, :location)
|
183
191
|
json_as_array = [json.to_h]
|
184
192
|
|
185
193
|
expect(json_as_array).to match_json_schema(schema)
|
186
194
|
end
|
187
195
|
|
188
196
|
it "fails against a schema that uses $ref" do
|
189
|
-
schema = create(:schema, :
|
197
|
+
schema = create(:schema, :referencing_locations)
|
190
198
|
|
191
|
-
json = build(:response, :
|
199
|
+
json = build(:response, :invalid_location)
|
192
200
|
json_as_array = [json.to_h]
|
193
201
|
|
194
202
|
expect(json_as_array).not_to match_json_schema(schema)
|
195
203
|
end
|
196
204
|
|
197
|
-
|
198
|
-
|
199
|
-
schema = create(:schema, :object)
|
205
|
+
it "validates against a schema referencing with 'definitions'" do
|
206
|
+
schema = create(:schema, :referencing_definitions)
|
200
207
|
|
201
|
-
|
202
|
-
|
208
|
+
json = build(:response, :location)
|
209
|
+
json_as_hash = { "locations" => [json] }
|
203
210
|
|
204
|
-
|
205
|
-
expect(invalid_json).not_to match_json_schema(schema, strict: true)
|
206
|
-
end
|
211
|
+
expect(json_as_hash).to match_json_schema(schema)
|
207
212
|
end
|
208
213
|
|
209
|
-
|
210
|
-
|
211
|
-
with_options(strict: true) do
|
212
|
-
schema = create(:schema, :object)
|
213
|
-
|
214
|
-
matching_json = build(:response, :object)
|
215
|
-
invalid_json = build(:response, { "id": 1, "title": "bar" })
|
214
|
+
it "fails against a schema referencing with 'definitions'" do
|
215
|
+
schema = create(:schema, :referencing_definitions)
|
216
216
|
|
217
|
-
|
218
|
-
|
219
|
-
end
|
220
|
-
end
|
217
|
+
json = build(:response, :invalid_location)
|
218
|
+
json_as_hash = { "locations" => [json] }
|
221
219
|
|
222
|
-
|
223
|
-
it "includes the reasons for failure in the exception's message" do
|
224
|
-
with_options(record_errors: true) do
|
225
|
-
schema = create(:schema, {
|
226
|
-
"type": "object",
|
227
|
-
"properties": {
|
228
|
-
"username": {
|
229
|
-
"allOf": [
|
230
|
-
{ "type": "string" },
|
231
|
-
{ "minLength": 5 },
|
232
|
-
],
|
233
|
-
},
|
234
|
-
},
|
235
|
-
})
|
236
|
-
|
237
|
-
invalid_json = build(:response, { "username": "foo" })
|
238
|
-
|
239
|
-
expect {
|
240
|
-
expect(invalid_json).to match_json_schema(schema)
|
241
|
-
}.to raise_error(/minimum/)
|
242
|
-
end
|
243
|
-
end
|
244
|
-
end
|
220
|
+
expect(json_as_hash).not_to match_json_schema(schema)
|
245
221
|
end
|
246
222
|
|
247
223
|
def raise_error_containing(schema_or_body)
|