Skip to content

Commit e59aced

Browse files
committed
Expand ValidatorMessageFormatSpec to cover all JSON Schema v4 validators
Added tests for 6 additional JSON Schema v4 validation keywords: - exclusiveMinimum/exclusiveMaximum (numeric bounds) - additionalItems (tuple validation) - patternProperties (property name patterns) - dependencies (property dependencies) - anyOf (at least one schema match) Renumbered all test methods (e1-e26) to match their sequential order in the spec definition for better readability and maintainability. Replaced all `contain` assertions with `beEqualTo` for exact error message matching, providing higher confidence that error messages remain unchanged after the validator upgrade. Test coverage is now complete for all JSON Schema Draft 4 validation keywords supported by json-schema-validator 1.0.76. All 26 tests pass with validator 1.0.76, providing comprehensive baseline coverage for backward compatibility verification after upgrading to 1.5.8.
1 parent d7b709f commit e59aced

File tree

1 file changed

+151
-38
lines changed

1 file changed

+151
-38
lines changed

modules/core/src/test/scala/com.snowplowanalytics.iglu.client/validator/ValidatorMessageFormatSpec.scala

Lines changed: 151 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -33,35 +33,41 @@ class ValidatorMessageFormatSpec extends Specification {
3333
minimum validator produces correct message format $e1
3434
maximum validator produces correct message format $e2
3535
multipleOf validator produces correct message format $e3
36+
exclusiveMinimum validator produces correct message format $e4
37+
exclusiveMaximum validator produces correct message format $e5
3638

3739
String validators:
38-
minLength validator produces correct message format $e4
39-
maxLength validator produces correct message format $e5
40-
pattern validator produces correct message format $e6
40+
minLength validator produces correct message format $e6
41+
maxLength validator produces correct message format $e7
42+
pattern validator produces correct message format $e8
4143

4244
Array validators:
43-
minItems validator produces correct message format $e7
44-
maxItems validator produces correct message format $e8
45-
uniqueItems validator produces correct message format $e9
46-
items validator (via type error) produces correct message format $e10
45+
minItems validator produces correct message format $e9
46+
maxItems validator produces correct message format $e10
47+
uniqueItems validator produces correct message format $e11
48+
items validator (via type error) produces correct message format $e12
49+
additionalItems validator produces correct message format $e13
4750

4851
Object validators:
49-
minProperties validator produces correct message format $e11
50-
maxProperties validator produces correct message format $e12
51-
required validator produces correct message format $e13
52-
additionalProperties validator produces correct message format $e14
52+
minProperties validator produces correct message format $e14
53+
maxProperties validator produces correct message format $e15
54+
required validator produces correct message format $e16
55+
additionalProperties validator produces correct message format $e17
56+
patternProperties validator produces correct message format $e18
57+
dependencies validator produces correct message format $e19
5358

5459
Type validators:
55-
type validator produces correct message format $e15
56-
enum validator produces correct message format $e16
60+
type validator produces correct message format $e20
61+
enum validator produces correct message format $e21
5762

5863
Composition validators:
59-
allOf validator (via sub-validator) produces correct message format $e17
60-
oneOf validator produces correct message format $e18
61-
not validator produces correct message format $e19
64+
allOf validator (via sub-validator) produces correct message format $e22
65+
anyOf validator produces correct message format $e23
66+
oneOf validator produces correct message format $e24
67+
not validator produces correct message format $e25
6268

6369
Format validators:
64-
format validator produces correct message format $e20
70+
format validator produces correct message format $e26
6571
"""
6672

6773
// Helper to check message matches expected pattern
@@ -116,9 +122,33 @@ class ValidatorMessageFormatSpec extends Specification {
116122
}
117123
}
118124

125+
def e4 = {
126+
val schema = json"""{ "minimum": 5, "exclusiveMinimum": true }"""
127+
val input = json"""5"""
128+
CirceValidator.validate(input, schema) match {
129+
case Left(ValidatorError.InvalidData(errors)) =>
130+
val report = errors.head
131+
report.keyword must beSome("minimum")
132+
report.message must beEqualTo("$: must have a minimum value of 5")
133+
case other => ko(s"Expected InvalidData with exclusiveMinimum error, got: $other")
134+
}
135+
}
136+
137+
def e5 = {
138+
val schema = json"""{ "maximum": 10, "exclusiveMaximum": true }"""
139+
val input = json"""10"""
140+
CirceValidator.validate(input, schema) match {
141+
case Left(ValidatorError.InvalidData(errors)) =>
142+
val report = errors.head
143+
report.keyword must beSome("maximum")
144+
report.message must beEqualTo("$: must have a maximum value of 10")
145+
case other => ko(s"Expected InvalidData with exclusiveMaximum error, got: $other")
146+
}
147+
}
148+
119149
// String validators
120150

121-
def e4 = {
151+
def e6 = {
122152
val schema = json"""{ "minLength": 5 }"""
123153
val input = json""""abc""""
124154
CirceValidator.validate(input, schema) match {
@@ -131,7 +161,7 @@ class ValidatorMessageFormatSpec extends Specification {
131161
}
132162
}
133163

134-
def e5 = {
164+
def e7 = {
135165
val schema = json"""{ "maxLength": 3 }"""
136166
val input = json""""abcd""""
137167
CirceValidator.validate(input, schema) match {
@@ -144,7 +174,7 @@ class ValidatorMessageFormatSpec extends Specification {
144174
}
145175
}
146176

147-
def e6 = {
177+
def e8 = {
148178
val schema = json"""{ "pattern": "^[a-z]+$$" }"""
149179
val input = json""""ABC123""""
150180
CirceValidator.validate(input, schema) match {
@@ -159,7 +189,7 @@ class ValidatorMessageFormatSpec extends Specification {
159189

160190
// Array validators
161191

162-
def e7 = {
192+
def e9 = {
163193
val schema = json"""{ "minItems": 3 }"""
164194
val input = json"""[1, 2]"""
165195
CirceValidator.validate(input, schema) match {
@@ -173,7 +203,7 @@ class ValidatorMessageFormatSpec extends Specification {
173203
}
174204
}
175205

176-
def e8 = {
206+
def e10 = {
177207
val schema = json"""{ "maxItems": 2 }"""
178208
val input = json"""[1, 2, 3]"""
179209
CirceValidator.validate(input, schema) match {
@@ -187,7 +217,7 @@ class ValidatorMessageFormatSpec extends Specification {
187217
}
188218
}
189219

190-
def e9 = {
220+
def e11 = {
191221
val schema = json"""{ "uniqueItems": true }"""
192222
val input = json"""[1, 2, 2, 3]"""
193223
CirceValidator.validate(input, schema) match {
@@ -199,7 +229,7 @@ class ValidatorMessageFormatSpec extends Specification {
199229
}
200230
}
201231

202-
def e10 = {
232+
def e12 = {
203233
val schema = json"""{
204234
"items": { "type": "string" }
205235
}"""
@@ -214,9 +244,27 @@ class ValidatorMessageFormatSpec extends Specification {
214244
}
215245
}
216246

247+
def e13 = {
248+
val schema = json"""{
249+
"items": [
250+
{ "type": "number" },
251+
{ "type": "string" }
252+
],
253+
"additionalItems": false
254+
}"""
255+
val input = json"""[1, "two", 3]"""
256+
CirceValidator.validate(input, schema) match {
257+
case Left(ValidatorError.InvalidData(errors)) =>
258+
val report = errors.head
259+
report.keyword must beSome("additionalItems")
260+
report.message must beEqualTo("$[2]: no validator found at this index")
261+
case other => ko(s"Expected InvalidData with additionalItems error, got: $other")
262+
}
263+
}
264+
217265
// Object validators
218266

219-
def e11 = {
267+
def e14 = {
220268
val schema = json"""{ "minProperties": 3 }"""
221269
val input = json"""{ "a": 1, "b": 2 }"""
222270
CirceValidator.validate(input, schema) match {
@@ -229,7 +277,7 @@ class ValidatorMessageFormatSpec extends Specification {
229277
}
230278
}
231279

232-
def e12 = {
280+
def e15 = {
233281
val schema = json"""{ "maxProperties": 2 }"""
234282
val input = json"""{ "a": 1, "b": 2, "c": 3 }"""
235283
CirceValidator.validate(input, schema) match {
@@ -242,7 +290,7 @@ class ValidatorMessageFormatSpec extends Specification {
242290
}
243291
}
244292

245-
def e13 = {
293+
def e16 = {
246294
val schema = json"""{
247295
"properties": {
248296
"name": { "type": "string" }
@@ -260,7 +308,7 @@ class ValidatorMessageFormatSpec extends Specification {
260308
}
261309
}
262310

263-
def e14 = {
311+
def e17 = {
264312
val schema = json"""{
265313
"properties": {
266314
"name": { "type": "string" }
@@ -280,9 +328,47 @@ class ValidatorMessageFormatSpec extends Specification {
280328
}
281329
}
282330

331+
def e18 = {
332+
val schema = json"""{
333+
"patternProperties": {
334+
"^num_": { "type": "number" }
335+
},
336+
"additionalProperties": false
337+
}"""
338+
val input = json"""{ "num_value": "not a number" }"""
339+
CirceValidator.validate(input, schema) match {
340+
case Left(ValidatorError.InvalidData(errors)) =>
341+
val report = errors.head
342+
// patternProperties validation will fail on type, not patternProperties itself
343+
report.keyword must beSome("type")
344+
report.message must beEqualTo("$.num_value: string found, number expected")
345+
case other => ko(s"Expected InvalidData with type error from patternProperties, got: $other")
346+
}
347+
}
348+
349+
def e19 = {
350+
val schema = json"""{
351+
"properties": {
352+
"name": { "type": "string" },
353+
"age": { "type": "number" }
354+
},
355+
"dependencies": {
356+
"age": ["name"]
357+
}
358+
}"""
359+
val input = json"""{ "age": 30 }"""
360+
CirceValidator.validate(input, schema) match {
361+
case Left(ValidatorError.InvalidData(errors)) =>
362+
val report = errors.head
363+
report.keyword must beSome("dependencies")
364+
report.message must beEqualTo("$: has an error with dependencies {age=[name]}")
365+
case other => ko(s"Expected InvalidData with dependencies error, got: $other")
366+
}
367+
}
368+
283369
// Type validators
284370

285-
def e15 = {
371+
def e20 = {
286372
val schema = json"""{ "type": "string" }"""
287373
val input = json"""123"""
288374
CirceValidator.validate(input, schema) match {
@@ -295,21 +381,23 @@ class ValidatorMessageFormatSpec extends Specification {
295381
}
296382
}
297383

298-
def e16 = {
384+
def e21 = {
299385
val schema = json"""{ "enum": ["red", "green", "blue"] }"""
300386
val input = json""""yellow""""
301387
CirceValidator.validate(input, schema) match {
302388
case Left(ValidatorError.InvalidData(errors)) =>
303389
val report = errors.head
304390
report.keyword must beSome("enum")
305-
report.message must contain("does not have a value in the enumeration")
391+
report.message must beEqualTo(
392+
"$: does not have a value in the enumeration [red, green, blue]"
393+
)
306394
case other => ko(s"Expected InvalidData with enum error, got: $other")
307395
}
308396
}
309397

310398
// Composition validators
311399

312-
def e17 = {
400+
def e22 = {
313401
val schema = json"""{
314402
"allOf": [
315403
{ "type": "string" },
@@ -326,7 +414,26 @@ class ValidatorMessageFormatSpec extends Specification {
326414
}
327415
}
328416

329-
def e18 = {
417+
def e23 = {
418+
val schema = json"""{
419+
"anyOf": [
420+
{ "type": "string" },
421+
{ "type": "number" }
422+
]
423+
}"""
424+
val input = json"""true"""
425+
CirceValidator.validate(input, schema) match {
426+
case Left(ValidatorError.InvalidData(errors)) =>
427+
// anyOf typically reports sub-validator failures, not anyOf itself
428+
// Look for the first error which should be a type error
429+
val report = errors.head
430+
report.keyword must beSome("type")
431+
report.message must beEqualTo("$: boolean found, string expected")
432+
case other => ko(s"Expected InvalidData with type error from anyOf, got: $other")
433+
}
434+
}
435+
436+
def e24 = {
330437
val schema = json"""{
331438
"oneOf": [
332439
{ "type": "number", "multipleOf": 5 },
@@ -338,12 +445,14 @@ class ValidatorMessageFormatSpec extends Specification {
338445
case Left(ValidatorError.InvalidData(errors)) =>
339446
val report = errors.head
340447
report.keyword must beSome("oneOf")
341-
report.message must contain("should be valid to one and only one of schema")
448+
report.message must beEqualTo(
449+
"$: should be valid to one and only one of schema, but more than one are valid: {\"type\":\"number\",\"multipleOf\":5}{\"type\":\"number\",\"multipleOf\":3}"
450+
)
342451
case other => ko(s"Expected InvalidData with oneOf error, got: $other")
343452
}
344453
}
345454

346-
def e19 = {
455+
def e25 = {
347456
val schema = json"""{
348457
"not": { "type": "string" }
349458
}"""
@@ -352,21 +461,25 @@ class ValidatorMessageFormatSpec extends Specification {
352461
case Left(ValidatorError.InvalidData(errors)) =>
353462
val report = errors.head
354463
report.keyword must beSome("not")
355-
report.message must contain("should not be valid to the schema")
464+
report.message must beEqualTo(
465+
"$: should not be valid to the schema \"not\" : {\"type\":\"string\"}"
466+
)
356467
case other => ko(s"Expected InvalidData with not error, got: $other")
357468
}
358469
}
359470

360471
// Format validators
361472

362-
def e20 = {
473+
def e26 = {
363474
val schema = json"""{ "format": "ipv4" }"""
364475
val input = json""""not-an-ip""""
365476
CirceValidator.validate(input, schema) match {
366477
case Left(ValidatorError.InvalidData(errors)) =>
367478
val report = errors.head
368479
report.keyword must beSome("format")
369-
report.message must contain("does not match the ipv4 pattern")
480+
report.message must beEqualTo(
481+
"""$: does not match the ipv4 pattern ^(([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])$"""
482+
)
370483
report.targets.headOption must beSome("ipv4")
371484
case other => ko(s"Expected InvalidData with format error, got: $other")
372485
}

0 commit comments

Comments
 (0)