Skip to content

Commit fdae2f8

Browse files
Issue386: FailFast should not cause exception on if (#485)
* Issue386: FailFast should not cause exception on 'if' * Added additional fail-fast and if-else tests
1 parent 8814cfa commit fdae2f8

File tree

9 files changed

+492
-22
lines changed

9 files changed

+492
-22
lines changed

pom.xml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,12 @@
106106
<version>${version.junit}</version>
107107
<scope>test</scope>
108108
</dependency>
109+
<dependency>
110+
<groupId>org.junit.jupiter</groupId>
111+
<artifactId>junit-jupiter-params</artifactId>
112+
<version>${version.junit}</version>
113+
<scope>test</scope>
114+
</dependency>
109115
<dependency>
110116
<groupId>org.mockito</groupId>
111117
<artifactId>mockito-core</artifactId>

src/main/java/com/networknt/schema/IfValidator.java

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -59,10 +59,18 @@ public Set<ValidationMessage> validate(JsonNode node, JsonNode rootNode, String
5959

6060
Set<ValidationMessage> errors = new LinkedHashSet<ValidationMessage>();
6161

62-
Set<ValidationMessage> ifErrors = ifSchema.validate(node, rootNode, at);
63-
if (ifErrors.isEmpty() && thenSchema != null) {
62+
boolean ifConditionPassed;
63+
try {
64+
ifConditionPassed = ifSchema.validate(node, rootNode, at).isEmpty();
65+
} catch (JsonSchemaException ex) {
66+
// When failFast is enabled, validations are thrown as exceptions.
67+
// An exception means the condition failed
68+
ifConditionPassed = false;
69+
}
70+
71+
if (ifConditionPassed && thenSchema != null) {
6472
errors.addAll(thenSchema.validate(node, rootNode, at));
65-
} else if (!ifErrors.isEmpty() && elseSchema != null) {
73+
} else if (!ifConditionPassed && elseSchema != null) {
6674
errors.addAll(elseSchema.validate(node, rootNode, at));
6775
}
6876

src/test/java/com/networknt/schema/Issue366FailFast.java renamed to src/test/java/com/networknt/schema/Issue366FailFastTest.java

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
import org.junit.jupiter.api.BeforeEach;
1414
import org.junit.jupiter.api.Test;
1515

16-
public class Issue366FailFast {
16+
public class Issue366FailFastTest {
1717

1818
@BeforeEach
1919
public void setup() throws IOException {
@@ -35,7 +35,7 @@ private void setupSchema() throws IOException {
3535

3636
URI uri = getSchema();
3737

38-
InputStream in = getClass().getResourceAsStream("/draft7/issue366_schema.json");
38+
InputStream in = getClass().getResourceAsStream("/schema/issue366_schema.json");
3939
JsonNode testCases = objectMapper.readValue(in, JsonNode.class);
4040
this.jsonSchema = schemaFactory.getSchema(uri, testCases,schemaValidatorsConfig);
4141
}
@@ -75,21 +75,21 @@ public void secondOneValid() throws Exception {
7575
@Test
7676
public void bothValid() throws Exception {
7777
String dataPath = "/data/issue366.json";
78-
78+
7979
assertThrows(JsonSchemaException.class, () -> {
8080
InputStream dataInputStream = getClass().getResourceAsStream(dataPath);
8181
JsonNode node = getJsonNodeFromStreamContent(dataInputStream);
8282
List<JsonNode> testNodes = node.findValues("tests");
8383
JsonNode testNode = testNodes.get(0).get(2);
8484
JsonNode dataNode = testNode.get("data");
85-
jsonSchema.validate(dataNode);
85+
jsonSchema.validate(dataNode);
8686
});
8787
}
8888

8989
@Test
9090
public void neitherValid() throws Exception {
9191
String dataPath = "/data/issue366.json";
92-
92+
9393
assertThrows(JsonSchemaException.class, () -> {
9494
InputStream dataInputStream = getClass().getResourceAsStream(dataPath);
9595
JsonNode node = getJsonNodeFromStreamContent(dataInputStream);

src/test/java/com/networknt/schema/Issue366FailSlow.java renamed to src/test/java/com/networknt/schema/Issue366FailSlowTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
import org.junit.jupiter.api.BeforeEach;
1414
import org.junit.jupiter.api.Test;
1515

16-
public class Issue366FailSlow {
16+
public class Issue366FailSlowTest {
1717

1818
@BeforeEach
1919
public void setup() throws IOException {
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
package com.networknt.schema;
2+
3+
import java.io.InputStream;
4+
import java.util.List;
5+
import java.util.Set;
6+
import java.util.stream.Collectors;
7+
8+
import org.junit.jupiter.api.Assertions;
9+
import org.junit.jupiter.api.Test;
10+
import org.junit.jupiter.params.ParameterizedTest;
11+
import org.junit.jupiter.params.provider.ValueSource;
12+
13+
import com.fasterxml.jackson.databind.JsonNode;
14+
import com.fasterxml.jackson.databind.ObjectMapper;
15+
16+
public class Issue386Test {
17+
protected JsonSchema getJsonSchemaFromPathV7(String schemaPath, boolean failFast) {
18+
InputStream schemaInputStream = getClass().getResourceAsStream(schemaPath);
19+
JsonSchemaFactory factory = JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V7);
20+
SchemaValidatorsConfig config = new SchemaValidatorsConfig();
21+
config.setFailFast(failFast);
22+
return factory.getSchema(schemaInputStream, config);
23+
}
24+
25+
protected JsonNode getJsonNodeFromPath(String dataPath) throws Exception {
26+
InputStream dataInputStream = getClass().getResourceAsStream(dataPath);
27+
ObjectMapper mapper = new ObjectMapper();
28+
JsonNode node = mapper.readTree(dataInputStream);
29+
return node;
30+
}
31+
32+
@ParameterizedTest
33+
@ValueSource(booleans = { true, false } )
34+
public void dataIsValid(boolean failFast) throws Exception {
35+
String schemaPath = "/schema/issue386-v7.json";
36+
String dataPath = "/data/issue386.json";
37+
JsonSchema schema = getJsonSchemaFromPathV7(schemaPath, failFast);
38+
JsonNode node = getJsonNodeFromPath(dataPath).get("valid");
39+
node.forEach(testNode -> {
40+
Set<ValidationMessage> errors = schema.validate(testNode.get("data"));
41+
Assertions.assertEquals(0, errors.size(), "Expected no errors for " + testNode.get("data"));
42+
});
43+
}
44+
45+
@Test
46+
public void dataIsInvalidFailFast() throws Exception {
47+
String schemaPath = "/schema/issue386-v7.json";
48+
String dataPath = "/data/issue386.json";
49+
JsonSchema schema = getJsonSchemaFromPathV7(schemaPath, true);
50+
JsonNode node = getJsonNodeFromPath(dataPath).get("invalid");
51+
node.forEach(testNode -> {
52+
try {
53+
schema.validate(testNode.get("data"));
54+
Assertions.fail();
55+
} catch (JsonSchemaException e) {
56+
Assertions.assertEquals(testNode.get("expectedErrors").get(0).asText(), e.getMessage());
57+
}
58+
});
59+
}
60+
61+
@Test
62+
public void dataIsInvalidFailSlow() throws Exception {
63+
String schemaPath = "/schema/issue386-v7.json";
64+
String dataPath = "/data/issue386.json";
65+
JsonSchema schema = getJsonSchemaFromPathV7(schemaPath, false);
66+
JsonNode node = getJsonNodeFromPath(dataPath).get("invalid");
67+
node.forEach(testNode -> {
68+
Set<ValidationMessage> errors = schema.validate(testNode.get("data"));
69+
List<String> errorMessages = errors.stream().map(x -> x.getMessage()).collect(Collectors.toList());
70+
testNode.get("expectedErrors").forEach(expectedError -> {
71+
Assertions.assertTrue(errorMessages.contains(expectedError.asText()));
72+
});
73+
});
74+
}
75+
}

src/test/resources/data/issue386.json

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
{
2+
"valid": [
3+
{
4+
"data": {
5+
"street_address": "1600 Pennsylvania Avenue NW",
6+
"country": "United States of America",
7+
"postal_code": "20500"
8+
}
9+
},
10+
{
11+
"data": {
12+
"street_address": "1600 Pennsylvania Avenue NW",
13+
"postal_code": "20500"
14+
}
15+
},
16+
{
17+
"data": {
18+
"street_address": "24 Sussex Drive",
19+
"country": "Canada",
20+
"postal_code": "K1M 1M4"
21+
}
22+
},
23+
{
24+
"data": {
25+
"street_address": "Adriaan Goekooplaan",
26+
"country": "Netherlands",
27+
"postal_code": "2517 JX"
28+
}
29+
}
30+
],
31+
"invalid": [
32+
{
33+
"data": {
34+
"street_address": "24 Sussex Drive",
35+
"country": "Canada",
36+
"postal_code": "10000"
37+
},
38+
"expectedErrors": ["$.postal_code: does not match the regex pattern [A-Z][0-9][A-Z] [0-9][A-Z][0-9]"]
39+
},
40+
{
41+
"description": "invalid through first then",
42+
"data": {
43+
"street_address": "1600 Pennsylvania Avenue NW",
44+
"postal_code": "K1M 1M4"
45+
},
46+
"expectedErrors": ["$.postal_code: does not match the regex pattern [0-9]{5}(-[0-9]{4})?"]
47+
}
48+
]
49+
}

src/test/resources/draft2019-09/if-then-else.json

Lines changed: 178 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,5 +184,183 @@
184184
"valid": true
185185
}
186186
]
187+
},
188+
{
189+
"description": "conditions by properties",
190+
"schema": {
191+
"type": "object",
192+
"properties": {
193+
"street_address": {
194+
"type": "string"
195+
},
196+
"country": {
197+
"enum": ["United States of America", "Canada"]
198+
}
199+
},
200+
"if": {
201+
"properties": {
202+
"country": {
203+
"const": "United States of America"
204+
}
205+
}
206+
},
207+
"then": {
208+
"properties": {
209+
"postal_code": {
210+
"pattern": "[0-9]{5}(-[0-9]{4})?"
211+
}
212+
}
213+
},
214+
"else": {
215+
"properties": {
216+
"postal_code": {
217+
"pattern": "[A-Z][0-9][A-Z] [0-9][A-Z][0-9]"
218+
}
219+
}
220+
}
221+
},
222+
"tests": [
223+
{
224+
"description": "valid through then",
225+
"data": {
226+
"street_address": "1600 Pennsylvania Avenue NW",
227+
"country": "United States of America",
228+
"postal_code": "20500"
229+
},
230+
"valid": true
231+
},
232+
{
233+
"description": "valid through then, alternative match",
234+
"data": {
235+
"street_address": "1600 Pennsylvania Avenue NW",
236+
"postal_code": "20500"
237+
},
238+
"valid": true
239+
},
240+
{
241+
"description": "valid through else",
242+
"data": {
243+
"street_address": "24 Sussex Drive",
244+
"country": "Canada",
245+
"postal_code": "K1M 1M4"
246+
},
247+
"valid": true
248+
},
249+
{
250+
"description": "invalid through else",
251+
"data": {
252+
"street_address": "24 Sussex Drive",
253+
"country": "Canada",
254+
"postal_code": "10000"
255+
},
256+
"valid": false
257+
}
258+
,
259+
{
260+
"description": "invalid through then",
261+
"data": {
262+
"street_address": "1600 Pennsylvania Avenue NW",
263+
"postal_code": "K1M 1M4"
264+
},
265+
"valid": false
266+
}
267+
]
268+
},
269+
{
270+
"description": "conditions by allOf properties",
271+
"schema": {
272+
"type": "object",
273+
"properties": {
274+
"street_address": {
275+
"type": "string"
276+
},
277+
"country": {
278+
"default": "United States of America",
279+
"enum": ["United States of America", "Canada", "Netherlands"]
280+
}
281+
},
282+
"allOf": [
283+
{
284+
"if": {
285+
"properties": { "country": { "const": "United States of America" } }
286+
},
287+
"then": {
288+
"properties": { "postal_code": { "pattern": "[0-9]{5}(-[0-9]{4})?" } }
289+
}
290+
},
291+
{
292+
"if": {
293+
"properties": { "country": { "const": "Canada" } },
294+
"required": ["country"]
295+
},
296+
"then": {
297+
"properties": { "postal_code": { "pattern": "[A-Z][0-9][A-Z] [0-9][A-Z][0-9]" } }
298+
}
299+
},
300+
{
301+
"if": {
302+
"properties": { "country": { "const": "Netherlands" } },
303+
"required": ["country"]
304+
},
305+
"then": {
306+
"properties": { "postal_code": { "pattern": "[0-9]{4} [A-Z]{2}" } }
307+
}
308+
}
309+
]
310+
},
311+
"tests": [
312+
{
313+
"description": "valid first if",
314+
"data": {
315+
"street_address": "1600 Pennsylvania Avenue NW",
316+
"country": "United States of America",
317+
"postal_code": "20500"
318+
},
319+
"valid": true
320+
},
321+
{
322+
"description": "valid first if, alternative match",
323+
"data": {
324+
"street_address": "1600 Pennsylvania Avenue NW",
325+
"postal_code": "20500"
326+
},
327+
"valid": true
328+
},
329+
{
330+
"description": "valid second if",
331+
"data": {
332+
"street_address": "24 Sussex Drive",
333+
"country": "Canada",
334+
"postal_code": "K1M 1M4"
335+
},
336+
"valid": true
337+
},
338+
{
339+
"description": "valid third if",
340+
"data": {
341+
"street_address": "Adriaan Goekooplaan",
342+
"country": "Netherlands",
343+
"postal_code": "2517 JX"
344+
},
345+
"valid": true
346+
},
347+
{
348+
"description": "invalid through second then",
349+
"data": {
350+
"street_address": "24 Sussex Drive",
351+
"country": "Canada",
352+
"postal_code": "10000"
353+
},
354+
"valid": false
355+
},
356+
{
357+
"description": "invalid through first then",
358+
"data": {
359+
"street_address": "1600 Pennsylvania Avenue NW",
360+
"postal_code": "K1M 1M4"
361+
},
362+
"valid": false
363+
}
364+
]
187365
}
188366
]

0 commit comments

Comments
 (0)