json_schema 0.0.7 → 0.0.9
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.
- data/README.md +9 -0
- data/bin/validate-schema +37 -0
- data/lib/commands/validate_schema.rb +102 -0
- data/lib/json_reference.rb +18 -2
- data/lib/json_schema.rb +1 -0
- data/lib/json_schema/document_store.rb +48 -0
- data/lib/json_schema/parser.rb +36 -14
- data/lib/json_schema/reference_expander.rb +128 -77
- data/lib/json_schema/schema.rb +140 -8
- data/lib/json_schema/validator.rb +22 -7
- data/schemas/hyper-schema.json +168 -0
- data/schemas/schema.json +150 -0
- data/test/commands/validate_schema_test.rb +103 -0
- data/test/json_reference/reference_test.rb +45 -0
- data/test/json_schema/parser_test.rb +18 -1
- data/test/json_schema/reference_expander_test.rb +81 -39
- data/test/json_schema/validator_test.rb +60 -4
- metadata +11 -3
@@ -0,0 +1,103 @@
|
|
1
|
+
require "test_helper"
|
2
|
+
|
3
|
+
require "commands/validate_schema"
|
4
|
+
require "tempfile"
|
5
|
+
|
6
|
+
describe Commands::ValidateSchema do
|
7
|
+
before do
|
8
|
+
@command = Commands::ValidateSchema.new
|
9
|
+
end
|
10
|
+
|
11
|
+
it "shows usage with no arguments" do
|
12
|
+
success = @command.run([])
|
13
|
+
assert_equal [], @command.errors
|
14
|
+
assert_equal [], @command.messages
|
15
|
+
refute success
|
16
|
+
end
|
17
|
+
|
18
|
+
it "runs successfully in detect mode" do
|
19
|
+
temp_file(basic_schema) do |path|
|
20
|
+
@command.extra_schemas << schema_path
|
21
|
+
@command.detect = true
|
22
|
+
success = @command.run([path])
|
23
|
+
assert_equal [], @command.errors
|
24
|
+
assert_equal ["#{path} is valid."], @command.messages
|
25
|
+
assert success
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
it "runs successfully out of detect mode" do
|
30
|
+
temp_file(basic_schema) do |path|
|
31
|
+
@command.detect = false
|
32
|
+
success = @command.run([schema_path, path])
|
33
|
+
assert_equal [], @command.errors
|
34
|
+
assert_equal ["#{path} is valid."], @command.messages
|
35
|
+
assert success
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
it "takes extra schemas" do
|
40
|
+
temp_file(basic_hyper_schema) do |path|
|
41
|
+
@command.detect = false
|
42
|
+
@command.extra_schemas << schema_path
|
43
|
+
success = @command.run([hyper_schema_path, path])
|
44
|
+
assert_equal [], @command.errors
|
45
|
+
assert_equal ["#{path} is valid."], @command.messages
|
46
|
+
assert success
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
it "requires at least one argument in detect mode" do
|
51
|
+
@command.detect = true
|
52
|
+
success = @command.run([])
|
53
|
+
assert_equal [], @command.errors
|
54
|
+
assert_equal [], @command.messages
|
55
|
+
refute success
|
56
|
+
end
|
57
|
+
|
58
|
+
it "requires at least two arguments out of detect mode" do
|
59
|
+
@command.detect = false
|
60
|
+
success = @command.run([hyper_schema_path])
|
61
|
+
assert_equal [], @command.errors
|
62
|
+
assert_equal [], @command.messages
|
63
|
+
refute success
|
64
|
+
end
|
65
|
+
|
66
|
+
it "errors on invalid files" do
|
67
|
+
@command.detect = false
|
68
|
+
success = @command.run(["dne-1", "dne-2"])
|
69
|
+
assert_equal ["dne-1: No such file or directory."], @command.errors
|
70
|
+
assert_equal [], @command.messages
|
71
|
+
refute success
|
72
|
+
end
|
73
|
+
|
74
|
+
def basic_hyper_schema
|
75
|
+
<<-eos
|
76
|
+
{ "$schema": "http://json-schema.org/draft-04/hyper-schema" }
|
77
|
+
eos
|
78
|
+
end
|
79
|
+
|
80
|
+
def basic_schema
|
81
|
+
<<-eos
|
82
|
+
{ "$schema": "http://json-schema.org/draft-04/schema" }
|
83
|
+
eos
|
84
|
+
end
|
85
|
+
|
86
|
+
def hyper_schema_path
|
87
|
+
File.expand_path("schema.json", "#{__FILE__}/../../../schemas")
|
88
|
+
end
|
89
|
+
|
90
|
+
def schema_path
|
91
|
+
File.expand_path("schema.json", "#{__FILE__}/../../../schemas")
|
92
|
+
end
|
93
|
+
|
94
|
+
def temp_file(contents)
|
95
|
+
file = Tempfile.new("schema")
|
96
|
+
file.write(contents)
|
97
|
+
file.size() # flush
|
98
|
+
yield(file.path)
|
99
|
+
ensure
|
100
|
+
file.close
|
101
|
+
file.unlink
|
102
|
+
end
|
103
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
require "test_helper"
|
2
|
+
|
3
|
+
require "json_reference"
|
4
|
+
|
5
|
+
describe JsonReference::Reference do
|
6
|
+
it "expands a reference without a URI" do
|
7
|
+
ref = reference("#/definitions")
|
8
|
+
assert_equal nil, ref.uri
|
9
|
+
assert_equal "#/definitions", ref.pointer
|
10
|
+
end
|
11
|
+
|
12
|
+
it "expands a reference with a URI" do
|
13
|
+
ref = reference("http://example.com#/definitions")
|
14
|
+
assert_equal URI.parse("http://example.com"), ref.uri
|
15
|
+
assert_equal "#/definitions", ref.pointer
|
16
|
+
end
|
17
|
+
|
18
|
+
it "expands just a root sign" do
|
19
|
+
ref = reference("#")
|
20
|
+
assert_equal nil, ref.uri
|
21
|
+
assert_equal "#", ref.pointer
|
22
|
+
end
|
23
|
+
|
24
|
+
it "expands a URI with just a root sign" do
|
25
|
+
ref = reference("http://example.com#")
|
26
|
+
assert_equal URI.parse("http://example.com"), ref.uri
|
27
|
+
assert_equal "#", ref.pointer
|
28
|
+
end
|
29
|
+
|
30
|
+
it "normalizes pointers by adding a root sign prefix" do
|
31
|
+
ref = reference("/definitions")
|
32
|
+
assert_equal nil, ref.uri
|
33
|
+
assert_equal "#/definitions", ref.pointer
|
34
|
+
end
|
35
|
+
|
36
|
+
it "normalizes pointers by stripping a trailing slash" do
|
37
|
+
ref = reference("#/definitions/")
|
38
|
+
assert_equal nil, ref.uri
|
39
|
+
assert_equal "#/definitions", ref.pointer
|
40
|
+
end
|
41
|
+
|
42
|
+
def reference(str)
|
43
|
+
JsonReference::Reference.new(str)
|
44
|
+
end
|
45
|
+
end
|
@@ -86,12 +86,29 @@ describe JsonSchema::Parser do
|
|
86
86
|
|
87
87
|
it "parses the basic set of object validations" do
|
88
88
|
schema = parse.definitions["app"]
|
89
|
-
assert_equal false, schema.additional_properties
|
90
89
|
assert_equal 10, schema.max_properties
|
91
90
|
assert_equal 1, schema.min_properties
|
92
91
|
assert_equal ["name"], schema.required
|
93
92
|
end
|
94
93
|
|
94
|
+
it "parses the additionalProperties object validation as boolean" do
|
95
|
+
pointer("#/definitions/app").merge!(
|
96
|
+
"additionalProperties" => false
|
97
|
+
)
|
98
|
+
schema = parse.definitions["app"]
|
99
|
+
assert_equal false, schema.additional_properties
|
100
|
+
end
|
101
|
+
|
102
|
+
it "parses the additionalProperties object validation as schema" do
|
103
|
+
pointer("#/definitions/app").merge!(
|
104
|
+
"additionalProperties" => {
|
105
|
+
"type" => "boolean"
|
106
|
+
}
|
107
|
+
)
|
108
|
+
schema = parse.definitions["app"].additional_properties
|
109
|
+
assert_equal ["boolean"], schema.type
|
110
|
+
end
|
111
|
+
|
95
112
|
it "parses the dependencies object validation" do
|
96
113
|
schema = parse.definitions["app"]
|
97
114
|
assert_equal ["ssl"], schema.dependencies["production"]
|
@@ -4,36 +4,46 @@ require "json_schema"
|
|
4
4
|
|
5
5
|
describe JsonSchema::ReferenceExpander do
|
6
6
|
it "expands references" do
|
7
|
-
|
7
|
+
expand
|
8
|
+
assert_equal [], errors
|
8
9
|
|
9
10
|
# this was always a fully-defined property
|
10
11
|
referenced = @schema.definitions["app"]
|
11
12
|
# this used to be a $ref
|
12
13
|
reference = @schema.properties["app"]
|
13
14
|
|
14
|
-
|
15
|
+
assert_equal "#/definitions/app", reference.reference.pointer
|
15
16
|
assert_equal referenced.description, reference.description
|
16
17
|
assert_equal referenced.id, reference.id
|
17
18
|
assert_equal referenced.type, reference.type
|
18
19
|
assert_equal referenced.uri, reference.uri
|
19
20
|
end
|
20
21
|
|
22
|
+
it "takes a document store" do
|
23
|
+
store = JsonSchema::DocumentStore.new
|
24
|
+
expand(store: store)
|
25
|
+
assert store.lookup_uri("/")
|
26
|
+
end
|
27
|
+
|
21
28
|
it "will expand anyOf" do
|
22
|
-
|
29
|
+
expand
|
30
|
+
assert_equal [], errors
|
23
31
|
schema = @schema.properties["app"].definitions["contrived_plus"]
|
24
32
|
assert_equal 3, schema.any_of[0].min_length
|
25
33
|
assert_equal 5, schema.any_of[1].min_length
|
26
34
|
end
|
27
35
|
|
28
36
|
it "will expand allOf" do
|
29
|
-
|
37
|
+
expand
|
38
|
+
assert_equal [], errors
|
30
39
|
schema = @schema.properties["app"].definitions["contrived_plus"]
|
31
40
|
assert_equal 30, schema.all_of[0].max_length
|
32
41
|
assert_equal 3, schema.all_of[1].min_length
|
33
42
|
end
|
34
43
|
|
35
44
|
it "will expand dependencies" do
|
36
|
-
|
45
|
+
expand
|
46
|
+
assert_equal [], errors
|
37
47
|
schema = @schema.properties["app"].dependencies["ssl"].properties["name"]
|
38
48
|
assert_equal ["string"], schema.type
|
39
49
|
end
|
@@ -44,7 +54,8 @@ describe JsonSchema::ReferenceExpander do
|
|
44
54
|
"$ref" => "#/definitions/app/definitions/name"
|
45
55
|
}
|
46
56
|
)
|
47
|
-
|
57
|
+
expand
|
58
|
+
assert_equal [], errors
|
48
59
|
schema = @schema.properties["app"].properties["flags"].items
|
49
60
|
assert_equal ["string"], schema.type
|
50
61
|
end
|
@@ -56,7 +67,8 @@ describe JsonSchema::ReferenceExpander do
|
|
56
67
|
{ "$ref" => "#/definitions/app/definitions/owner" }
|
57
68
|
]
|
58
69
|
)
|
59
|
-
|
70
|
+
expand
|
71
|
+
assert_equal [], errors
|
60
72
|
schema0 = @schema.properties["app"].properties["flags"].items[0]
|
61
73
|
schema1 = @schema.properties["app"].properties["flags"].items[0]
|
62
74
|
assert_equal ["string"], schema0.type
|
@@ -64,20 +76,33 @@ describe JsonSchema::ReferenceExpander do
|
|
64
76
|
end
|
65
77
|
|
66
78
|
it "will expand oneOf" do
|
67
|
-
|
79
|
+
expand
|
80
|
+
assert_equal [], errors
|
68
81
|
schema = @schema.properties["app"].definitions["contrived_plus"]
|
69
82
|
assert_equal /^(foo|aaa)$/, schema.one_of[0].pattern
|
70
83
|
assert_equal /^(foo|zzz)$/, schema.one_of[1].pattern
|
71
84
|
end
|
72
85
|
|
73
86
|
it "will expand not" do
|
74
|
-
|
87
|
+
expand
|
88
|
+
assert_equal [], errors
|
75
89
|
schema = @schema.properties["app"].definitions["contrived_plus"]
|
76
90
|
assert_equal /^$/, schema.not.pattern
|
77
91
|
end
|
78
92
|
|
93
|
+
it "will expand additionalProperties" do
|
94
|
+
pointer("#").merge!(
|
95
|
+
"additionalProperties" => { "$ref" => "#" }
|
96
|
+
)
|
97
|
+
expand
|
98
|
+
assert_equal [], errors
|
99
|
+
schema = @schema.additional_properties
|
100
|
+
assert_equal ["object"], schema.type
|
101
|
+
end
|
102
|
+
|
79
103
|
it "will expand patternProperties" do
|
80
|
-
|
104
|
+
expand
|
105
|
+
assert_equal [], errors
|
81
106
|
# value ([1]) of the #first tuple in hash
|
82
107
|
schema = @schema.properties["app"].definitions["roles"].
|
83
108
|
pattern_properties.first[1]
|
@@ -85,51 +110,68 @@ describe JsonSchema::ReferenceExpander do
|
|
85
110
|
end
|
86
111
|
|
87
112
|
it "will expand hyperschema link schemas" do
|
88
|
-
|
113
|
+
expand
|
114
|
+
assert_equal [], errors
|
89
115
|
schema = @schema.properties["app"].links[0].schema.properties["name"]
|
90
116
|
assert_equal ["string"], schema.type
|
91
117
|
end
|
92
118
|
|
93
119
|
it "will perform multiple passes to resolve all references" do
|
94
|
-
|
95
|
-
"
|
96
|
-
|
97
|
-
},
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
120
|
+
pointer("#/properties").merge!(
|
121
|
+
"app0" => { "$ref" => "#/properties/app1" },
|
122
|
+
"app1" => { "$ref" => "#/properties/app2" },
|
123
|
+
"app2" => { "$ref" => "#/definitions/app" },
|
124
|
+
)
|
125
|
+
expand
|
126
|
+
assert_equal [], errors
|
127
|
+
schema = @schema.properties["app0"]
|
128
|
+
assert_equal ["object"], schema.type
|
129
|
+
end
|
130
|
+
|
131
|
+
it "will resolve circular dependencies" do
|
132
|
+
pointer("#/properties").merge!(
|
133
|
+
"app" => { "$ref" => "#" }
|
134
|
+
)
|
135
|
+
expand
|
136
|
+
assert_equal [], errors
|
137
|
+
schema = @schema.properties["app"]
|
138
|
+
assert_equal ["object"], schema.type
|
103
139
|
end
|
104
140
|
|
105
141
|
it "errors on a JSON Pointer that can't be resolved" do
|
106
|
-
|
107
|
-
"$ref" => "#/definitions/nope"
|
108
|
-
|
142
|
+
pointer("#/properties").merge!(
|
143
|
+
"app" => { "$ref" => "#/definitions/nope" }
|
144
|
+
)
|
109
145
|
refute expand
|
110
|
-
assert_includes
|
146
|
+
assert_includes errors,
|
111
147
|
%{Couldn't resolve pointer "#/definitions/nope".}
|
148
|
+
assert_includes errors,
|
149
|
+
%{Couldn't resolve references: #/definitions/nope.}
|
112
150
|
end
|
113
151
|
|
114
|
-
it "errors on a
|
115
|
-
|
116
|
-
"$ref" => "/schemata/user#/definitions/name"
|
117
|
-
|
152
|
+
it "errors on a URI that can't be resolved" do
|
153
|
+
pointer("#/properties").merge!(
|
154
|
+
"app" => { "$ref" => "/schemata/user#/definitions/name" }
|
155
|
+
)
|
118
156
|
refute expand
|
119
|
-
assert_includes
|
120
|
-
%{Couldn't resolve references
|
157
|
+
assert_includes errors,
|
158
|
+
%{Couldn't resolve references: /schemata/user#/definitions/name.}
|
159
|
+
assert_includes errors, %{Couldn't resolve URI: /schemata/user.}
|
121
160
|
end
|
122
161
|
|
123
|
-
it "errors on a
|
124
|
-
|
125
|
-
"$ref" => "#/properties/
|
126
|
-
|
162
|
+
it "errors on a reference cycle" do
|
163
|
+
pointer("#/properties").merge!(
|
164
|
+
"app0" => { "$ref" => "#/properties/app2" },
|
165
|
+
"app1" => { "$ref" => "#/properties/app0" },
|
166
|
+
"app2" => { "$ref" => "#/properties/app1" },
|
167
|
+
)
|
127
168
|
refute expand
|
128
|
-
|
129
|
-
|
169
|
+
properties = "#/properties/app0, #/properties/app1, #/properties/app2"
|
170
|
+
assert_includes errors, %{Reference cycle detected: #{properties}.}
|
171
|
+
assert_includes errors, %{Couldn't resolve references: #{properties}.}
|
130
172
|
end
|
131
173
|
|
132
|
-
def
|
174
|
+
def errors
|
133
175
|
@expander.errors.map { |e| e.message }
|
134
176
|
end
|
135
177
|
|
@@ -141,9 +183,9 @@ describe JsonSchema::ReferenceExpander do
|
|
141
183
|
@schema_sample ||= DataScaffold.schema_sample
|
142
184
|
end
|
143
185
|
|
144
|
-
def expand
|
186
|
+
def expand(options = {})
|
145
187
|
@schema = JsonSchema::Parser.new.parse!(schema_sample)
|
146
188
|
@expander = JsonSchema::ReferenceExpander.new
|
147
|
-
@expander.expand(@schema)
|
189
|
+
@expander.expand(@schema, options)
|
148
190
|
end
|
149
191
|
end
|
@@ -291,7 +291,15 @@ describe JsonSchema::Validator do
|
|
291
291
|
%{Expected data to be a multiple of 0.01, value was: 0.005.}
|
292
292
|
end
|
293
293
|
|
294
|
-
it "validates additionalProperties" do
|
294
|
+
it "validates additionalProperties boolean successfully" do
|
295
|
+
pointer("#/definitions/app").merge!(
|
296
|
+
"additionalProperties" => true
|
297
|
+
)
|
298
|
+
data_sample["foo"] = "bar"
|
299
|
+
assert validate
|
300
|
+
end
|
301
|
+
|
302
|
+
it "validates additionalProperties boolean unsuccessfully" do
|
295
303
|
pointer("#/definitions/app").merge!(
|
296
304
|
"additionalProperties" => false
|
297
305
|
)
|
@@ -300,6 +308,28 @@ describe JsonSchema::Validator do
|
|
300
308
|
assert_includes error_messages, %{Extra keys in object: foo.}
|
301
309
|
end
|
302
310
|
|
311
|
+
it "validates additionalProperties schema successfully" do
|
312
|
+
pointer("#/definitions/app").merge!(
|
313
|
+
"additionalProperties" => {
|
314
|
+
"type" => ["boolean"]
|
315
|
+
}
|
316
|
+
)
|
317
|
+
data_sample["foo"] = true
|
318
|
+
assert validate
|
319
|
+
end
|
320
|
+
|
321
|
+
it "validates additionalProperties schema unsuccessfully" do
|
322
|
+
pointer("#/definitions/app").merge!(
|
323
|
+
"additionalProperties" => {
|
324
|
+
"type" => ["boolean"]
|
325
|
+
}
|
326
|
+
)
|
327
|
+
data_sample["foo"] = 4
|
328
|
+
refute validate
|
329
|
+
assert_includes error_messages,
|
330
|
+
%{Expected data to be of type "boolean"; value was: 4.}
|
331
|
+
end
|
332
|
+
|
303
333
|
it "validates simple dependencies" do
|
304
334
|
pointer("#/definitions/app/dependencies").merge!(
|
305
335
|
"production" => "ssl"
|
@@ -516,7 +546,25 @@ describe JsonSchema::Validator do
|
|
516
546
|
%{Expected data to match "ipv6" format, value was: 1::3:4:5:6:7:8:9.}
|
517
547
|
end
|
518
548
|
|
519
|
-
it "validates
|
549
|
+
it "validates regex format successfully" do
|
550
|
+
pointer("#/definitions/app/definitions/owner").merge!(
|
551
|
+
"format" => "regex"
|
552
|
+
)
|
553
|
+
data_sample["owner"] = "^owner@heroku\.com$"
|
554
|
+
assert validate
|
555
|
+
end
|
556
|
+
|
557
|
+
it "validates regex format successfully" do
|
558
|
+
pointer("#/definitions/app/definitions/owner").merge!(
|
559
|
+
"format" => "regex"
|
560
|
+
)
|
561
|
+
data_sample["owner"] = "^owner($"
|
562
|
+
refute validate
|
563
|
+
assert_includes error_messages,
|
564
|
+
%{Expected data to match "regex" format, value was: ^owner($.}
|
565
|
+
end
|
566
|
+
|
567
|
+
it "validates absolute uri format successfully" do
|
520
568
|
pointer("#/definitions/app/definitions/owner").merge!(
|
521
569
|
"format" => "uri"
|
522
570
|
)
|
@@ -524,14 +572,22 @@ describe JsonSchema::Validator do
|
|
524
572
|
assert validate
|
525
573
|
end
|
526
574
|
|
575
|
+
it "validates relative uri format successfully" do
|
576
|
+
pointer("#/definitions/app/definitions/owner").merge!(
|
577
|
+
"format" => "uri"
|
578
|
+
)
|
579
|
+
data_sample["owner"] = "schemata/app"
|
580
|
+
assert validate
|
581
|
+
end
|
582
|
+
|
527
583
|
it "validates uri format unsuccessfully" do
|
528
584
|
pointer("#/definitions/app/definitions/owner").merge!(
|
529
585
|
"format" => "uri"
|
530
586
|
)
|
531
|
-
data_sample["owner"] = "
|
587
|
+
data_sample["owner"] = "http://"
|
532
588
|
refute validate
|
533
589
|
assert_includes error_messages,
|
534
|
-
%{Expected data to match "uri" format, value was:
|
590
|
+
%{Expected data to match "uri" format, value was: http://.}
|
535
591
|
end
|
536
592
|
|
537
593
|
it "validates uuid format successfully" do
|