1 package com.networknt.schema; 2 3 import com.fasterxml.jackson.core.JsonProcessingException; 4 import com.fasterxml.jackson.databind.ObjectMapper; 5 import org.junit.jupiter.api.Assertions; 6 import org.junit.jupiter.api.BeforeAll; 7 import org.junit.jupiter.params.ParameterizedTest; 8 import org.junit.jupiter.params.provider.Arguments; 9 import org.junit.jupiter.params.provider.MethodSource; 10 11 import java.io.InputStream; 12 import java.util.Set; 13 import java.util.stream.Stream; 14 15 /** 16 * This project uses a dependency (com.ethlo.time:itu) to validate time representations. Version 1.51 of this library 17 * has a problem dealing with certain time zones having a negative offset; for example "-2:30" (Newfoundland time, NDT). 18 * Moving to version 1.7.0 of this library resolves the issue. 19 * 20 * This test class confirms that valid negative offsets do not result in a JSON validation error if the ITU library is 21 * updated to version 1.7.0 or later. 22 */ 23 public class Issue575Test { 24 private static JsonSchema schema; 25 26 @BeforeAll init()27 static void init() { 28 JsonSchemaFactory factory = JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V201909); 29 String schemaPath = "/schema/issue575-2019-09.json"; 30 InputStream schemaInputStream = Issue575Test.class.getResourceAsStream(schemaPath); 31 schema = factory.getSchema(schemaInputStream); 32 } 33 validTimeZoneOffsets()34 public static Stream<Arguments> validTimeZoneOffsets() { 35 String json1 = "{\"testDateTime\":\"2022-05-18T08:27:53-05:00\"}"; // America/New_York 36 String json2 = "{\"testDateTime\":\"2022-05-18T08:27:53-04:00\"}"; // America/New_York (DST) 37 String json3 = "{\"testDateTime\":\"2022-05-18T08:27:53-03:30\"}"; // America/St_Johns 38 String json4 = "{\"testDateTime\":\"2022-05-18T08:27:53-02:30\"}"; // America/St_Johns (DST) 39 String json5 = "{\"testDateTime\":\"2022-05-18T08:27:53+02:00\"}"; // Africa/Cairo 40 String json6 = "{\"testDateTime\":\"2022-05-18T08:27:53+03:30\"}"; // Asia/Tehran 41 String json7 = "{\"testDateTime\":\"2022-05-18T08:27:53+04:30\"}"; // Asia/Tehran (DST) 42 String json8 = "{\"testDateTime\":\"2022-05-18T08:27:53+04:00\"}"; // Asia/Dubai 43 String json9 = "{\"testDateTime\":\"2022-05-18T08:27:53+05:00\"}"; // Indian/Maldives 44 String json10 = "{\"testDateTime\":\"2022-05-18T08:27:53+05:30\"}"; // Asia/Kolkata 45 String json11 = "{\"testDateTime\":\"2022-05-18T08:27:53+10:00\"}"; // Australia/Sydney 46 String json12 = "{\"testDateTime\":\"2022-05-18T08:27:53+11:00\"}"; // Australia/Sydney (DST) 47 String json13 = "{\"testDateTime\":\"2022-05-18T08:27:53+14:00\"}"; // Pacific/Kiritimati 48 String json14 = "{\"testDateTime\":\"2022-05-18T18:45:32.123-05:00\"}"; // America/New_York 49 String json15 = "{\"testDateTime\":\"2022-05-18T18:45:32.123456-05:00\"}"; // America/New_York 50 String json16 = "{\"testDateTime\":\"2022-05-18T08:27:53Z\"}"; // UTC 51 String json17 = "{\"testDateTime\":\"2022-05-18T08:27:53+00:00\"}"; // UTC 52 53 return Stream.of( 54 Arguments.of(json1), 55 Arguments.of(json2), 56 Arguments.of(json3), 57 Arguments.of(json4), 58 Arguments.of(json5), 59 Arguments.of(json6), 60 Arguments.of(json7), 61 Arguments.of(json8), 62 Arguments.of(json9), 63 Arguments.of(json10), 64 Arguments.of(json11), 65 Arguments.of(json12), 66 Arguments.of(json13), 67 Arguments.of(json14), 68 Arguments.of(json15), 69 Arguments.of(json16), 70 Arguments.of(json17) 71 ); 72 } 73 74 /** 75 * Confirms that valid time zone offsets do not result in a JSON validation error. 76 * 77 * @param jsonObject a sample JSON payload to test 78 */ 79 @ParameterizedTest 80 @MethodSource("validTimeZoneOffsets") testValidTimeZoneOffsets(String jsonObject)81 void testValidTimeZoneOffsets(String jsonObject) throws JsonProcessingException { 82 Set<ValidationMessage> errors = schema.validate(new ObjectMapper().readTree(jsonObject)); 83 Assertions.assertTrue(errors.isEmpty()); 84 } 85 invalidTimeRepresentations()86 public static Stream<Arguments> invalidTimeRepresentations() { 87 // Invalid JSON payload: 30 days in April 88 String json1 = "{\"testDateTime\":\"2022-04-31T08:27:53+05:00\"}"; 89 // Invalid JSON payload: Invalid date/time separator 90 String json2 = "{\"testDateTime\":\"2022-05-18X08:27:53+05:00\"}"; 91 // Invalid JSON payload: Time zone details are missing 92 String json3 = "{\"testDateTime\":\"2022-05-18T08:27:53\"}"; 93 // Invalid JSON payload: seconds missing from time 94 String json4 = "{\"testDateTime\":\"2022-05-18T11:23Z\"}"; 95 // Invalid JSON payload: Text instead of date-time value 96 String json5 = "{\"testDateTime\":\"Orlando\"}"; 97 // Invalid JSON payload: A time zone offset of +23:00 is not valid 98 String json6 = "{\"testDateTime\":\"2022-05-18T08:27:53+23:00\"}"; 99 // Invalid JSON payload: A time zone offset of -23:00 is not valid 100 String json7 = "{\"testDateTime\":\"2022-05-18T08:27:53-23:00\"}"; 101 // Invalid JSON payload: com.ethlo.time:itu does not allow offset -00:00 (Valid per RFC3339 section 4.3. but prohibited in ISO-8601) 102 String json8 = "{\"testDateTime\":\"2022-05-18T08:27:53-00:00\"}"; 103 104 return Stream.of( 105 Arguments.of(json1), 106 Arguments.of(json2), 107 Arguments.of(json3), 108 Arguments.of(json4), 109 Arguments.of(json5), 110 Arguments.of(json6), 111 Arguments.of(json7), 112 Arguments.of(json8) 113 ); 114 } 115 116 /** 117 * Confirms that invalid time representations result in one or more a JSON validation errors. 118 * 119 * @param jsonObject a sample JSON payload to test 120 */ 121 @ParameterizedTest 122 @MethodSource("invalidTimeRepresentations") testInvalidTimeRepresentations(String jsonObject)123 void testInvalidTimeRepresentations(String jsonObject) throws JsonProcessingException { 124 Set<ValidationMessage> errors = schema.validate(new ObjectMapper().readTree(jsonObject), OutputFormat.DEFAULT, (executionContext, validationContext) -> { 125 executionContext.getExecutionConfig().setFormatAssertionsEnabled(true); 126 }); 127 Assertions.assertFalse(errors.isEmpty()); 128 } 129 } 130