From f1aa58cd09e6609a261376673f6460369de13187 Mon Sep 17 00:00:00 2001
From: preetamnpr <128618622+preetamnpr@users.noreply.github.com>
Date: Thu, 28 Nov 2024 16:32:42 +0100
Subject: [PATCH 1/8] SD-1823 revisit of the Ovs checks with unit test cases.
---
ovs/pom.xml | 5 +
.../standards/ovs/OvsScenarioListBuilder.java | 11 +-
.../standards/ovs/checks/OvsChecks.java | 306 ++++++++----------
.../ovs/party/OvsFilterParameter.java | 45 ++-
.../standards/ovs/party/OvsPublisher.java | 38 ++-
...s-300-response-wrong-attribute-values.json | 119 +++++++
.../ovs-300-response-wrong-date-times.json | 118 +++++++
.../ovs-300-response-wrong-structure.json | 119 +++++++
.../ovs/messages/ovs-300-response.json | 45 ++-
.../standards/ovs/OvsChecksTest.java | 273 ++++++++++++++++
10 files changed, 883 insertions(+), 196 deletions(-)
create mode 100644 ovs/src/main/resources/standards/ovs/messages/ovs-300-response-wrong-attribute-values.json
create mode 100644 ovs/src/main/resources/standards/ovs/messages/ovs-300-response-wrong-date-times.json
create mode 100644 ovs/src/main/resources/standards/ovs/messages/ovs-300-response-wrong-structure.json
create mode 100644 ovs/src/test/java/org/dcsa/conformance/standards/ovs/OvsChecksTest.java
diff --git a/ovs/pom.xml b/ovs/pom.xml
index 34701936..d9e08d71 100644
--- a/ovs/pom.xml
+++ b/ovs/pom.xml
@@ -23,5 +23,10 @@
org.projectlombok
lombok
+
+
+ org.junit.jupiter
+ junit-jupiter
+
diff --git a/ovs/src/main/java/org/dcsa/conformance/standards/ovs/OvsScenarioListBuilder.java b/ovs/src/main/java/org/dcsa/conformance/standards/ovs/OvsScenarioListBuilder.java
index 819ad016..5b5e7fdc 100644
--- a/ovs/src/main/java/org/dcsa/conformance/standards/ovs/OvsScenarioListBuilder.java
+++ b/ovs/src/main/java/org/dcsa/conformance/standards/ovs/OvsScenarioListBuilder.java
@@ -41,13 +41,13 @@ public static LinkedHashMap createModuleScenario
CARRIER_SERVICE_NAME,
"Red Falcon Service",
START_DATE,
- "2026-01-01")),
+ "2024-01-01")),
scenarioWithParameters(
Map.of(
CARRIER_SERVICE_NAME,
"Great Lion Service",
END_DATE,
- "2021-01-01")),
+ "2025-01-01")),
scenarioWithParameters(Map.of(CARRIER_SERVICE_CODE, "BW1")),
scenarioWithParameters(Map.of(CARRIER_SERVICE_CODE, "BW1", LIMIT, "1")),
scenarioWithParameters(Map.of(UNIVERSAL_SERVICE_REFERENCE, "SR12345A")),
@@ -64,9 +64,10 @@ public static LinkedHashMap createModuleScenario
noAction()
.thenEither(
scenarioWithParameters(Map.of(UN_LOCATION_CODE, "NLAMS")),
- scenarioWithParameters(Map.of(UN_LOCATION_CODE, "USNYC", LIMIT, "1")),
- scenarioWithParameters(Map.of(FACILITY_SMDG_CODE, "APM")),
- scenarioWithParameters(Map.of(FACILITY_SMDG_CODE, "APM", LIMIT, "1")))),
+ scenarioWithParameters(Map.of(UN_LOCATION_CODE, "USNYC", LIMIT, "1"))
+ //TODO: Check schema validation for this
+ /*scenarioWithParameters(Map.of(FACILITY_SMDG_CODE, "APM")),
+ scenarioWithParameters(Map.of(FACILITY_SMDG_CODE, "APM", LIMIT, "1"))*/)),
Map.entry(
"Voyage schedules",
noAction()
diff --git a/ovs/src/main/java/org/dcsa/conformance/standards/ovs/checks/OvsChecks.java b/ovs/src/main/java/org/dcsa/conformance/standards/ovs/checks/OvsChecks.java
index 5cff8bd2..2e3c4c0d 100644
--- a/ovs/src/main/java/org/dcsa/conformance/standards/ovs/checks/OvsChecks.java
+++ b/ovs/src/main/java/org/dcsa/conformance/standards/ovs/checks/OvsChecks.java
@@ -13,6 +13,7 @@
import java.time.LocalDate;
import java.time.OffsetDateTime;
import java.time.format.DateTimeFormatter;
+import java.time.format.DateTimeParseException;
import java.util.*;
import java.util.function.BiPredicate;
import java.util.function.Supplier;
@@ -30,124 +31,70 @@ public static ActionCheck responseContentChecks(
checks.add(
JsonAttribute.customValidator(
- "Validate carrierServiceCode exists and "
- + "matches in JSON response if request parameter has carrierServiceCode",
- body ->
- validateParameter(
- body,
- sspSupplier,
- OvsFilterParameter.CARRIER_SERVICE_CODE,
- "*/carrierServiceCode")));
-
- checks.add(
- JsonAttribute.customValidator(
- "Validate universalServiceReference exists and "
- + "matches in JSON response if request parameter has universalServiceReference",
- body ->
- validateParameter(
- body,
- sspSupplier,
- OvsFilterParameter.UNIVERSAL_SERVICE_REFERENCE,
- "*/universalServiceReference")));
-
- checks.add(
- JsonAttribute.customValidator(
- "Validate vesselIMONumber exists and"
- + " matches in JSON response if request parameter has vesselIMONumber",
- body ->
- validateParameter(
- body,
- sspSupplier,
- OvsFilterParameter.VESSEL_IMO_NUMBER,
- "*/vesselSchedules/*/vesselIMONumber")));
-
- checks.add(
- JsonAttribute.customValidator(
- "Validate vesselName exists and"
- + " matches in JSON response if request parameter has vesselName",
- body ->
- validateParameter(
- body,
- sspSupplier,
- OvsFilterParameter.VESSEL_NAME,
- "*/vesselSchedules/*/vesselName")));
-
- checks.add(
- JsonAttribute.customValidator(
- "Validate carrierVoyageNumber exists and "
- + "matches in JSON response if request parameter has carrierVoyageNumber",
- body ->
- validateParameter(
- body,
- sspSupplier,
- OvsFilterParameter.CARRIER_VOYAGE_NUMBER,
- "*/vesselSchedules/*/transportCalls/*/carrierExportVoyageNumber",
- "*/vesselSchedules/*/transportCalls/*/carrierImportVoyageNumber")));
-
- checks.add(
- JsonAttribute.customValidator(
- "Validate universalVoyageReference exists and"
- + " matches in JSON response if request parameter has universalVoyageReference",
- body ->
- validateParameter(
- body,
- sspSupplier,
- OvsFilterParameter.UNIVERSAL_VOYAGE_REFERENCE,
- "*/vesselSchedules/*/transportCalls/*/universalImportVoyageReference",
- "*/vesselSchedules/*/transportCalls/*/universalExportVoyageReference")));
-
- checks.add(
- JsonAttribute.customValidator(
- "Validate UNLocationCode exists and "
- + "matches in JSON response if request parameter has UNLocationCode",
- body ->
- validateParameter(
- body,
- sspSupplier,
- OvsFilterParameter.UN_LOCATION_CODE,
- "*/vesselSchedules/*/transportCalls/*/location/UNLocationCode")));
+ "Every response received during a conformance test must contain schedules",
+ body -> {
+ Set validationErrors = new LinkedHashSet<>();
+ checkServiceSchedulesExist(body)
+ .forEach(
+ validationError ->
+ validationErrors.add(
+ "CheckServiceSchedules failed: %s".formatted(validationError)));
+ if (validationErrors.isEmpty()) {
+ return Set.of();
+ }
+ return validationErrors;
+ }));
checks.add(
JsonAttribute.customValidator(
- "Validate facilitySMDGCode exists and "
- + "matches in JSON response if request parameter has facilitySMDGCode",
- body ->
- validateParameter(
- body,
- sspSupplier,
- OvsFilterParameter.FACILITY_SMDG_CODE,
- "*/vesselSchedules/*/transportCalls/*/location/facilitySMDGCode")));
+ "If present, at least schedule attribute must match the corresponding query parameters",
+ body -> {
+ Map filterParametersMap = sspSupplier.get().getMap();
+ Set validationErrors = new LinkedHashSet<>();
+ checkThatScheduleValuesMatchParamValues(body, filterParametersMap)
+ .forEach(
+ validationError ->
+ validationErrors.add(
+ "Schedule Param Value Validation failed: %s"
+ .formatted(validationError)));
+ return validationErrors;
+ }));
checks.add(
JsonAttribute.customValidator(
- "Validate startDate exists and "
- + "matches in JSON response if request parameter has startDate",
- body ->
- validateDate(
- body,
- sspSupplier,
- OvsFilterParameter.START_DATE,
- (eventDate, expectedStartDate) ->
- !eventDate.isAfter(
- expectedStartDate)))); // Check if eventDate is before or equal to
- // startDate
+ "Check eventDateTime is greater than or equal to startDate filter parameter if present",
+ body -> {
+ Set validationErrors = new LinkedHashSet<>();
+ Map filterParametersMap = sspSupplier.get().getMap();
+ validateDate(
+ body, filterParametersMap, OvsFilterParameter.START_DATE, LocalDate::isBefore)
+ .forEach(
+ validationError ->
+ validationErrors.add(
+ "Start Date EventDateTime validation failed: %s"
+ .formatted(validationError)));
+ return validationErrors;
+ }));
checks.add(
JsonAttribute.customValidator(
- "Validate endDate exists and "
- + "matches in JSON response if request parameter has endDate",
- body ->
- validateDate(
- body,
- sspSupplier,
- OvsFilterParameter.END_DATE,
- (eventDate, expectedEndDate) ->
- !eventDate.isBefore(
- expectedEndDate)))); // Check if eventDate is after or equal to endDate
+ "Check eventDateTime is less than or equal to endDate filter parameter if present",
+ body -> {
+ Set validationErrors = new LinkedHashSet<>();
+ Map filterParametersMap = sspSupplier.get().getMap();
+ validateDate(
+ body, filterParametersMap, OvsFilterParameter.END_DATE, LocalDate::isAfter)
+ .forEach(
+ validationError ->
+ validationErrors.add(
+ "EndDate EventDateTime validation failed: %s"
+ .formatted(validationError)));
+ return validationErrors;
+ }));
checks.add(
JsonAttribute.customValidator(
- "Validate transportCallReference is unique across each array node",
+ "Check transportCallReference is unique across each service schedules",
OvsChecks::validateUniqueTransportCallReference));
checks.add(
@@ -166,7 +113,6 @@ public static ActionCheck responseContentChecks(
"The number of schedules exceeds the limit parameter: " + expectedLimit);
}
}
-
return Set.of();
}));
@@ -174,73 +120,56 @@ public static ActionCheck responseContentChecks(
OvsRole::isPublisher, matched, HttpMessageType.RESPONSE, standardVersion, checks);
}
- private Set validateParameter(
- JsonNode body,
- Supplier sspSupplier,
- OvsFilterParameter parameter,
- String... jsonPaths) {
- Optional> param =
- sspSupplier.get().getMap().entrySet().stream()
- .filter(e -> e.getKey().equals(parameter))
- .findFirst();
-
- if (param.isPresent()) {
- Set expectedValues =
- Arrays.stream(param.get().getValue().split(","))
- .map(String::trim)
- .collect(Collectors.toSet());
-
- // Check if ANY of the jsonPaths match the expectedValues
- if (jsonPaths.length > 1) {
- boolean anyMatch =
- Stream.of(jsonPaths)
- .anyMatch(
- jsonPath ->
- findMatchingNodes(body, jsonPath)
- .anyMatch(
- valueNode ->
- !(valueNode.isMissingNode() || valueNode.isNull())
- && expectedValues.contains(valueNode.asText())));
-
- if (!anyMatch) {
- return Set.of(
- "Missing or mismatched values for parameter: "
- + parameter.getQueryParamName()
- + " in any of the paths: "
- + Arrays.toString(jsonPaths));
- }
- } else {
- Set errors =
- Stream.of(jsonPaths)
- .flatMap(
- jsonPath ->
- findMatchingNodes(body, jsonPath)
- .filter(
- valueNode ->
- !(valueNode.isMissingNode() || valueNode.isNull())
- && !expectedValues.contains(valueNode.asText()))
- .map(
- valueNode ->
- "Missing or mismatched "
- + jsonPath
- + ": "
- + valueNode.asText()))
- .collect(Collectors.toSet());
+ public Set checkThatScheduleValuesMatchParamValues(
+ JsonNode schedulesNode, Map filterParametersMap) {
+ Set validationErrors = new LinkedHashSet<>();
+ Arrays.stream(OvsFilterParameter.values())
+ .filter(param -> !param.getJsonPaths().isEmpty())
+ .filter(param -> !param.isSeparateCheckRequired())
+ .filter(filterParametersMap::containsKey)
+ .forEach(
+ filterParameter -> {
+ Set parameterValues =
+ Arrays.stream(filterParametersMap.get(filterParameter).split(","))
+ .collect(Collectors.toSet());
+ Set attributeValues = new HashSet<>();
+ Set jsonPaths = filterParameter.getJsonPaths();
+ jsonPaths.forEach(
+ jsonPathExpression -> {
+ findMatchingNodes(schedulesNode, jsonPathExpression)
+ .forEach(
+ node -> {
+ if (!node.isMissingNode() && !node.isNull()) {
+ attributeValues.add(node.asText());
+ }
+ });
+ });
+ if (!attributeValues.isEmpty()
+ && parameterValues.stream().noneMatch(attributeValues::contains)) {
+ validationErrors.add(
+ "Value%s '%s' at path%s '%s' do%s not match value%s of query parameter '%s'"
+ .formatted(
+ attributeValues.size() > 1 ? "s" : "",
+ String.join(", ", attributeValues),
+ String.join(", ", jsonPaths),
+ attributeValues.size() > 1 ? "" : "es",
+ filterParametersMap.get(filterParameter).contains(",") ? "s" : "",
+ String.join(", ", filterParametersMap.get(filterParameter).split(",")),
+ filterParameter.getQueryParamName()));
+ }
+ });
- return errors.isEmpty() ? Set.of() : errors;
- }
- }
- return Set.of();
+ return validationErrors;
}
- private Set validateDate(
+ public Set validateDate(
JsonNode body,
- Supplier sspSupplier,
+ Map filterParametersMap,
OvsFilterParameter dateParameter,
BiPredicate dateComparison) {
Optional> dateParam =
- sspSupplier.get().getMap().entrySet().stream()
+ filterParametersMap.entrySet().stream()
.filter(e -> e.getKey().equals(dateParameter))
.findFirst();
@@ -258,11 +187,12 @@ private Set validateDate(
StreamSupport.stream(timestampsNode.spliterator(), false)
.filter(
timestampNode -> {
- OffsetDateTime eventDateTime =
- OffsetDateTime.parse(
- timestampNode.path("eventDateTime").asText(),
- DateTimeFormatter.ISO_DATE_TIME);
- return dateComparison.test(eventDateTime.toLocalDate(), expectedDate);
+ LocalDate eventDateTime =
+ stringToISODateTime(timestampNode.path("eventDateTime").asText());
+ if (eventDateTime != null) {
+ return dateComparison.test(eventDateTime, expectedDate);
+ }
+ return false;
}))
.map(
timestampNode ->
@@ -277,14 +207,21 @@ private Set validateDate(
return errors.isEmpty() ? Set.of() : errors;
}
- private Set validateUniqueTransportCallReference(JsonNode body) {
+ private static LocalDate stringToISODateTime(String dateTimeString) {
+ try {
+ return OffsetDateTime.parse(dateTimeString, DateTimeFormatter.ISO_DATE_TIME).toLocalDate();
+ } catch (DateTimeParseException e) {
+ return null;
+ }
+ }
+
+ public Set validateUniqueTransportCallReference(JsonNode body) {
Set transportCallReferences = new HashSet<>();
Set errors = new HashSet<>();
-
// Iterate over each array node in the response body
for (JsonNode node : body) {
- // Assuming the path to transportCallReference is consistent across array nodes
- findMatchingNodes(node, "*/vesselSchedules/*/transportCalls/*/transportCallReference")
+ findMatchingNodes(node, "vesselSchedules/*/transportCalls/*/transportCallReference")
+ .filter(tcrNode -> !tcrNode.isMissingNode() && !tcrNode.isNull())
.forEach(
transportCallReferenceNode -> {
String transportCallReference = transportCallReferenceNode.asText();
@@ -293,15 +230,13 @@ private Set validateUniqueTransportCallReference(JsonNode body) {
}
});
}
-
return errors;
}
- private Stream findMatchingNodes(JsonNode node, String jsonPath) {
+ public Stream findMatchingNodes(JsonNode node, String jsonPath) {
if (jsonPath.isEmpty() || jsonPath.equals("/")) {
return Stream.of(node);
}
-
String[] pathSegments = jsonPath.split("/");
if (pathSegments[0].equals("*")) {
if (node.isArray()) {
@@ -324,4 +259,19 @@ private Stream findMatchingNodes(JsonNode node, String jsonPath) {
String.join("/", Arrays.copyOfRange(pathSegments, 1, pathSegments.length)));
}
}
+
+ public Set checkServiceSchedulesExist(JsonNode body) {
+ Set validationErrors = new LinkedHashSet<>();
+ if (body == null || body.isMissingNode() || body.isNull()) {
+ validationErrors.add("Response body is missing or null.");
+ } else {
+ boolean hasVesselSchedules =
+ findMatchingNodes(body, "*/vesselSchedules")
+ .anyMatch(node -> !node.isMissingNode() && node.isArray() && !node.isEmpty());
+ if (!hasVesselSchedules) {
+ validationErrors.add("Response doesn't have schedules.");
+ }
+ }
+ return validationErrors;
+ }
}
diff --git a/ovs/src/main/java/org/dcsa/conformance/standards/ovs/party/OvsFilterParameter.java b/ovs/src/main/java/org/dcsa/conformance/standards/ovs/party/OvsFilterParameter.java
index fdb2abe2..cce5403b 100644
--- a/ovs/src/main/java/org/dcsa/conformance/standards/ovs/party/OvsFilterParameter.java
+++ b/ovs/src/main/java/org/dcsa/conformance/standards/ovs/party/OvsFilterParameter.java
@@ -4,6 +4,7 @@
import java.util.Arrays;
import java.util.Map;
+import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;
@@ -11,15 +12,25 @@ public enum OvsFilterParameter {
CARRIER_SERVICE_NAME("carrierServiceName"),
CARRIER_SERVICE_CODE("carrierServiceCode"),
UNIVERSAL_SERVICE_REFERENCE("universalServiceReference"),
- VESSEL_IMO_NUMBER("vesselIMONumber"),
- VESSEL_NAME("vesselName"),
- CARRIER_VOYAGE_NUMBER("carrierVoyageNumber"),
- UNIVERSAL_VOYAGE_REFERENCE("universalVoyageReference"),
- UN_LOCATION_CODE("UNLocationCode"),
- FACILITY_SMDG_CODE("facilitySMDGCode"),
- START_DATE("startDate"),
- END_DATE("endDate"),
- LIMIT("limit"),
+ VESSEL_IMO_NUMBER("vesselIMONumber", false, "*/vesselSchedules/*/vesselIMONumber"),
+ VESSEL_NAME("vesselName", false, "*/vesselSchedules/*/vesselName"),
+ CARRIER_VOYAGE_NUMBER(
+ "carrierVoyageNumber",
+ false,
+ "*/vesselSchedules/*/transportCalls/*/carrierExportVoyageNumber",
+ "*/vesselSchedules/*/transportCalls/*/carrierImportVoyageNumber"),
+ UNIVERSAL_VOYAGE_REFERENCE(
+ "universalVoyageReference",
+ false,
+ "*/vesselSchedules/*/transportCalls/*/universalImportVoyageReference",
+ "*/vesselSchedules/*/transportCalls/*/universalExportVoyageReference"),
+ UN_LOCATION_CODE(
+ "UNLocationCode", false, "*/vesselSchedules/*/transportCalls/*/location/UNLocationCode"),
+ FACILITY_SMDG_CODE(
+ "facilitySMDGCode", false, "*/vesselSchedules/*/transportCalls/*/location/facilitySMDGCode"),
+ START_DATE("startDate", true, "*/vesselSchedules/*/transportCalls/*/timestamps/*/eventDateTime"),
+ END_DATE("endDate", true, "*/vesselSchedules/*/transportCalls/*/timestamps/*/eventDateTime"),
+ LIMIT("limit", true),
;
public static final Map byQueryParamName =
@@ -30,7 +41,21 @@ public enum OvsFilterParameter {
@Getter private final String queryParamName;
- OvsFilterParameter(String queryParamName) {
+ @Getter private final Set jsonPaths;
+
+ @Getter private final boolean isSeparateCheckRequired;
+
+ OvsFilterParameter(String queryParamName, boolean isSeparateCheckRequired, String... jsonPaths) {
this.queryParamName = queryParamName;
+ this.jsonPaths = Set.of(jsonPaths);
+ this.isSeparateCheckRequired = isSeparateCheckRequired;
+ }
+
+ OvsFilterParameter(String queryParamName) {
+ this(queryParamName, false, "*/" + queryParamName);
+ }
+
+ OvsFilterParameter(String queryParamName, boolean isSeparateCheckRequired) {
+ this(queryParamName, isSeparateCheckRequired, "*/" + queryParamName);
}
}
diff --git a/ovs/src/main/java/org/dcsa/conformance/standards/ovs/party/OvsPublisher.java b/ovs/src/main/java/org/dcsa/conformance/standards/ovs/party/OvsPublisher.java
index 31ae5a7b..3b09fc99 100644
--- a/ovs/src/main/java/org/dcsa/conformance/standards/ovs/party/OvsPublisher.java
+++ b/ovs/src/main/java/org/dcsa/conformance/standards/ovs/party/OvsPublisher.java
@@ -44,6 +44,11 @@ public OvsPublisher(
orchestratorAuthHeader);
}
+ private static final boolean RETURN_EMPTY_RESPONSE = false;
+ private static final boolean USE_WRONG_ATTRIBUTE_VALUES = false;
+ private static final boolean USE_WRONG_DATE_TIMES = false;
+ private static final boolean USE_WRONG_RESPONSE_STRUCTURE = false;
+
@Override
protected void exportPartyJsonState(ObjectNode targetObjectNode) {}
@@ -120,6 +125,7 @@ public ConformanceResponse handleRequest(ConformanceRequest request) {
request.queryParams().containsKey("limit")
? request.queryParams().get("limit").iterator().next()
: "100");
+
if (filteredArray.size() > limit) {
ArrayNode limitedArray = OBJECT_MAPPER.createArrayNode();
for (int i = 0; i < limit; i++) {
@@ -130,7 +136,37 @@ public ConformanceResponse handleRequest(ConformanceRequest request) {
Map> headers =
new HashMap<>(Map.of(API_VERSION, List.of(apiVersion)));
- return request.createResponse(200, headers, new ConformanceMessageBody(filteredArray));
+
+ if (RETURN_EMPTY_RESPONSE) {
+ return request.createResponse(
+ 200, headers, new ConformanceMessageBody(OBJECT_MAPPER.createArrayNode()));
+ } else if (USE_WRONG_ATTRIBUTE_VALUES) {
+ return request.createResponse(
+ 200,
+ headers,
+ new ConformanceMessageBody(
+ JsonToolkit.templateFileToJsonNode(
+ "/standards/ovs/messages/ovs-300-response-wrong-attribute-values.json",
+ Map.ofEntries())));
+ } else if (USE_WRONG_DATE_TIMES) {
+ return request.createResponse(
+ 200,
+ headers,
+ new ConformanceMessageBody(
+ JsonToolkit.templateFileToJsonNode(
+ "/standards/ovs/messages/ovs-300-response-wrong-date-times.json",
+ Map.ofEntries())));
+ } else if (USE_WRONG_RESPONSE_STRUCTURE) {
+ return request.createResponse(
+ 200,
+ headers,
+ new ConformanceMessageBody(
+ JsonToolkit.templateFileToJsonNode(
+ "/standards/ovs/messages/ovs-300-response-wrong-structure.json",
+ Map.ofEntries())));
+ } else {
+ return request.createResponse(200, headers, new ConformanceMessageBody(filteredArray));
+ }
}
private ArrayNode applyFilter(
diff --git a/ovs/src/main/resources/standards/ovs/messages/ovs-300-response-wrong-attribute-values.json b/ovs/src/main/resources/standards/ovs/messages/ovs-300-response-wrong-attribute-values.json
new file mode 100644
index 00000000..4b92b204
--- /dev/null
+++ b/ovs/src/main/resources/standards/ovs/messages/ovs-300-response-wrong-attribute-values.json
@@ -0,0 +1,119 @@
+[
+ {
+ "carrierServiceName": "Great Lioness Service ",
+ "carrierServiceCode": "AA1",
+ "universalServiceReference": "SRSSS12345A",
+ "vesselSchedules": [
+ {
+ "vesselOperatorSMDGLinerCode": "HLC",
+ "vesselIMONumber": "9321483",
+ "vesselName": "King of the Roads",
+ "vesselCallSign": "NCVV",
+ "isDummyVessel": true,
+ "transportCalls": [
+ {
+ "portVisitReference": "NLAMS1234589",
+ "transportCallReference": "SR11X-9321483-2107W-NLAMS-ACT-1-1",
+ "carrierImportVoyageNumber": "2755N",
+ "carrierExportVoyageNumber": "2654S",
+ "universalImportVoyageReference": "2544N",
+ "universalExportVoyageReference": "2765S",
+ "location": {
+ "locationName": "Port of Amsterdam",
+ "locationType": "UNLO",
+ "UNLocationCode": "NLERRAMS"
+ },
+ "statusCode": "OMIT",
+ "timestamps": [
+ {
+ "eventTypeCode": "ARRI",
+ "eventClassifierCode": "PLN",
+ "eventDateTime": "2025-01-14T09:21:00+01:00",
+ "delayReasonCode": "WEA",
+ "changeRemark": "Bad weather"
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "carrierServiceName": "Great Tiger Service",
+ "carrierServiceCode": "FK1",
+ "universalServiceReference": "SR6789SSS0B",
+ "vesselSchedules": [
+ {
+ "vesselOperatorSMDGLinerCode": "MSC",
+ "vesselIMONumber": "9456789",
+ "vesselName": "Eyes of the Tiger",
+ "vesselCallSign": "QOCE",
+ "isDummyVessel": false,
+ "transportCalls": [
+ {
+ "portVisitReference": "SGSIN1234567",
+ "transportCallReference": "SR22XXXY-9456789-2108E-SGSIN-ACT-2-2",
+ "carrierImportVoyageNumber": "2348N",
+ "carrierExportVoyageNumber": "2876S",
+ "universalImportVoyageReference": "2887N",
+ "universalExportVoyageReference": "2343S",
+ "location": {
+ "locationName": "Port of Singapore",
+ "locationType": "UNLO",
+ "UNLocationCode": "SGSIN"
+ },
+ "statusCode": "ARRI",
+ "timestamps": [
+ {
+ "eventTypeCode": "DEPA",
+ "eventClassifierCode": "ACT",
+ "eventDateTime": "2025-02-20T15:30:00+08:00",
+ "delayReasonCode": "TRF",
+ "changeRemark": "Traffic congestion"
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "carrierServiceName": "Red Eagle Service",
+ "carrierServiceCode": "RF1",
+ "universalServiceReference": "SR54321C",
+ "vesselSchedules": [
+ {
+ "vesselOperatorSMDGLinerCode": "MAE",
+ "vesselIMONumber": "9876543",
+ "vesselName": "Eagle of the Rats",
+ "vesselCallSign": "EGLS",
+ "isDummyVessel": false,
+ "transportCalls": [
+ {
+ "portVisitReference": "USNYC1234567",
+ "transportCallReference": "SR33333Z-9876543-2109W-USNYC-ACT-3-3",
+ "carrierImportVoyageNumber": "2765N",
+ "carrierExportVoyageNumber": "2889S",
+ "universalImportVoyageReference": "2444N",
+ "universalExportVoyageReference": "2555S",
+ "location": {
+ "locationName": "Port of New York",
+ "locationType": "UNLO",
+ "UNLocationCode": "USSSYC"
+ },
+ "statusCode": "ARRI",
+ "timestamps": [
+ {
+ "eventTypeCode": "ARRI",
+ "eventClassifierCode": "ACT",
+ "eventDateTime": "2025-03-15T10:00:00-05:00",
+ "delayReasonCode": "TRF",
+ "changeRemark": "Heavy traffic"
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ }
+]
diff --git a/ovs/src/main/resources/standards/ovs/messages/ovs-300-response-wrong-date-times.json b/ovs/src/main/resources/standards/ovs/messages/ovs-300-response-wrong-date-times.json
new file mode 100644
index 00000000..257bc336
--- /dev/null
+++ b/ovs/src/main/resources/standards/ovs/messages/ovs-300-response-wrong-date-times.json
@@ -0,0 +1,118 @@
+[
+ {
+ "carrierServiceName": "Great Lion Servicedd",
+ "carrierServiceCode": "FE1",
+ "universalServiceReference": "SR12345A",
+ "vesselSchedules": [
+ {
+ "vesselOperatorSMDGLinerCode": "HLC",
+ "vesselIMONumber": "9321483",
+ "vesselName": "King of the Seas",
+ "vesselCallSign": "NCVV",
+ "isDummyVessel": true,
+ "transportCalls": [
+ {
+ "portVisitReference": "NLAMS1234589",
+ "transportCallReference": "SR11111X-9321483-2107W-NLAMS-ACT-1-1",
+ "carrierImportVoyageNumber": "2103N",
+ "carrierExportVoyageNumber": "2103S",
+ "universalImportVoyageReference": "2103N",
+ "universalExportVoyageReference": "2103S",
+ "location": {
+ "locationName": "Port of Amsterdam",
+ "locationType": "UNLO",
+ "UNLocationCode": "NLAMS"
+ },
+ "statusCode": "OMIT",
+ "timestamps": [
+ {
+ "eventTypeCode": "ARRI",
+ "eventClassifierCode": "PLN",
+ "delayReasonCode": "WEA",
+ "changeRemark": "Bad weather"
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "carrierServiceName": "Great Lion Servicedd",
+ "carrierServiceCode": "BW1",
+ "universalServiceReference": "SR67890B",
+ "vesselSchedules": [
+ {
+ "vesselOperatorSMDGLinerCode": "MSC",
+ "vesselIMONumber": "9456789",
+ "vesselName": "Queen of the Oceans",
+ "vesselCallSign": "QOCE",
+ "isDummyVessel": false,
+ "transportCalls": [
+ {
+ "portVisitReference": "SGSIN1234567",
+ "transportCallReference": "SR22222Y-9456789-2108E-SGSIN-ACT-2-2",
+ "carrierImportVoyageNumber": "2104N",
+ "carrierExportVoyageNumber": "2104S",
+ "universalImportVoyageReference": "2104N",
+ "universalExportVoyageReference": "2104S",
+ "location": {
+ "locationName": "Port of Singapore",
+ "locationType": "UNLO",
+ "UNLocationCode": "SGSIN"
+ },
+ "statusCode": "ARRI",
+ "timestamps": [
+ {
+ "eventTypeCode": "DEPA",
+ "eventClassifierCode": "ACT",
+ "eventDateTime": "2025/02/20T15:30:00+08:00",
+ "delayReasonCode": "TRF",
+ "changeRemark": "Traffic congestion"
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "carrierServiceName": "Red Falcon Service",
+ "carrierServiceCode": "RF1",
+ "universalServiceReference": "SR54321C",
+ "vesselSchedules": [
+ {
+ "vesselOperatorSMDGLinerCode": "MAE",
+ "vesselIMONumber": "9876543",
+ "vesselName": "Eagle of the Seas",
+ "vesselCallSign": "EGLS",
+ "isDummyVessel": false,
+ "transportCalls": [
+ {
+ "portVisitReference": "USNYC1234567",
+ "transportCallReference": "SR33333Z-9876543-2109W-USNYC-ACT-3-3",
+ "carrierImportVoyageNumber": "2105N",
+ "carrierExportVoyageNumber": "2105S",
+ "universalImportVoyageReference": "2105N",
+ "universalExportVoyageReference": "2105S",
+ "location": {
+ "locationName": "Port of New York",
+ "locationType": "UNLO",
+ "UNLocationCode": "USNYC"
+ },
+ "statusCode": "ARRI",
+ "timestamps": [
+ {
+ "eventTypeCode": "ARRI",
+ "eventClassifierCode": "ACT",
+ "eventDateTime": "2025.03.15T10:00:00-05:00",
+ "delayReasonCode": "TRF",
+ "changeRemark": "Heavy traffic"
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ }
+]
diff --git a/ovs/src/main/resources/standards/ovs/messages/ovs-300-response-wrong-structure.json b/ovs/src/main/resources/standards/ovs/messages/ovs-300-response-wrong-structure.json
new file mode 100644
index 00000000..a94748c6
--- /dev/null
+++ b/ovs/src/main/resources/standards/ovs/messages/ovs-300-response-wrong-structure.json
@@ -0,0 +1,119 @@
+[
+ {
+ "carrierServiceName": "Great Lion Servicedd",
+ "carrierServiceCode": "FE1",
+ "universalServiceReference": "SR12345A",
+ "vessels": [
+ {
+ "vesselOperatorSMDGLinerCode": "HLC",
+ "vesselIMONumber": "9321483",
+ "vesselName": "King of the Seas",
+ "vesselCallSign": "NCVV",
+ "isDummyVessel": true,
+ "transportCalls": [
+ {
+ "portVisitReference": "NLAMS1234589",
+ "transportCallReference": "SR11111X-9321483-2107W-NLAMS-ACT-1-1",
+ "carrierImportVoyageNumber": "2103N",
+ "carrierExportVoyageNumber": "2103S",
+ "universalImportVoyageReference": "2103N",
+ "universalExportVoyageReference": "2103S",
+ "location": {
+ "locationName": "Port of Amsterdam",
+ "locationType": "UNLO",
+ "UNLocationCode": "NLAMS"
+ },
+ "statusCode": "OMIT",
+ "timestamps": [
+ {
+ "eventTypeCode": "ARRI",
+ "eventClassifierCode": "PLN",
+ "eventDateTime": "2025-01-14T09:21:00+01:00",
+ "delayReasonCode": "WEA",
+ "changeRemark": "Bad weather"
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "carrierServiceName": "Great Lion Servicedd",
+ "carrierServiceCode": "BW1",
+ "universalServiceReference": "SR67890B",
+ "vesselSchedules": [
+ {
+ "vesselOperatorSMDGLinerCode": "MSC",
+ "vesselIMONumber": "9456789",
+ "vesselName": "Queen of the Oceans",
+ "vesselCallSign": "QOCE",
+ "isDummyVessel": false,
+ "transportCalls": [
+ {
+ "portVisitReference": "SGSIN1234567",
+ "transportCallReference": "SR22222Y-9456789-2108E-SGSIN-ACT-2-2",
+ "carrierImportVoyageNumber": "2104N",
+ "carrierExportVoyageNumber": "2104S",
+ "universalImportVoyageReference": "2104N",
+ "universalExportVoyageReference": "2104S",
+ "location": {
+ "locationName": "Port of Singapore",
+ "locationType": "UNLO",
+ "UNLocationCode": "SGSIN"
+ },
+ "statusCode": "ARRI",
+ "timestamps": [
+ {
+ "eventTypeCode": "DEPA",
+ "eventClassifierCode": "ACT",
+ "eventDateTime": "2025-02-20T15:30:00+08:00",
+ "delayReasonCode": "TRF",
+ "changeRemark": "Traffic congestion"
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "carrierServiceName": "Red Falcon Service",
+ "carrierServiceCode": "RF1",
+ "universalServiceReference": "SR54321C",
+ "vesselSchedules": [
+ {
+ "vesselOperatorSMDGLinerCode": "MAE",
+ "vesselIMONumber": "9876543",
+ "vesselName": "Eagle of the Seas",
+ "vesselCallSign": "EGLS",
+ "isDummyVessel": false,
+ "transportCalls": [
+ {
+ "portVisitReference": "USNYC1234567",
+ "transportCallReference": "SR33333Z-9876543-2109W-USNYC-ACT-3-3",
+ "carrierImportVoyageNumber": "2105N",
+ "carrierExportVoyageNumber": "2105S",
+ "universalImportVoyageReference": "2105N",
+ "universalExportVoyageReference": "2105S",
+ "location": {
+ "locationName": "Port of New York",
+ "locationType": "UNLO",
+ "UNLocationCode": "USNYC"
+ },
+ "statusCode": "ARRI",
+ "timestamps": [
+ {
+ "eventTypeCode": "ARRI",
+ "eventClassifierCode": "ACT",
+ "eventDateTime": "2025-03-15T10:00:00-05:00",
+ "delayReasonCode": "TRF",
+ "changeRemark": "Heavy traffic"
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ }
+]
diff --git a/ovs/src/main/resources/standards/ovs/messages/ovs-300-response.json b/ovs/src/main/resources/standards/ovs/messages/ovs-300-response.json
index 2f261703..897fbdf2 100644
--- a/ovs/src/main/resources/standards/ovs/messages/ovs-300-response.json
+++ b/ovs/src/main/resources/standards/ovs/messages/ovs-300-response.json
@@ -13,7 +13,7 @@
"transportCalls": [
{
"portVisitReference": "NLAMS1234589",
- "transportCallReference": "SR11111X-9321483-2107W-NLAMS-ACT-1-1",
+ "transportCallReference": "SR12345X-9321483-2107W-NLAMS-ACT-1-1",
"carrierImportVoyageNumber": "2103N",
"carrierExportVoyageNumber": "2103S",
"universalImportVoyageReference": "2103N",
@@ -28,7 +28,7 @@
{
"eventTypeCode": "ARRI",
"eventClassifierCode": "PLN",
- "eventDateTime": "2025-01-14T09:21:00+01:00",
+ "eventDateTime": "2024-01-14T09:21:00+01:00",
"delayReasonCode": "WEA",
"changeRemark": "Bad weather"
}
@@ -115,5 +115,46 @@
]
}
]
+ },
+ {
+ "carrierServiceName": "Red Eagle Service",
+ "carrierServiceCode": "RF1",
+ "universalServiceReference": "SR54821C",
+ "vesselSchedules": [
+ {
+ "vesselOperatorSMDGLinerCode": "MAE",
+ "vesselIMONumber": "9876543",
+ "vesselName": "Eagle of the Seas",
+ "vesselCallSign": "EGLS",
+ "isDummyVessel": false,
+ "transportCalls": [
+ {
+ "portVisitReference": "USNYC1234567",
+ "transportCallReference": "SR12345Z-9876543-2109W-USNYC-ACT-3-3",
+ "carrierImportVoyageNumber": "2105N",
+ "carrierExportVoyageNumber": "2105S",
+ "universalImportVoyageReference": "2105N",
+ "universalExportVoyageReference": "2105S",
+ "location": {
+ "locationName": "Port of New York",
+ "locationType": "UNLO",
+ "UNLocationCode": "USNYC",
+ "facilitySMDGCode": "APM"
+ },
+ "statusCode": "ARRI",
+ "timestamps": [
+ {
+ "eventTypeCode": "ARRI",
+ "eventClassifierCode": "ACT",
+ "eventDateTime": "2025-03-15T10:00:00-05:00",
+ "delayReasonCode": "TRF",
+ "changeRemark": "Heavy traffic"
+ }
+ ]
+ }
+ ]
+ }
+ ]
}
+
]
diff --git a/ovs/src/test/java/org/dcsa/conformance/standards/ovs/OvsChecksTest.java b/ovs/src/test/java/org/dcsa/conformance/standards/ovs/OvsChecksTest.java
new file mode 100644
index 00000000..636f86d7
--- /dev/null
+++ b/ovs/src/test/java/org/dcsa/conformance/standards/ovs/OvsChecksTest.java
@@ -0,0 +1,273 @@
+package org.dcsa.conformance.standards.ovs;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import org.dcsa.conformance.standards.ovs.checks.OvsChecks;
+import org.dcsa.conformance.standards.ovs.party.OvsFilterParameter;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import java.io.IOException;
+import java.time.LocalDate;
+import java.util.*;
+import java.util.stream.Stream;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+public class OvsChecksTest {
+
+ private final ObjectMapper objectMapper = new ObjectMapper();
+
+ private JsonNode serviceNodes;
+
+ @BeforeEach
+ void setUp() {
+ JsonNode vesselSchedules = createServiceVesselSchedules("1234567", "Great Vessel");
+ serviceNodes = createServiceNodes("Great Lion Service", "GLS", "SR12345A", vesselSchedules);
+ }
+
+ @Test
+ void testCheckThatScheduleValuesMatchParamValues_match() {
+ Map filterParametersMap =
+ Map.of(
+ OvsFilterParameter.CARRIER_SERVICE_CODE,
+ "GLS",
+ OvsFilterParameter.CARRIER_SERVICE_NAME,
+ "Great Lion Service",
+ OvsFilterParameter.UNIVERSAL_SERVICE_REFERENCE,
+ "SR12345A");
+
+ Set result =
+ OvsChecks.checkThatScheduleValuesMatchParamValues(serviceNodes, filterParametersMap);
+ assertTrue(result.isEmpty());
+ }
+
+ @Test
+ void testCheckThatScheduleValuesMatchParamValues_noMatch() {
+ Map filterParametersMap =
+ Map.of(OvsFilterParameter.CARRIER_SERVICE_CODE, "BW1");
+ Set result =
+ OvsChecks.checkThatScheduleValuesMatchParamValues(serviceNodes, filterParametersMap);
+ assertFalse(result.isEmpty());
+ }
+
+ @Test
+ void testCheckThatScheduleValuesMatchParamValues_emptyParams() {
+ Map filterParametersMap = Collections.emptyMap();
+ Set result =
+ OvsChecks.checkThatScheduleValuesMatchParamValues(serviceNodes, filterParametersMap);
+ assertTrue(result.isEmpty());
+ }
+
+ @Test
+ void testValidateDate_dateWithinRange() {
+ Map filterParametersMap =
+ Map.of(OvsFilterParameter.START_DATE, "2024-07-19");
+ Set result =
+ OvsChecks.validateDate(
+ serviceNodes, filterParametersMap, OvsFilterParameter.START_DATE, LocalDate::isBefore);
+ assertTrue(result.isEmpty());
+ }
+
+ @Test
+ void testValidateDate_dateOutsideRange() {
+ Map filterParametersMap =
+ Map.of(OvsFilterParameter.START_DATE, "2024-07-30");
+ Set result =
+ OvsChecks.validateDate(
+ serviceNodes, filterParametersMap, OvsFilterParameter.START_DATE, LocalDate::isBefore);
+ assertFalse(result.isEmpty());
+ }
+
+ @Test
+ void testValidateDate_noStartDate() {
+ Map filterParametersMap = Collections.emptyMap();
+ Set result =
+ OvsChecks.validateDate(
+ serviceNodes, filterParametersMap, OvsFilterParameter.START_DATE, LocalDate::isBefore);
+ assertTrue(result.isEmpty());
+ }
+
+ @Test
+ void testValidateUniqueTransportCallReference_unique() {
+ JsonNode vesselSchedules = createServiceVesselSchedules("1234567", "Great Vessel");
+ JsonNode serviceNodes =
+ createServiceNodes("Great Lion Service", "GLS", "SR12345A", vesselSchedules);
+ JsonNode transportCalls = vesselSchedules.get(0).get("transportCalls");
+
+ ObjectNode newTransportCall =
+ new ObjectMapper().createObjectNode().put("transportCallReference", "TCREF2");
+ ((ArrayNode) transportCalls).add(newTransportCall);
+ Set result = OvsChecks.validateUniqueTransportCallReference(serviceNodes);
+ assertTrue(result.isEmpty());
+ }
+
+ @Test
+ void testValidateUniqueTransportCallReference_duplicate() {
+ JsonNode vesselSchedules = createServiceVesselSchedules("1234567", "Great Vessel");
+
+ JsonNode serviceNodes =
+ createServiceNodes("Great Lion Service", "GLS", "SR12345A", vesselSchedules);
+ JsonNode transportCalls = vesselSchedules.get(0).get("transportCalls");
+
+ ObjectNode newTransportCall =
+ new ObjectMapper().createObjectNode().put("transportCallReference", "TCREF1");
+ ((ArrayNode) transportCalls).add(newTransportCall);
+
+ Set result = OvsChecks.validateUniqueTransportCallReference(serviceNodes);
+ assertFalse(result.isEmpty());
+ assertEquals(1, result.size());
+ }
+
+ @Test
+ void testValidateUniqueTransportCallReference_noVesselSchedules() {
+ ((ObjectNode) serviceNodes.get(0)).remove("vesselSchedules");
+ Set result = OvsChecks.validateUniqueTransportCallReference(serviceNodes);
+ assertTrue(result.isEmpty());
+ }
+
+ @Test
+ void testFindMatchingNodes_rootMatch() {
+ JsonNode root = objectMapper.createObjectNode().put("value", "test");
+ Stream result = OvsChecks.findMatchingNodes(root, "/");
+ assertEquals(1, result.count());
+ }
+
+ @Test
+ void testFindMatchingNodes_arrayMatch() throws IOException {
+ JsonNode root = objectMapper.readTree("[{\"a\": 1}, {\"b\": 2}]");
+ Stream result = OvsChecks.findMatchingNodes(root, "*");
+ assertEquals(2, result.count());
+ }
+
+ @Test
+ void testFindMatchingNodes_emptyArrayMatch() throws IOException {
+ JsonNode root = objectMapper.readTree("[]");
+ Stream result = OvsChecks.findMatchingNodes(root, "*");
+ assertEquals(0, result.count());
+ }
+
+ @Test
+ void testCheckServiceSchedulesExist_emptyServiceSchedules() {
+ JsonNode body = objectMapper.createArrayNode();
+ Set result = OvsChecks.checkServiceSchedulesExist(body);
+ assertEquals(1, result.size()); // Empty array should not return errors
+ }
+
+ @Test
+ void testCheckServiceSchedulesExist_nullServiceNode() {
+ Set result = OvsChecks.checkServiceSchedulesExist(null);
+ assertEquals(1, result.size()); // Empty array should not return errors
+ }
+
+ @Test
+ void testValidateDate_invalidDateFormat() {
+ Map filterParametersMap =
+ Map.of(OvsFilterParameter.START_DATE, "2024-07-19");
+ JsonNode timeStamps =
+ serviceNodes
+ .get(0)
+ .get("vesselSchedules")
+ .get(0)
+ .get("transportCalls")
+ .get(0)
+ .get("timestamps");
+
+ ObjectNode invalidTimeStamp =
+ new ObjectMapper().createObjectNode().put("eventDateTime", "TCREF1");
+ ((ArrayNode) timeStamps).add(invalidTimeStamp);
+
+ Set result =
+ OvsChecks.validateDate(
+ serviceNodes, filterParametersMap, OvsFilterParameter.START_DATE, LocalDate::isBefore);
+ assertFalse(result.isEmpty()); // Should contain errors about invalid date format
+ }
+
+ @Test
+ void testValidateDate_nullDate() { // Null date
+ Map filterParametersMap =
+ Map.of(OvsFilterParameter.START_DATE, "2024-07-19");
+ Set result =
+ OvsChecks.validateDate(
+ serviceNodes, filterParametersMap, OvsFilterParameter.START_DATE, LocalDate::isBefore);
+ assertFalse(result.isEmpty()); // Should contain errors (missing or null eventDateTime)
+ }
+
+ // Helper method to create a sample JsonNode for vessel schedules
+ private JsonNode createServiceNodes(
+ String carrierServiceName,
+ String carrierServiceCode,
+ String universalServiceReference,
+ JsonNode vesselSchedules) {
+ ObjectMapper objectMapper = new ObjectMapper();
+
+ // Create the root ArrayNode
+ ArrayNode rootArrayNode = objectMapper.createArrayNode();
+
+ // Create the first ObjectNode
+ ObjectNode firstObjectNode = objectMapper.createObjectNode();
+ firstObjectNode.put("carrierServiceName", carrierServiceName);
+ firstObjectNode.put("carrierServiceCode", carrierServiceCode);
+ firstObjectNode.put("universalServiceReference", universalServiceReference);
+ firstObjectNode.set("vesselSchedules", vesselSchedules);
+
+ rootArrayNode.add(firstObjectNode);
+ return rootArrayNode;
+ }
+
+ private JsonNode createServiceVesselSchedules(String vesselIMONumber, String vesselName) {
+ ArrayNode vesselSchedulesArrayNode = objectMapper.createArrayNode();
+ ObjectNode vesselSchedule = objectMapper.createObjectNode();
+ vesselSchedule.put("vesselIMONumber", vesselIMONumber);
+ vesselSchedule.put("vesselName", vesselName);
+ vesselSchedule.set(
+ "transportCalls",
+ createTransportCalls("TCREF1", "2104N", "2104S", "SR12345A", "SR12345A", "NLAMS"));
+ vesselSchedulesArrayNode.add(vesselSchedule);
+ return vesselSchedulesArrayNode;
+ }
+
+ private JsonNode createTransportCalls(
+ String transportCallReference,
+ String carrierImportVoyageNumber,
+ String carrierExportVoyageNumber,
+ String universalImportVoyageReference,
+ String universalExportVoyageReference,
+ String UNLocationCode) {
+ // Create the transportCalls ArrayNode for the vesselSchedule
+ ArrayNode transportCallsArrayNode = objectMapper.createArrayNode();
+ ObjectNode transportCall = objectMapper.createObjectNode();
+ transportCall.put("transportCallReference", transportCallReference);
+ transportCall.put("carrierImportVoyageNumber", carrierImportVoyageNumber);
+ transportCall.put("carrierExportVoyageNumber", carrierExportVoyageNumber);
+ transportCall.put("universalImportVoyageReference", universalImportVoyageReference);
+ transportCall.put("universalExportVoyageReference", universalExportVoyageReference);
+
+ // Create the location ObjectNode for the first transportCall
+ ObjectNode location = objectMapper.createObjectNode();
+ location.put("UNLocationCode", UNLocationCode);
+ transportCall.set("location", location);
+ transportCall.set("timestamps", createTimestamps());
+ transportCallsArrayNode.add(transportCall);
+ return transportCallsArrayNode;
+ }
+
+ private JsonNode createEventDateTime(String eventDateTime) {
+ // Create a timestamp for timestamps ArrayNode
+ ObjectNode timestamp = objectMapper.createObjectNode();
+ timestamp.put("eventTypeCode", "ARRI");
+ timestamp.put("eventClassifierCode", "PLN");
+ timestamp.put("eventDateTime", eventDateTime);
+ return timestamp;
+ }
+
+ private JsonNode createTimestamps() {
+ // Create the timestamps ArrayNode
+ ArrayNode timestampsArrayNode = objectMapper.createArrayNode();
+ timestampsArrayNode.add(createEventDateTime("2024-07-21T10:00:00Z"));
+ timestampsArrayNode.add(createEventDateTime("2024-07-22T10:00:00Z"));
+ timestampsArrayNode.add(createEventDateTime("2024-07-23T10:00:00Z"));
+ return timestampsArrayNode;
+ }
+}
From abd955fc17f35dfd005cd54e9e427b3e979af42e Mon Sep 17 00:00:00 2001
From: preetamnpr <128618622+preetamnpr@users.noreply.github.com>
Date: Thu, 28 Nov 2024 16:52:00 +0100
Subject: [PATCH 2/8] SD-1823 fixed failed tests and added @Getter.
---
.../standards/ovs/party/OvsFilterParameter.java | 7 ++++---
.../conformance/standards/ovs/OvsChecksTest.java | 12 +-----------
2 files changed, 5 insertions(+), 14 deletions(-)
diff --git a/ovs/src/main/java/org/dcsa/conformance/standards/ovs/party/OvsFilterParameter.java b/ovs/src/main/java/org/dcsa/conformance/standards/ovs/party/OvsFilterParameter.java
index cce5403b..b761b177 100644
--- a/ovs/src/main/java/org/dcsa/conformance/standards/ovs/party/OvsFilterParameter.java
+++ b/ovs/src/main/java/org/dcsa/conformance/standards/ovs/party/OvsFilterParameter.java
@@ -8,6 +8,7 @@
import java.util.function.Function;
import java.util.stream.Collectors;
+@Getter
public enum OvsFilterParameter {
CARRIER_SERVICE_NAME("carrierServiceName"),
CARRIER_SERVICE_CODE("carrierServiceCode"),
@@ -39,11 +40,11 @@ public enum OvsFilterParameter {
Collectors.toUnmodifiableMap(
OvsFilterParameter::getQueryParamName, Function.identity()));
- @Getter private final String queryParamName;
+ private final String queryParamName;
- @Getter private final Set jsonPaths;
+ private final Set jsonPaths;
- @Getter private final boolean isSeparateCheckRequired;
+ private final boolean isSeparateCheckRequired;
OvsFilterParameter(String queryParamName, boolean isSeparateCheckRequired, String... jsonPaths) {
this.queryParamName = queryParamName;
diff --git a/ovs/src/test/java/org/dcsa/conformance/standards/ovs/OvsChecksTest.java b/ovs/src/test/java/org/dcsa/conformance/standards/ovs/OvsChecksTest.java
index 636f86d7..d4d3fe23 100644
--- a/ovs/src/test/java/org/dcsa/conformance/standards/ovs/OvsChecksTest.java
+++ b/ovs/src/test/java/org/dcsa/conformance/standards/ovs/OvsChecksTest.java
@@ -181,17 +181,7 @@ void testValidateDate_invalidDateFormat() {
Set result =
OvsChecks.validateDate(
serviceNodes, filterParametersMap, OvsFilterParameter.START_DATE, LocalDate::isBefore);
- assertFalse(result.isEmpty()); // Should contain errors about invalid date format
- }
-
- @Test
- void testValidateDate_nullDate() { // Null date
- Map filterParametersMap =
- Map.of(OvsFilterParameter.START_DATE, "2024-07-19");
- Set result =
- OvsChecks.validateDate(
- serviceNodes, filterParametersMap, OvsFilterParameter.START_DATE, LocalDate::isBefore);
- assertFalse(result.isEmpty()); // Should contain errors (missing or null eventDateTime)
+ assertTrue(result.isEmpty()); // Should contain errors about invalid date format
}
// Helper method to create a sample JsonNode for vessel schedules
From cc4a93107b5a8ebfc68f0861c654f362ce9b5acc Mon Sep 17 00:00:00 2001
From: preetamnpr <128618622+preetamnpr@users.noreply.github.com>
Date: Thu, 28 Nov 2024 18:31:55 +0100
Subject: [PATCH 3/8] SD-1823 added additional tests, additionalProperties flag
is set to false and removed TODO
---
.../standards/ovs/OvsScenarioListBuilder.java | 7 +-
.../ovs/messages/ovs-300-response.json | 3 +-
.../standards/ovs/schemas/OVS_v3.0.0.yaml | 1 +
.../standards/ovs/OvsChecksTest.java | 171 +++++++++++++++++-
4 files changed, 172 insertions(+), 10 deletions(-)
diff --git a/ovs/src/main/java/org/dcsa/conformance/standards/ovs/OvsScenarioListBuilder.java b/ovs/src/main/java/org/dcsa/conformance/standards/ovs/OvsScenarioListBuilder.java
index 5b5e7fdc..713da7ed 100644
--- a/ovs/src/main/java/org/dcsa/conformance/standards/ovs/OvsScenarioListBuilder.java
+++ b/ovs/src/main/java/org/dcsa/conformance/standards/ovs/OvsScenarioListBuilder.java
@@ -64,10 +64,9 @@ public static LinkedHashMap createModuleScenario
noAction()
.thenEither(
scenarioWithParameters(Map.of(UN_LOCATION_CODE, "NLAMS")),
- scenarioWithParameters(Map.of(UN_LOCATION_CODE, "USNYC", LIMIT, "1"))
- //TODO: Check schema validation for this
- /*scenarioWithParameters(Map.of(FACILITY_SMDG_CODE, "APM")),
- scenarioWithParameters(Map.of(FACILITY_SMDG_CODE, "APM", LIMIT, "1"))*/)),
+ scenarioWithParameters(Map.of(UN_LOCATION_CODE, "USNYC", LIMIT, "1")),
+ scenarioWithParameters(Map.of(FACILITY_SMDG_CODE, "APM")),
+ scenarioWithParameters(Map.of(FACILITY_SMDG_CODE, "APM", LIMIT, "1")))),
Map.entry(
"Voyage schedules",
noAction()
diff --git a/ovs/src/main/resources/standards/ovs/messages/ovs-300-response.json b/ovs/src/main/resources/standards/ovs/messages/ovs-300-response.json
index 897fbdf2..447c6198 100644
--- a/ovs/src/main/resources/standards/ovs/messages/ovs-300-response.json
+++ b/ovs/src/main/resources/standards/ovs/messages/ovs-300-response.json
@@ -136,8 +136,7 @@
"universalImportVoyageReference": "2105N",
"universalExportVoyageReference": "2105S",
"location": {
- "locationName": "Port of New York",
- "locationType": "UNLO",
+ "locationType": "FACS",
"UNLocationCode": "USNYC",
"facilitySMDGCode": "APM"
},
diff --git a/ovs/src/main/resources/standards/ovs/schemas/OVS_v3.0.0.yaml b/ovs/src/main/resources/standards/ovs/schemas/OVS_v3.0.0.yaml
index d82950fd..eefd2af6 100644
--- a/ovs/src/main/resources/standards/ovs/schemas/OVS_v3.0.0.yaml
+++ b/ovs/src/main/resources/standards/ovs/schemas/OVS_v3.0.0.yaml
@@ -482,6 +482,7 @@ components:
maxLength: 75
UNLocationLocation:
title: UNLocation Location
+ additionalProperties: false
x-stoplight:
id: x4suin19xkq6q
type: object
diff --git a/ovs/src/test/java/org/dcsa/conformance/standards/ovs/OvsChecksTest.java b/ovs/src/test/java/org/dcsa/conformance/standards/ovs/OvsChecksTest.java
index d4d3fe23..f9c6d9ce 100644
--- a/ovs/src/test/java/org/dcsa/conformance/standards/ovs/OvsChecksTest.java
+++ b/ovs/src/test/java/org/dcsa/conformance/standards/ovs/OvsChecksTest.java
@@ -52,6 +52,168 @@ void testCheckThatScheduleValuesMatchParamValues_noMatch() {
assertFalse(result.isEmpty());
}
+ @Test
+ void testCheckCarrierServiceName_match() {
+ Map filterParametersMap =
+ Map.of(OvsFilterParameter.CARRIER_SERVICE_NAME, "Great Lion Service");
+ Set result =
+ OvsChecks.checkThatScheduleValuesMatchParamValues(serviceNodes, filterParametersMap);
+ assertTrue(result.isEmpty());
+ }
+
+ @Test
+ void testCheckCarrierServiceName_noMatch() {
+ Map filterParametersMap =
+ Map.of(OvsFilterParameter.CARRIER_SERVICE_NAME, "Great Tiger Service");
+ Set result =
+ OvsChecks.checkThatScheduleValuesMatchParamValues(serviceNodes, filterParametersMap);
+ assertFalse(result.isEmpty());
+ }
+
+ @Test
+ void testCheckUniversalServiceReference_match() {
+ Map filterParametersMap =
+ Map.of(OvsFilterParameter.UNIVERSAL_SERVICE_REFERENCE, "SR12345A");
+ Set result =
+ OvsChecks.checkThatScheduleValuesMatchParamValues(serviceNodes, filterParametersMap);
+ assertTrue(result.isEmpty());
+ }
+
+ @Test
+ void testCheckUniversalServiceReference_noMatch() {
+ Map filterParametersMap =
+ Map.of(OvsFilterParameter.UNIVERSAL_SERVICE_REFERENCE, "SRA");
+ Set result =
+ OvsChecks.checkThatScheduleValuesMatchParamValues(serviceNodes, filterParametersMap);
+ assertFalse(result.isEmpty());
+ }
+
+ @Test
+ void testCheckVesselIMONumber_match() {
+ Map filterParametersMap =
+ Map.of(OvsFilterParameter.VESSEL_IMO_NUMBER, "1234567");
+ Set result =
+ OvsChecks.checkThatScheduleValuesMatchParamValues(serviceNodes, filterParametersMap);
+ assertTrue(result.isEmpty());
+ }
+
+ @Test
+ void testCheckVesselIMONumber_noMatch() {
+ Map filterParametersMap =
+ Map.of(OvsFilterParameter.VESSEL_IMO_NUMBER, "1234");
+ Set result =
+ OvsChecks.checkThatScheduleValuesMatchParamValues(serviceNodes, filterParametersMap);
+ assertFalse(result.isEmpty());
+ }
+
+ @Test
+ void testCheckVesselName_match() {
+ Map filterParametersMap =
+ Map.of(OvsFilterParameter.VESSEL_NAME, "Great Vessel");
+ Set result =
+ OvsChecks.checkThatScheduleValuesMatchParamValues(serviceNodes, filterParametersMap);
+ assertTrue(result.isEmpty());
+ }
+
+
+ @Test
+ void testCheckVesselName_noMatch() {
+ Map filterParametersMap =
+ Map.of(OvsFilterParameter.VESSEL_NAME, "Great Bowl");
+ Set result =
+ OvsChecks.checkThatScheduleValuesMatchParamValues(serviceNodes, filterParametersMap);
+ assertFalse(result.isEmpty());
+ }
+
+
+ @Test
+ void testCheckCarrierVoyageNumber_match() {
+ Map filterParametersMap =
+ Map.of(OvsFilterParameter.CARRIER_VOYAGE_NUMBER, "2104N,2104S");
+ Set result =
+ OvsChecks.checkThatScheduleValuesMatchParamValues(serviceNodes, filterParametersMap);
+ assertTrue(result.isEmpty());
+ }
+
+ @Test
+ void testCheckCarrierVoyageNumber_noMatch() {
+ Map filterParametersMap =
+ Map.of(OvsFilterParameter.CARRIER_VOYAGE_NUMBER, "2104P,2104Q");
+ Set result =
+ OvsChecks.checkThatScheduleValuesMatchParamValues(serviceNodes, filterParametersMap);
+ assertFalse(result.isEmpty());
+ }
+
+ @Test
+ void testCheckUniversalVoyageReference_match() {
+ Map filterParametersMap =
+ Map.of(OvsFilterParameter.UNIVERSAL_VOYAGE_REFERENCE, "SR12345A,SR45678A");
+ Set result =
+ OvsChecks.checkThatScheduleValuesMatchParamValues(serviceNodes, filterParametersMap);
+ assertTrue(result.isEmpty());
+ }
+
+ @Test
+ void testCheckUniversalVoyageReference_noMatch() {
+ Map filterParametersMap =
+ Map.of(OvsFilterParameter.UNIVERSAL_VOYAGE_REFERENCE, "SR1245A,SR458A");
+ Set result =
+ OvsChecks.checkThatScheduleValuesMatchParamValues(serviceNodes, filterParametersMap);
+ assertFalse(result.isEmpty());
+ }
+
+ @Test
+ void testCheckUNLocationCode_match() {
+ Map filterParametersMap =
+ Map.of(OvsFilterParameter.UN_LOCATION_CODE, "NLAMS");
+ Set result =
+ OvsChecks.checkThatScheduleValuesMatchParamValues(serviceNodes, filterParametersMap);
+ assertTrue(result.isEmpty());
+ }
+
+ @Test
+ void testCheckUNLocationCode_noMatch() {
+ Map filterParametersMap =
+ Map.of(OvsFilterParameter.UN_LOCATION_CODE, "USNYC");
+ Set result =
+ OvsChecks.checkThatScheduleValuesMatchParamValues(serviceNodes, filterParametersMap);
+ assertFalse(result.isEmpty());
+ }
+
+ @Test
+ void testCheckFacilitySMDGCode_match() {
+ Map filterParametersMap =
+ Map.of(OvsFilterParameter.FACILITY_SMDG_CODE, "APM");
+ JsonNode transportCall =
+ serviceNodes
+ .get(0)
+ .get("vesselSchedules")
+ .get(0)
+ .get("transportCalls")
+ .get(0);
+ ((ObjectNode) transportCall.get("location")).put("facilitySMDGCode", "APM");
+ Set result =
+ OvsChecks.checkThatScheduleValuesMatchParamValues(serviceNodes, filterParametersMap);
+ assertTrue(result.isEmpty());
+ }
+
+ @Test
+ void testCheckFacilitySMDGCode_noMatch() {
+ JsonNode transportCall =
+ serviceNodes
+ .get(0)
+ .get("vesselSchedules")
+ .get(0)
+ .get("transportCalls")
+ .get(0);
+ ((ObjectNode) transportCall.get("location")).put("facilitySMDGCode", "APM");
+ Map filterParametersMap =
+ Map.of(OvsFilterParameter.FACILITY_SMDG_CODE, "APP");
+ Set result =
+ OvsChecks.checkThatScheduleValuesMatchParamValues(serviceNodes, filterParametersMap);
+ assertFalse(result.isEmpty());
+ }
+
@Test
void testCheckThatScheduleValuesMatchParamValues_emptyParams() {
Map filterParametersMap = Collections.emptyMap();
@@ -152,13 +314,13 @@ void testFindMatchingNodes_emptyArrayMatch() throws IOException {
void testCheckServiceSchedulesExist_emptyServiceSchedules() {
JsonNode body = objectMapper.createArrayNode();
Set result = OvsChecks.checkServiceSchedulesExist(body);
- assertEquals(1, result.size()); // Empty array should not return errors
+ assertEquals(1, result.size());
}
@Test
void testCheckServiceSchedulesExist_nullServiceNode() {
Set result = OvsChecks.checkServiceSchedulesExist(null);
- assertEquals(1, result.size()); // Empty array should not return errors
+ assertEquals(1, result.size());
}
@Test
@@ -181,7 +343,7 @@ void testValidateDate_invalidDateFormat() {
Set result =
OvsChecks.validateDate(
serviceNodes, filterParametersMap, OvsFilterParameter.START_DATE, LocalDate::isBefore);
- assertTrue(result.isEmpty()); // Should contain errors about invalid date format
+ assertTrue(result.isEmpty());
}
// Helper method to create a sample JsonNode for vessel schedules
@@ -213,7 +375,8 @@ private JsonNode createServiceVesselSchedules(String vesselIMONumber, String ves
vesselSchedule.put("vesselName", vesselName);
vesselSchedule.set(
"transportCalls",
- createTransportCalls("TCREF1", "2104N", "2104S", "SR12345A", "SR12345A", "NLAMS"));
+ createTransportCalls("TCREF1", "2104N", "2104S",
+ "SR12345A", "SR45678A", "NLAMS"));
vesselSchedulesArrayNode.add(vesselSchedule);
return vesselSchedulesArrayNode;
}
From d806693c946f57dab33ac4b3315a11cf6dd46c66 Mon Sep 17 00:00:00 2001
From: preetamnpr <128618622+preetamnpr@users.noreply.github.com>
Date: Fri, 29 Nov 2024 09:57:24 +0100
Subject: [PATCH 4/8] SD-1823 enhanced the validation check failure with
jsonpath and relevant changes to OvsChecksTest.java
---
.../standards/ovs/checks/OvsChecks.java | 132 ++++++++++++------
.../standards/ovs/OvsChecksTest.java | 6 +-
2 files changed, 90 insertions(+), 48 deletions(-)
diff --git a/ovs/src/main/java/org/dcsa/conformance/standards/ovs/checks/OvsChecks.java b/ovs/src/main/java/org/dcsa/conformance/standards/ovs/checks/OvsChecks.java
index 2e3c4c0d..f915fbd6 100644
--- a/ovs/src/main/java/org/dcsa/conformance/standards/ovs/checks/OvsChecks.java
+++ b/ovs/src/main/java/org/dcsa/conformance/standards/ovs/checks/OvsChecks.java
@@ -2,6 +2,7 @@
import com.fasterxml.jackson.databind.JsonNode;
import lombok.experimental.UtilityClass;
+import lombok.extern.slf4j.Slf4j;
import org.dcsa.conformance.core.check.ActionCheck;
import org.dcsa.conformance.core.check.JsonAttribute;
import org.dcsa.conformance.core.check.JsonContentCheck;
@@ -21,6 +22,7 @@
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
+@Slf4j
@UtilityClass
public class OvsChecks {
@@ -62,7 +64,7 @@ public static ActionCheck responseContentChecks(
checks.add(
JsonAttribute.customValidator(
- "Check eventDateTime is greater than or equal to startDate filter parameter if present",
+ "Check eventDateTime is greater than startDate filter parameter if present",
body -> {
Set validationErrors = new LinkedHashSet<>();
Map filterParametersMap = sspSupplier.get().getMap();
@@ -78,7 +80,7 @@ public static ActionCheck responseContentChecks(
checks.add(
JsonAttribute.customValidator(
- "Check eventDateTime is less than or equal to endDate filter parameter if present",
+ "Check eventDateTime is less than endDate filter parameter if present",
body -> {
Set validationErrors = new LinkedHashSet<>();
Map filterParametersMap = sspSupplier.get().getMap();
@@ -110,7 +112,8 @@ public static ActionCheck responseContentChecks(
int expectedLimit = Integer.parseInt(limitParam.get().getValue().trim());
if (body.size() > expectedLimit) {
return Set.of(
- "The number of schedules exceeds the limit parameter: " + expectedLimit);
+ "The number of service schedules exceeds the limit parameter: "
+ + expectedLimit);
}
}
return Set.of();
@@ -132,30 +135,50 @@ public Set checkThatScheduleValuesMatchParamValues(
Set parameterValues =
Arrays.stream(filterParametersMap.get(filterParameter).split(","))
.collect(Collectors.toSet());
- Set attributeValues = new HashSet<>();
+ Set> attributeValues = new HashSet<>();
Set jsonPaths = filterParameter.getJsonPaths();
jsonPaths.forEach(
jsonPathExpression -> {
findMatchingNodes(schedulesNode, jsonPathExpression)
.forEach(
- node -> {
- if (!node.isMissingNode() && !node.isNull()) {
- attributeValues.add(node.asText());
+ result -> {
+ if (!result.getValue().isMissingNode()
+ && !result.getValue().isNull()) {
+ attributeValues.add(result);
}
});
});
if (!attributeValues.isEmpty()
- && parameterValues.stream().noneMatch(attributeValues::contains)) {
- validationErrors.add(
- "Value%s '%s' at path%s '%s' do%s not match value%s of query parameter '%s'"
- .formatted(
- attributeValues.size() > 1 ? "s" : "",
- String.join(", ", attributeValues),
- String.join(", ", jsonPaths),
- attributeValues.size() > 1 ? "" : "es",
- filterParametersMap.get(filterParameter).contains(",") ? "s" : "",
- String.join(", ", filterParametersMap.get(filterParameter).split(",")),
- filterParameter.getQueryParamName()));
+ && parameterValues.stream()
+ .noneMatch(
+ parameterValue ->
+ attributeValues.stream()
+ .anyMatch(
+ entry ->
+ entry
+ .getValue()
+ .asText()
+ .trim()
+ .equals(
+ parameterValue.trim())))) { // Trim and compare
+
+ String errorMessage =
+ String.format(
+ "Value%s '%s' at path%s '%s' do%s not match value%s '%s' of query parameter '%s'",
+ attributeValues.size() > 1 ? "s" : "",
+ attributeValues.stream()
+ .map(e -> e.getValue().asText())
+ .collect(Collectors.joining(", ")),
+ attributeValues.size() > 1 ? "s" : "",
+ attributeValues.stream()
+ .map(Map.Entry::getKey)
+ .collect(Collectors.joining(", ")), // Get keys here
+ attributeValues.size() > 1 ? "" : "es",
+ parameterValues.size() > 1 ? "s" : "",
+ String.join(", ", parameterValues),
+ filterParameter.getQueryParamName());
+
+ validationErrors.add(errorMessage);
}
});
@@ -184,7 +207,7 @@ public Set validateDate(
findMatchingNodes(body, "*/vesselSchedules/*/transportCalls/*/timestamps")
.flatMap(
timestampsNode ->
- StreamSupport.stream(timestampsNode.spliterator(), false)
+ StreamSupport.stream(timestampsNode.getValue().spliterator(), false)
.filter(
timestampNode -> {
LocalDate eventDateTime =
@@ -199,8 +222,8 @@ public Set validateDate(
"Event DateTime "
+ timestampNode.path("eventDateTime").asText()
+ (dateParameter == OvsFilterParameter.START_DATE
- ? " is before or equal to the startDate: "
- : " is after or equal to the endDate: ")
+ ? " is before the startDate: "
+ : " is after the endDate: ")
+ expectedDate)
.collect(Collectors.toSet());
@@ -211,52 +234,67 @@ private static LocalDate stringToISODateTime(String dateTimeString) {
try {
return OffsetDateTime.parse(dateTimeString, DateTimeFormatter.ISO_DATE_TIME).toLocalDate();
} catch (DateTimeParseException e) {
+ log.error("Failed to parse date time string: {}", dateTimeString, e);
return null;
}
}
public Set validateUniqueTransportCallReference(JsonNode body) {
- Set transportCallReferences = new HashSet<>();
Set errors = new HashSet<>();
- // Iterate over each array node in the response body
+ // Iterate over each service schedule in the response body
for (JsonNode node : body) {
+ Set transportCallReferences = new HashSet<>();
findMatchingNodes(node, "vesselSchedules/*/transportCalls/*/transportCallReference")
- .filter(tcrNode -> !tcrNode.isMissingNode() && !tcrNode.isNull())
+ .filter(tcrNode -> !tcrNode.getValue().isMissingNode() && !tcrNode.getValue().isNull())
.forEach(
transportCallReferenceNode -> {
- String transportCallReference = transportCallReferenceNode.asText();
+ String transportCallReference = transportCallReferenceNode.getValue().asText();
if (!transportCallReferences.add(transportCallReference)) {
- errors.add("Duplicate transportCallReference found: " + transportCallReference);
+ errors.add(
+ ("Duplicate transportCallReference %s " + "found at %s")
+ .formatted(transportCallReference, transportCallReferenceNode.getKey()));
}
});
}
return errors;
}
- public Stream findMatchingNodes(JsonNode node, String jsonPath) {
+ public Stream> findMatchingNodes(JsonNode node, String jsonPath) {
+ return findMatchingNodes(node, jsonPath, "");
+ }
+
+ private Stream> findMatchingNodes(
+ JsonNode node, String jsonPath, String currentPath) {
if (jsonPath.isEmpty() || jsonPath.equals("/")) {
- return Stream.of(node);
+ return Stream.of(Map.entry(currentPath, node));
}
- String[] pathSegments = jsonPath.split("/");
- if (pathSegments[0].equals("*")) {
+
+ String[] pathSegments = jsonPath.split("/", 2);
+ String segment = pathSegments[0];
+ String remainingPath = pathSegments.length > 1 ? pathSegments[1] : "";
+
+ if (segment.equals("*")) {
if (node.isArray()) {
- // If the node is an array, iterate over its elements
- return StreamSupport.stream(node.spliterator(), false)
- .flatMap(
- childNode ->
- findMatchingNodes(
- childNode,
- String.join(
- "/", Arrays.copyOfRange(pathSegments, 1, pathSegments.length))));
+ List> results = new ArrayList<>();
+ for (int i = 0; i < node.size(); i++) {
+ JsonNode childNode = node.get(i);
+ String newPath = currentPath.isEmpty() ? String.valueOf(i) : currentPath + "/" + i;
+ results.addAll(findMatchingNodes(childNode, remainingPath, newPath).toList());
+ }
+ return results.stream();
+ } else if (node.isObject()) {
+ return findMatchingNodes(node, remainingPath, currentPath);
} else {
- // If not an array, treat it as a single node
- return findMatchingNodes(
- node, String.join("/", Arrays.copyOfRange(pathSegments, 1, pathSegments.length)));
+ return Stream.of();
}
} else {
- return findMatchingNodes(
- node.path(pathSegments[0]),
- String.join("/", Arrays.copyOfRange(pathSegments, 1, pathSegments.length)));
+ JsonNode childNode = node.path(segment);
+ if (!childNode.isMissingNode()) {
+ String newPath = currentPath.isEmpty() ? segment : currentPath + "/" + segment;
+ return findMatchingNodes(childNode, remainingPath, newPath);
+ } else {
+ return Stream.of();
+ }
}
}
@@ -267,7 +305,11 @@ public Set checkServiceSchedulesExist(JsonNode body) {
} else {
boolean hasVesselSchedules =
findMatchingNodes(body, "*/vesselSchedules")
- .anyMatch(node -> !node.isMissingNode() && node.isArray() && !node.isEmpty());
+ .anyMatch(
+ node ->
+ !node.getValue().isMissingNode()
+ && node.getValue().isArray()
+ && !node.getValue().isEmpty());
if (!hasVesselSchedules) {
validationErrors.add("Response doesn't have schedules.");
}
diff --git a/ovs/src/test/java/org/dcsa/conformance/standards/ovs/OvsChecksTest.java b/ovs/src/test/java/org/dcsa/conformance/standards/ovs/OvsChecksTest.java
index f9c6d9ce..4340f088 100644
--- a/ovs/src/test/java/org/dcsa/conformance/standards/ovs/OvsChecksTest.java
+++ b/ovs/src/test/java/org/dcsa/conformance/standards/ovs/OvsChecksTest.java
@@ -292,21 +292,21 @@ void testValidateUniqueTransportCallReference_noVesselSchedules() {
@Test
void testFindMatchingNodes_rootMatch() {
JsonNode root = objectMapper.createObjectNode().put("value", "test");
- Stream result = OvsChecks.findMatchingNodes(root, "/");
+ Stream result = OvsChecks.findMatchingNodes(root, "/").map(Map.Entry::getValue);
assertEquals(1, result.count());
}
@Test
void testFindMatchingNodes_arrayMatch() throws IOException {
JsonNode root = objectMapper.readTree("[{\"a\": 1}, {\"b\": 2}]");
- Stream result = OvsChecks.findMatchingNodes(root, "*");
+ Stream result = OvsChecks.findMatchingNodes(root, "*").map(Map.Entry::getValue);;
assertEquals(2, result.count());
}
@Test
void testFindMatchingNodes_emptyArrayMatch() throws IOException {
JsonNode root = objectMapper.readTree("[]");
- Stream result = OvsChecks.findMatchingNodes(root, "*");
+ Stream result = OvsChecks.findMatchingNodes(root, "*").map(Map.Entry::getValue);;
assertEquals(0, result.count());
}
From f9367aa9157384f5e4b5a40ec05915726848ec81 Mon Sep 17 00:00:00 2001
From: preetamnpr <128618622+preetamnpr@users.noreply.github.com>
Date: Fri, 29 Nov 2024 14:37:22 +0100
Subject: [PATCH 5/8] SD-1823 added suggestions from the codiumAI review agent,
partyinput validation and change the limit to 5.
---
.../standards/ovs/OvsScenarioListBuilder.java | 20 ++++----
.../SupplyScenarioParametersAction.java | 51 ++++++++++++++++++-
.../standards/ovs/checks/OvsChecks.java | 3 ++
3 files changed, 63 insertions(+), 11 deletions(-)
diff --git a/ovs/src/main/java/org/dcsa/conformance/standards/ovs/OvsScenarioListBuilder.java b/ovs/src/main/java/org/dcsa/conformance/standards/ovs/OvsScenarioListBuilder.java
index 713da7ed..4a973c5b 100644
--- a/ovs/src/main/java/org/dcsa/conformance/standards/ovs/OvsScenarioListBuilder.java
+++ b/ovs/src/main/java/org/dcsa/conformance/standards/ovs/OvsScenarioListBuilder.java
@@ -35,7 +35,7 @@ public static LinkedHashMap createModuleScenario
.thenEither(
scenarioWithParameters(Map.of(CARRIER_SERVICE_NAME, "Great Lion Service")),
scenarioWithParameters(
- Map.of(CARRIER_SERVICE_NAME, "Blue Whale Service", LIMIT, "1")),
+ Map.of(CARRIER_SERVICE_NAME, "Blue Whale Service", LIMIT, "5")),
scenarioWithParameters(
Map.of(
CARRIER_SERVICE_NAME,
@@ -49,24 +49,24 @@ public static LinkedHashMap createModuleScenario
END_DATE,
"2025-01-01")),
scenarioWithParameters(Map.of(CARRIER_SERVICE_CODE, "BW1")),
- scenarioWithParameters(Map.of(CARRIER_SERVICE_CODE, "BW1", LIMIT, "1")),
+ scenarioWithParameters(Map.of(CARRIER_SERVICE_CODE, "BW1", LIMIT, "5")),
scenarioWithParameters(Map.of(UNIVERSAL_SERVICE_REFERENCE, "SR12345A")),
scenarioWithParameters(
- Map.of(UNIVERSAL_SERVICE_REFERENCE, "SR67890B", LIMIT, "1")))),
+ Map.of(UNIVERSAL_SERVICE_REFERENCE, "SR67890B", LIMIT, "5")))),
Map.entry(
"Vessel schedules",
noAction()
.thenEither(
scenarioWithParameters(Map.of(VESSEL_IMO_NUMBER, "9456789")),
- scenarioWithParameters(Map.of(VESSEL_IMO_NUMBER, "9876543", LIMIT, "1")))),
+ scenarioWithParameters(Map.of(VESSEL_IMO_NUMBER, "9876543", LIMIT, "5")))),
Map.entry(
"Location schedules",
noAction()
.thenEither(
scenarioWithParameters(Map.of(UN_LOCATION_CODE, "NLAMS")),
- scenarioWithParameters(Map.of(UN_LOCATION_CODE, "USNYC", LIMIT, "1")),
+ scenarioWithParameters(Map.of(UN_LOCATION_CODE, "USNYC", LIMIT, "5")),
scenarioWithParameters(Map.of(FACILITY_SMDG_CODE, "APM")),
- scenarioWithParameters(Map.of(FACILITY_SMDG_CODE, "APM", LIMIT, "1")))),
+ scenarioWithParameters(Map.of(FACILITY_SMDG_CODE, "APM", LIMIT, "5")))),
Map.entry(
"Voyage schedules",
noAction()
@@ -79,7 +79,7 @@ public static LinkedHashMap createModuleScenario
Map.of(
CARRIER_VOYAGE_NUMBER, "2104S",
CARRIER_SERVICE_CODE, "BW1",
- LIMIT, "1")),
+ LIMIT, "5")),
scenarioWithParameters(
Map.of(
CARRIER_VOYAGE_NUMBER, "2103N",
@@ -88,7 +88,7 @@ public static LinkedHashMap createModuleScenario
Map.of(
CARRIER_VOYAGE_NUMBER, "2103S",
UNIVERSAL_SERVICE_REFERENCE, "SR12345A",
- LIMIT, "1")),
+ LIMIT, "5")),
scenarioWithParameters(
Map.of(
UNIVERSAL_VOYAGE_REFERENCE, "2103N",
@@ -97,7 +97,7 @@ public static LinkedHashMap createModuleScenario
Map.of(
UNIVERSAL_VOYAGE_REFERENCE, "2103S",
CARRIER_SERVICE_CODE, "FE1",
- LIMIT, "1")),
+ LIMIT, "5")),
scenarioWithParameters(
Map.of(
UNIVERSAL_VOYAGE_REFERENCE, "2105N",
@@ -106,7 +106,7 @@ public static LinkedHashMap createModuleScenario
Map.of(
UNIVERSAL_VOYAGE_REFERENCE, "2105S",
UNIVERSAL_SERVICE_REFERENCE, "SR54321C",
- LIMIT, "1")))))
+ LIMIT, "5")))))
.collect(
Collectors.toMap(
Map.Entry::getKey, Map.Entry::getValue, (e1, e2) -> e1, LinkedHashMap::new));
diff --git a/ovs/src/main/java/org/dcsa/conformance/standards/ovs/action/SupplyScenarioParametersAction.java b/ovs/src/main/java/org/dcsa/conformance/standards/ovs/action/SupplyScenarioParametersAction.java
index 6896ae0d..699ef07e 100644
--- a/ovs/src/main/java/org/dcsa/conformance/standards/ovs/action/SupplyScenarioParametersAction.java
+++ b/ovs/src/main/java/org/dcsa/conformance/standards/ovs/action/SupplyScenarioParametersAction.java
@@ -4,10 +4,17 @@
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
+import java.time.LocalDate;
+import java.time.format.DateTimeFormatter;
+import java.time.format.DateTimeParseException;
+import java.util.Arrays;
import java.util.Map;
+import java.util.Set;
import java.util.stream.Collectors;
+import java.util.stream.StreamSupport;
import lombok.Getter;
+import org.dcsa.conformance.core.UserFacingException;
import org.dcsa.conformance.standards.ovs.party.OvsFilterParameter;
import org.dcsa.conformance.standards.ovs.party.SuppliedScenarioParameters;
@@ -90,6 +97,48 @@ public boolean isInputRequired() {
@Override
public void handlePartyInput(JsonNode partyInput) {
super.handlePartyInput(partyInput);
- suppliedScenarioParameters = SuppliedScenarioParameters.fromJson(partyInput.get("input"));
+ JsonNode inputNode = partyInput.get("input");
+ Set inputKeys =
+ StreamSupport.stream(
+ ((Iterable) inputNode::fieldNames)
+ .spliterator(),
+ false)
+ .collect(Collectors.toSet());
+
+ Set missingKeys =
+ StreamSupport.stream(
+ ((Iterable) () -> getJsonForHumanReadablePrompt().fieldNames())
+ .spliterator(),
+ false)
+ .collect(Collectors.toSet());
+ missingKeys.removeAll(inputKeys);
+ if (!missingKeys.isEmpty()) {
+ throw new UserFacingException(
+ "The input must contain: %s".formatted(String.join(", ", missingKeys)));
+ }
+
+ Arrays.stream(OvsFilterParameter.values())
+ .map(OvsFilterParameter::getQueryParamName)
+ .filter(
+ queryParamName ->
+ queryParamName.startsWith(
+ OvsFilterParameter.START_DATE.getQueryParamName())
+ || queryParamName.startsWith(OvsFilterParameter.END_DATE.getQueryParamName()))
+ .filter(inputNode::hasNonNull)
+ .forEach(
+ queryParamName -> {
+ String dateValue = inputNode.path(queryParamName).asText();
+ try {
+ LocalDate.parse(dateValue, DateTimeFormatter.ISO_DATE);
+ } catch (DateTimeParseException e) {
+ throw new UserFacingException(
+ "Invalid date-time format '%s' for input parameter '%s'"
+ .formatted(dateValue, queryParamName),
+ e);
+ }
+ });
+
+ suppliedScenarioParameters = SuppliedScenarioParameters.fromJson(inputNode);
+
}
}
diff --git a/ovs/src/main/java/org/dcsa/conformance/standards/ovs/checks/OvsChecks.java b/ovs/src/main/java/org/dcsa/conformance/standards/ovs/checks/OvsChecks.java
index f915fbd6..97842aea 100644
--- a/ovs/src/main/java/org/dcsa/conformance/standards/ovs/checks/OvsChecks.java
+++ b/ovs/src/main/java/org/dcsa/conformance/standards/ovs/checks/OvsChecks.java
@@ -208,6 +208,9 @@ public Set validateDate(
.flatMap(
timestampsNode ->
StreamSupport.stream(timestampsNode.getValue().spliterator(), false)
+ .filter(
+ eventDateTimeNode ->
+ !eventDateTimeNode.isMissingNode() && !eventDateTimeNode.isNull())
.filter(
timestampNode -> {
LocalDate eventDateTime =
From d0ebefe0558bbab3a4518885f5596de091bf7334 Mon Sep 17 00:00:00 2001
From: preetamnpr <128618622+preetamnpr@users.noreply.github.com>
Date: Tue, 3 Dec 2024 15:08:03 +0100
Subject: [PATCH 6/8] SD-1823 moved the different publisher responses to test
folder and added unit test cases.
---
.../core/check/JsonAttributeBasedCheck.java | 6 +-
ovs/pom.xml | 1 +
.../standards/ovs/party/OvsPublisher.java | 40 +----
.../standards/ovs/OvsChecksTest.java | 154 ++++++++++++------
...s-300-response-wrong-attribute-values.json | 0
.../ovs-300-response-wrong-date-times.json | 0
.../ovs-300-response-wrong-structure.json | 0
7 files changed, 111 insertions(+), 90 deletions(-)
rename ovs/src/{main/resources/standards/ovs => test/resources}/messages/ovs-300-response-wrong-attribute-values.json (100%)
rename ovs/src/{main/resources/standards/ovs => test/resources}/messages/ovs-300-response-wrong-date-times.json (100%)
rename ovs/src/{main/resources/standards/ovs => test/resources}/messages/ovs-300-response-wrong-structure.json (100%)
diff --git a/core/src/main/java/org/dcsa/conformance/core/check/JsonAttributeBasedCheck.java b/core/src/main/java/org/dcsa/conformance/core/check/JsonAttributeBasedCheck.java
index 63bc1052..1dae74ec 100644
--- a/core/src/main/java/org/dcsa/conformance/core/check/JsonAttributeBasedCheck.java
+++ b/core/src/main/java/org/dcsa/conformance/core/check/JsonAttributeBasedCheck.java
@@ -9,14 +9,16 @@
import java.util.function.Predicate;
import java.util.stream.Stream;
+import lombok.Getter;
import lombok.NonNull;
import org.dcsa.conformance.core.traffic.ConformanceExchange;
import org.dcsa.conformance.core.traffic.HttpMessageType;
-class JsonAttributeBasedCheck extends ActionCheck {
+
+public class JsonAttributeBasedCheck extends ActionCheck {
private final String standardsVersion;
- private final List validators;
+ @Getter private final List validators;
JsonAttributeBasedCheck(
String titlePrefix,
diff --git a/ovs/pom.xml b/ovs/pom.xml
index d9e08d71..3af3f6e4 100644
--- a/ovs/pom.xml
+++ b/ovs/pom.xml
@@ -27,6 +27,7 @@
org.junit.jupiter
junit-jupiter
+ test
diff --git a/ovs/src/main/java/org/dcsa/conformance/standards/ovs/party/OvsPublisher.java b/ovs/src/main/java/org/dcsa/conformance/standards/ovs/party/OvsPublisher.java
index 3b09fc99..a3db8b72 100644
--- a/ovs/src/main/java/org/dcsa/conformance/standards/ovs/party/OvsPublisher.java
+++ b/ovs/src/main/java/org/dcsa/conformance/standards/ovs/party/OvsPublisher.java
@@ -44,11 +44,6 @@ public OvsPublisher(
orchestratorAuthHeader);
}
- private static final boolean RETURN_EMPTY_RESPONSE = false;
- private static final boolean USE_WRONG_ATTRIBUTE_VALUES = false;
- private static final boolean USE_WRONG_DATE_TIMES = false;
- private static final boolean USE_WRONG_RESPONSE_STRUCTURE = false;
-
@Override
protected void exportPartyJsonState(ObjectNode targetObjectNode) {}
@@ -135,38 +130,9 @@ public ConformanceResponse handleRequest(ConformanceRequest request) {
}
Map> headers =
- new HashMap<>(Map.of(API_VERSION, List.of(apiVersion)));
-
- if (RETURN_EMPTY_RESPONSE) {
- return request.createResponse(
- 200, headers, new ConformanceMessageBody(OBJECT_MAPPER.createArrayNode()));
- } else if (USE_WRONG_ATTRIBUTE_VALUES) {
- return request.createResponse(
- 200,
- headers,
- new ConformanceMessageBody(
- JsonToolkit.templateFileToJsonNode(
- "/standards/ovs/messages/ovs-300-response-wrong-attribute-values.json",
- Map.ofEntries())));
- } else if (USE_WRONG_DATE_TIMES) {
- return request.createResponse(
- 200,
- headers,
- new ConformanceMessageBody(
- JsonToolkit.templateFileToJsonNode(
- "/standards/ovs/messages/ovs-300-response-wrong-date-times.json",
- Map.ofEntries())));
- } else if (USE_WRONG_RESPONSE_STRUCTURE) {
- return request.createResponse(
- 200,
- headers,
- new ConformanceMessageBody(
- JsonToolkit.templateFileToJsonNode(
- "/standards/ovs/messages/ovs-300-response-wrong-structure.json",
- Map.ofEntries())));
- } else {
- return request.createResponse(200, headers, new ConformanceMessageBody(filteredArray));
- }
+ new HashMap<>(Map.of(API_VERSION, List.of(apiVersion)));
+
+ return request.createResponse(200, headers, new ConformanceMessageBody(filteredArray));
}
private ArrayNode applyFilter(
diff --git a/ovs/src/test/java/org/dcsa/conformance/standards/ovs/OvsChecksTest.java b/ovs/src/test/java/org/dcsa/conformance/standards/ovs/OvsChecksTest.java
index 4340f088..4203a04a 100644
--- a/ovs/src/test/java/org/dcsa/conformance/standards/ovs/OvsChecksTest.java
+++ b/ovs/src/test/java/org/dcsa/conformance/standards/ovs/OvsChecksTest.java
@@ -4,15 +4,19 @@
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
+import org.dcsa.conformance.core.check.ActionCheck;
+import org.dcsa.conformance.core.check.JsonAttributeBasedCheck;
+import org.dcsa.conformance.core.toolkit.JsonToolkit;
import org.dcsa.conformance.standards.ovs.checks.OvsChecks;
import org.dcsa.conformance.standards.ovs.party.OvsFilterParameter;
+import org.dcsa.conformance.standards.ovs.party.SuppliedScenarioParameters;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import java.io.IOException;
import java.time.LocalDate;
import java.util.*;
+import java.util.function.Supplier;
import java.util.stream.Stream;
-
import static org.junit.jupiter.api.Assertions.*;
public class OvsChecksTest {
@@ -27,6 +31,65 @@ void setUp() {
serviceNodes = createServiceNodes("Great Lion Service", "GLS", "SR12345A", vesselSchedules);
}
+ @Test
+ void testResponseContentChecks_validResponse() {
+ Set issues =
+ executeResponseChecks(Map.of(OvsFilterParameter.CARRIER_SERVICE_CODE, "GLS"), serviceNodes);
+ assertTrue(issues.isEmpty());
+ }
+
+ @Test
+ void testResponseContentChecks_withWrongAttributesValuesResponse() {
+ JsonNode jsonBody = JsonToolkit.templateFileToJsonNode(
+ "/messages/ovs-300-response-wrong-attribute-values.json",
+ Map.ofEntries());
+
+ Set issues =
+ executeResponseChecks(Map.of(OvsFilterParameter.CARRIER_SERVICE_CODE, "GLS"), jsonBody);
+ assertFalse(issues.isEmpty());
+ }
+
+ @Test
+ void testResponseContentChecks_withWrongDateTimesResponse() {
+ JsonNode jsonBody = JsonToolkit.templateFileToJsonNode(
+ "/messages/ovs-300-response-wrong-date-times.json",
+ Map.ofEntries());
+
+ Set issues =
+ executeResponseChecks(Map.of(OvsFilterParameter.START_DATE, "2027-07-19"), jsonBody);
+ assertTrue(issues.isEmpty());
+ }
+
+ @Test
+ void testResponseContentChecks_withWrongStructureResponse() {
+ JsonNode jsonBody = JsonToolkit.templateFileToJsonNode(
+ "/messages/ovs-300-response-wrong-structure.json",
+ Map.ofEntries());
+
+ Set issues =
+ executeResponseChecks(Map.of(OvsFilterParameter.CARRIER_SERVICE_CODE, ""), jsonBody);
+ assertFalse(issues.isEmpty());
+ }
+
+ private Set executeResponseChecks(
+ Map ovsFilterParameterStringMap, JsonNode serviceNodes) {
+ UUID matchedId = UUID.randomUUID();
+ String standardVersion = "3.0.0";
+ Supplier sspSupplier =
+ () -> SuppliedScenarioParameters.fromMap(ovsFilterParameterStringMap);
+ Set issues = new HashSet<>();
+
+ ActionCheck actionCheck =
+ OvsChecks.responseContentChecks(matchedId, standardVersion, sspSupplier);
+ ((JsonAttributeBasedCheck) actionCheck)
+ .getValidators()
+ .forEach(
+ validator -> {
+ issues.addAll(validator.validate(serviceNodes));
+ });
+ return issues;
+ }
+
@Test
void testCheckThatScheduleValuesMatchParamValues_match() {
Map filterParametersMap =
@@ -55,162 +118,150 @@ void testCheckThatScheduleValuesMatchParamValues_noMatch() {
@Test
void testCheckCarrierServiceName_match() {
Map filterParametersMap =
- Map.of(OvsFilterParameter.CARRIER_SERVICE_NAME, "Great Lion Service");
+ Map.of(OvsFilterParameter.CARRIER_SERVICE_NAME, "Great Lion Service");
Set result =
- OvsChecks.checkThatScheduleValuesMatchParamValues(serviceNodes, filterParametersMap);
+ OvsChecks.checkThatScheduleValuesMatchParamValues(serviceNodes, filterParametersMap);
assertTrue(result.isEmpty());
}
@Test
void testCheckCarrierServiceName_noMatch() {
Map filterParametersMap =
- Map.of(OvsFilterParameter.CARRIER_SERVICE_NAME, "Great Tiger Service");
+ Map.of(OvsFilterParameter.CARRIER_SERVICE_NAME, "Great Tiger Service");
Set result =
- OvsChecks.checkThatScheduleValuesMatchParamValues(serviceNodes, filterParametersMap);
+ OvsChecks.checkThatScheduleValuesMatchParamValues(serviceNodes, filterParametersMap);
assertFalse(result.isEmpty());
}
@Test
void testCheckUniversalServiceReference_match() {
Map filterParametersMap =
- Map.of(OvsFilterParameter.UNIVERSAL_SERVICE_REFERENCE, "SR12345A");
+ Map.of(OvsFilterParameter.UNIVERSAL_SERVICE_REFERENCE, "SR12345A");
Set result =
- OvsChecks.checkThatScheduleValuesMatchParamValues(serviceNodes, filterParametersMap);
+ OvsChecks.checkThatScheduleValuesMatchParamValues(serviceNodes, filterParametersMap);
assertTrue(result.isEmpty());
}
@Test
void testCheckUniversalServiceReference_noMatch() {
Map filterParametersMap =
- Map.of(OvsFilterParameter.UNIVERSAL_SERVICE_REFERENCE, "SRA");
+ Map.of(OvsFilterParameter.UNIVERSAL_SERVICE_REFERENCE, "SRA");
Set result =
- OvsChecks.checkThatScheduleValuesMatchParamValues(serviceNodes, filterParametersMap);
+ OvsChecks.checkThatScheduleValuesMatchParamValues(serviceNodes, filterParametersMap);
assertFalse(result.isEmpty());
}
@Test
void testCheckVesselIMONumber_match() {
Map filterParametersMap =
- Map.of(OvsFilterParameter.VESSEL_IMO_NUMBER, "1234567");
+ Map.of(OvsFilterParameter.VESSEL_IMO_NUMBER, "1234567");
Set result =
- OvsChecks.checkThatScheduleValuesMatchParamValues(serviceNodes, filterParametersMap);
+ OvsChecks.checkThatScheduleValuesMatchParamValues(serviceNodes, filterParametersMap);
assertTrue(result.isEmpty());
}
@Test
void testCheckVesselIMONumber_noMatch() {
Map filterParametersMap =
- Map.of(OvsFilterParameter.VESSEL_IMO_NUMBER, "1234");
+ Map.of(OvsFilterParameter.VESSEL_IMO_NUMBER, "1234");
Set result =
- OvsChecks.checkThatScheduleValuesMatchParamValues(serviceNodes, filterParametersMap);
+ OvsChecks.checkThatScheduleValuesMatchParamValues(serviceNodes, filterParametersMap);
assertFalse(result.isEmpty());
}
@Test
void testCheckVesselName_match() {
Map filterParametersMap =
- Map.of(OvsFilterParameter.VESSEL_NAME, "Great Vessel");
+ Map.of(OvsFilterParameter.VESSEL_NAME, "Great Vessel");
Set result =
- OvsChecks.checkThatScheduleValuesMatchParamValues(serviceNodes, filterParametersMap);
+ OvsChecks.checkThatScheduleValuesMatchParamValues(serviceNodes, filterParametersMap);
assertTrue(result.isEmpty());
}
-
@Test
void testCheckVesselName_noMatch() {
Map filterParametersMap =
- Map.of(OvsFilterParameter.VESSEL_NAME, "Great Bowl");
+ Map.of(OvsFilterParameter.VESSEL_NAME, "Great Bowl");
Set result =
- OvsChecks.checkThatScheduleValuesMatchParamValues(serviceNodes, filterParametersMap);
+ OvsChecks.checkThatScheduleValuesMatchParamValues(serviceNodes, filterParametersMap);
assertFalse(result.isEmpty());
}
-
@Test
void testCheckCarrierVoyageNumber_match() {
Map filterParametersMap =
- Map.of(OvsFilterParameter.CARRIER_VOYAGE_NUMBER, "2104N,2104S");
+ Map.of(OvsFilterParameter.CARRIER_VOYAGE_NUMBER, "2104N,2104S");
Set result =
- OvsChecks.checkThatScheduleValuesMatchParamValues(serviceNodes, filterParametersMap);
+ OvsChecks.checkThatScheduleValuesMatchParamValues(serviceNodes, filterParametersMap);
assertTrue(result.isEmpty());
}
@Test
void testCheckCarrierVoyageNumber_noMatch() {
Map filterParametersMap =
- Map.of(OvsFilterParameter.CARRIER_VOYAGE_NUMBER, "2104P,2104Q");
+ Map.of(OvsFilterParameter.CARRIER_VOYAGE_NUMBER, "2104P,2104Q");
Set result =
- OvsChecks.checkThatScheduleValuesMatchParamValues(serviceNodes, filterParametersMap);
+ OvsChecks.checkThatScheduleValuesMatchParamValues(serviceNodes, filterParametersMap);
assertFalse(result.isEmpty());
}
@Test
void testCheckUniversalVoyageReference_match() {
Map filterParametersMap =
- Map.of(OvsFilterParameter.UNIVERSAL_VOYAGE_REFERENCE, "SR12345A,SR45678A");
+ Map.of(OvsFilterParameter.UNIVERSAL_VOYAGE_REFERENCE, "SR12345A,SR45678A");
Set result =
- OvsChecks.checkThatScheduleValuesMatchParamValues(serviceNodes, filterParametersMap);
+ OvsChecks.checkThatScheduleValuesMatchParamValues(serviceNodes, filterParametersMap);
assertTrue(result.isEmpty());
}
@Test
void testCheckUniversalVoyageReference_noMatch() {
Map filterParametersMap =
- Map.of(OvsFilterParameter.UNIVERSAL_VOYAGE_REFERENCE, "SR1245A,SR458A");
+ Map.of(OvsFilterParameter.UNIVERSAL_VOYAGE_REFERENCE, "SR1245A,SR458A");
Set result =
- OvsChecks.checkThatScheduleValuesMatchParamValues(serviceNodes, filterParametersMap);
+ OvsChecks.checkThatScheduleValuesMatchParamValues(serviceNodes, filterParametersMap);
assertFalse(result.isEmpty());
}
@Test
void testCheckUNLocationCode_match() {
Map filterParametersMap =
- Map.of(OvsFilterParameter.UN_LOCATION_CODE, "NLAMS");
+ Map.of(OvsFilterParameter.UN_LOCATION_CODE, "NLAMS");
Set result =
- OvsChecks.checkThatScheduleValuesMatchParamValues(serviceNodes, filterParametersMap);
+ OvsChecks.checkThatScheduleValuesMatchParamValues(serviceNodes, filterParametersMap);
assertTrue(result.isEmpty());
}
@Test
void testCheckUNLocationCode_noMatch() {
Map filterParametersMap =
- Map.of(OvsFilterParameter.UN_LOCATION_CODE, "USNYC");
+ Map.of(OvsFilterParameter.UN_LOCATION_CODE, "USNYC");
Set result =
- OvsChecks.checkThatScheduleValuesMatchParamValues(serviceNodes, filterParametersMap);
+ OvsChecks.checkThatScheduleValuesMatchParamValues(serviceNodes, filterParametersMap);
assertFalse(result.isEmpty());
}
@Test
void testCheckFacilitySMDGCode_match() {
Map filterParametersMap =
- Map.of(OvsFilterParameter.FACILITY_SMDG_CODE, "APM");
+ Map.of(OvsFilterParameter.FACILITY_SMDG_CODE, "APM");
JsonNode transportCall =
- serviceNodes
- .get(0)
- .get("vesselSchedules")
- .get(0)
- .get("transportCalls")
- .get(0);
+ serviceNodes.get(0).get("vesselSchedules").get(0).get("transportCalls").get(0);
((ObjectNode) transportCall.get("location")).put("facilitySMDGCode", "APM");
Set result =
- OvsChecks.checkThatScheduleValuesMatchParamValues(serviceNodes, filterParametersMap);
+ OvsChecks.checkThatScheduleValuesMatchParamValues(serviceNodes, filterParametersMap);
assertTrue(result.isEmpty());
}
@Test
void testCheckFacilitySMDGCode_noMatch() {
JsonNode transportCall =
- serviceNodes
- .get(0)
- .get("vesselSchedules")
- .get(0)
- .get("transportCalls")
- .get(0);
+ serviceNodes.get(0).get("vesselSchedules").get(0).get("transportCalls").get(0);
((ObjectNode) transportCall.get("location")).put("facilitySMDGCode", "APM");
Map filterParametersMap =
- Map.of(OvsFilterParameter.FACILITY_SMDG_CODE, "APP");
+ Map.of(OvsFilterParameter.FACILITY_SMDG_CODE, "APP");
Set result =
- OvsChecks.checkThatScheduleValuesMatchParamValues(serviceNodes, filterParametersMap);
+ OvsChecks.checkThatScheduleValuesMatchParamValues(serviceNodes, filterParametersMap);
assertFalse(result.isEmpty());
}
@@ -299,14 +350,16 @@ void testFindMatchingNodes_rootMatch() {
@Test
void testFindMatchingNodes_arrayMatch() throws IOException {
JsonNode root = objectMapper.readTree("[{\"a\": 1}, {\"b\": 2}]");
- Stream result = OvsChecks.findMatchingNodes(root, "*").map(Map.Entry::getValue);;
+ Stream result = OvsChecks.findMatchingNodes(root, "*").map(Map.Entry::getValue);
+ ;
assertEquals(2, result.count());
}
@Test
void testFindMatchingNodes_emptyArrayMatch() throws IOException {
JsonNode root = objectMapper.readTree("[]");
- Stream result = OvsChecks.findMatchingNodes(root, "*").map(Map.Entry::getValue);;
+ Stream result = OvsChecks.findMatchingNodes(root, "*").map(Map.Entry::getValue);
+ ;
assertEquals(0, result.count());
}
@@ -375,8 +428,7 @@ private JsonNode createServiceVesselSchedules(String vesselIMONumber, String ves
vesselSchedule.put("vesselName", vesselName);
vesselSchedule.set(
"transportCalls",
- createTransportCalls("TCREF1", "2104N", "2104S",
- "SR12345A", "SR45678A", "NLAMS"));
+ createTransportCalls("TCREF1", "2104N", "2104S", "SR12345A", "SR45678A", "NLAMS"));
vesselSchedulesArrayNode.add(vesselSchedule);
return vesselSchedulesArrayNode;
}
diff --git a/ovs/src/main/resources/standards/ovs/messages/ovs-300-response-wrong-attribute-values.json b/ovs/src/test/resources/messages/ovs-300-response-wrong-attribute-values.json
similarity index 100%
rename from ovs/src/main/resources/standards/ovs/messages/ovs-300-response-wrong-attribute-values.json
rename to ovs/src/test/resources/messages/ovs-300-response-wrong-attribute-values.json
diff --git a/ovs/src/main/resources/standards/ovs/messages/ovs-300-response-wrong-date-times.json b/ovs/src/test/resources/messages/ovs-300-response-wrong-date-times.json
similarity index 100%
rename from ovs/src/main/resources/standards/ovs/messages/ovs-300-response-wrong-date-times.json
rename to ovs/src/test/resources/messages/ovs-300-response-wrong-date-times.json
diff --git a/ovs/src/main/resources/standards/ovs/messages/ovs-300-response-wrong-structure.json b/ovs/src/test/resources/messages/ovs-300-response-wrong-structure.json
similarity index 100%
rename from ovs/src/main/resources/standards/ovs/messages/ovs-300-response-wrong-structure.json
rename to ovs/src/test/resources/messages/ovs-300-response-wrong-structure.json
From a75e892bdd79811b88e686983193c83e9b38d541 Mon Sep 17 00:00:00 2001
From: preetamnpr <128618622+preetamnpr@users.noreply.github.com>
Date: Wed, 4 Dec 2024 16:09:39 +0100
Subject: [PATCH 7/8] SD-1823 reverted core changes by implementing the wrapper
method.
---
.../core/check/JsonAttributeBasedCheck.java | 5 +-
.../standards/ovs/checks/OvsChecks.java | 166 +++++++++---------
.../ovs/{ => checks}/OvsChecksTest.java | 53 +++---
3 files changed, 105 insertions(+), 119 deletions(-)
rename ovs/src/test/java/org/dcsa/conformance/standards/ovs/{ => checks}/OvsChecksTest.java (89%)
diff --git a/core/src/main/java/org/dcsa/conformance/core/check/JsonAttributeBasedCheck.java b/core/src/main/java/org/dcsa/conformance/core/check/JsonAttributeBasedCheck.java
index 1dae74ec..1a496759 100644
--- a/core/src/main/java/org/dcsa/conformance/core/check/JsonAttributeBasedCheck.java
+++ b/core/src/main/java/org/dcsa/conformance/core/check/JsonAttributeBasedCheck.java
@@ -9,16 +9,15 @@
import java.util.function.Predicate;
import java.util.stream.Stream;
-import lombok.Getter;
import lombok.NonNull;
import org.dcsa.conformance.core.traffic.ConformanceExchange;
import org.dcsa.conformance.core.traffic.HttpMessageType;
-public class JsonAttributeBasedCheck extends ActionCheck {
+class JsonAttributeBasedCheck extends ActionCheck {
private final String standardsVersion;
- @Getter private final List validators;
+ private final List validators;
JsonAttributeBasedCheck(
String titlePrefix,
diff --git a/ovs/src/main/java/org/dcsa/conformance/standards/ovs/checks/OvsChecks.java b/ovs/src/main/java/org/dcsa/conformance/standards/ovs/checks/OvsChecks.java
index 97842aea..a0c101a7 100644
--- a/ovs/src/main/java/org/dcsa/conformance/standards/ovs/checks/OvsChecks.java
+++ b/ovs/src/main/java/org/dcsa/conformance/standards/ovs/checks/OvsChecks.java
@@ -26,99 +26,99 @@
@UtilityClass
public class OvsChecks {
- public static ActionCheck responseContentChecks(
- UUID matched, String standardVersion, Supplier sspSupplier) {
-
+ public List buildResponseContentChecks(Map filterParametersMap) {
var checks = new ArrayList();
-
checks.add(
- JsonAttribute.customValidator(
- "Every response received during a conformance test must contain schedules",
- body -> {
- Set validationErrors = new LinkedHashSet<>();
- checkServiceSchedulesExist(body)
- .forEach(
- validationError ->
- validationErrors.add(
- "CheckServiceSchedules failed: %s".formatted(validationError)));
- if (validationErrors.isEmpty()) {
- return Set.of();
- }
- return validationErrors;
- }));
+ JsonAttribute.customValidator(
+ "Every response received during a conformance test must contain schedules",
+ body -> {
+ Set validationErrors = new LinkedHashSet<>();
+ checkServiceSchedulesExist(body)
+ .forEach(
+ validationError ->
+ validationErrors.add(
+ "CheckServiceSchedules failed: %s".formatted(validationError)));
+ if (validationErrors.isEmpty()) {
+ return Set.of();
+ }
+ return validationErrors;
+ }));
checks.add(
- JsonAttribute.customValidator(
- "If present, at least schedule attribute must match the corresponding query parameters",
- body -> {
- Map filterParametersMap = sspSupplier.get().getMap();
- Set validationErrors = new LinkedHashSet<>();
- checkThatScheduleValuesMatchParamValues(body, filterParametersMap)
- .forEach(
- validationError ->
- validationErrors.add(
- "Schedule Param Value Validation failed: %s"
- .formatted(validationError)));
- return validationErrors;
- }));
+ JsonAttribute.customValidator(
+ "If present, at least one schedule attribute must match the corresponding query parameters",
+ body -> {
+ Set validationErrors = new LinkedHashSet<>();
+ checkThatScheduleValuesMatchParamValues(body, filterParametersMap)
+ .forEach(
+ validationError ->
+ validationErrors.add(
+ "Schedule Param Value Validation failed: %s"
+ .formatted(validationError)));
+ return validationErrors;
+ }));
checks.add(
- JsonAttribute.customValidator(
- "Check eventDateTime is greater than startDate filter parameter if present",
- body -> {
- Set validationErrors = new LinkedHashSet<>();
- Map filterParametersMap = sspSupplier.get().getMap();
- validateDate(
- body, filterParametersMap, OvsFilterParameter.START_DATE, LocalDate::isBefore)
- .forEach(
- validationError ->
- validationErrors.add(
- "Start Date EventDateTime validation failed: %s"
- .formatted(validationError)));
- return validationErrors;
- }));
+ JsonAttribute.customValidator(
+ "Check eventDateTime is greater than startDate filter parameter if present",
+ body -> {
+ Set validationErrors = new LinkedHashSet<>();
+ validateDate(
+ body, filterParametersMap, OvsFilterParameter.START_DATE, LocalDate::isBefore)
+ .forEach(
+ validationError ->
+ validationErrors.add(
+ "Start Date EventDateTime validation failed: %s"
+ .formatted(validationError)));
+ return validationErrors;
+ }));
checks.add(
- JsonAttribute.customValidator(
- "Check eventDateTime is less than endDate filter parameter if present",
- body -> {
- Set validationErrors = new LinkedHashSet<>();
- Map filterParametersMap = sspSupplier.get().getMap();
- validateDate(
- body, filterParametersMap, OvsFilterParameter.END_DATE, LocalDate::isAfter)
- .forEach(
- validationError ->
- validationErrors.add(
- "EndDate EventDateTime validation failed: %s"
- .formatted(validationError)));
- return validationErrors;
- }));
+ JsonAttribute.customValidator(
+ "Check eventDateTime is less than endDate filter parameter if present",
+ body -> {
+ Set validationErrors = new LinkedHashSet<>();
+ validateDate(
+ body, filterParametersMap, OvsFilterParameter.END_DATE, LocalDate::isAfter)
+ .forEach(
+ validationError ->
+ validationErrors.add(
+ "EndDate EventDateTime validation failed: %s"
+ .formatted(validationError)));
+ return validationErrors;
+ }));
checks.add(
- JsonAttribute.customValidator(
- "Check transportCallReference is unique across each service schedules",
- OvsChecks::validateUniqueTransportCallReference));
+ JsonAttribute.customValidator(
+ "Check transportCallReference is unique across each service schedules",
+ OvsChecks::validateUniqueTransportCallReference));
checks.add(
- JsonAttribute.customValidator(
- "Validate limit exists and the number of schedules does not exceed the limit",
- body -> {
- Optional> limitParam =
- sspSupplier.get().getMap().entrySet().stream()
- .filter(e -> e.getKey().equals(OvsFilterParameter.LIMIT))
- .findFirst();
+ JsonAttribute.customValidator(
+ "Validate limit exists and the number of schedules does not exceed the limit",
+ body -> {
+ Optional> limitParam =
+ filterParametersMap.entrySet().stream()
+ .filter(e -> e.getKey().equals(OvsFilterParameter.LIMIT))
+ .findFirst();
- if (limitParam.isPresent()) {
- int expectedLimit = Integer.parseInt(limitParam.get().getValue().trim());
- if (body.size() > expectedLimit) {
- return Set.of(
- "The number of service schedules exceeds the limit parameter: "
- + expectedLimit);
- }
- }
- return Set.of();
- }));
+ if (limitParam.isPresent()) {
+ int expectedLimit = Integer.parseInt(limitParam.get().getValue().trim());
+ if (body.size() > expectedLimit) {
+ return Set.of(
+ "The number of service schedules exceeds the limit parameter: "
+ + expectedLimit);
+ }
+ }
+ return Set.of();
+ }));
+ return checks;
+ }
+ public static ActionCheck responseContentChecks(
+ UUID matched, String standardVersion, Supplier sspSupplier) {
+ Map filterParametersMap = sspSupplier.get().getMap();
+ var checks = buildResponseContentChecks(filterParametersMap);
return JsonAttribute.contentChecks(
OvsRole::isPublisher, matched, HttpMessageType.RESPONSE, standardVersion, checks);
}
@@ -302,9 +302,9 @@ private Stream> findMatchingNodes(
}
public Set checkServiceSchedulesExist(JsonNode body) {
- Set validationErrors = new LinkedHashSet<>();
+
if (body == null || body.isMissingNode() || body.isNull()) {
- validationErrors.add("Response body is missing or null.");
+ return Set.of("Response body is missing or null.");
} else {
boolean hasVesselSchedules =
findMatchingNodes(body, "*/vesselSchedules")
@@ -314,9 +314,9 @@ public Set checkServiceSchedulesExist(JsonNode body) {
&& node.getValue().isArray()
&& !node.getValue().isEmpty());
if (!hasVesselSchedules) {
- validationErrors.add("Response doesn't have schedules.");
+ return Set.of("Response doesn't have schedules.");
}
}
- return validationErrors;
+ return Set.of();
}
}
diff --git a/ovs/src/test/java/org/dcsa/conformance/standards/ovs/OvsChecksTest.java b/ovs/src/test/java/org/dcsa/conformance/standards/ovs/checks/OvsChecksTest.java
similarity index 89%
rename from ovs/src/test/java/org/dcsa/conformance/standards/ovs/OvsChecksTest.java
rename to ovs/src/test/java/org/dcsa/conformance/standards/ovs/checks/OvsChecksTest.java
index 4203a04a..c5d3e6dd 100644
--- a/ovs/src/test/java/org/dcsa/conformance/standards/ovs/OvsChecksTest.java
+++ b/ovs/src/test/java/org/dcsa/conformance/standards/ovs/checks/OvsChecksTest.java
@@ -1,27 +1,22 @@
-package org.dcsa.conformance.standards.ovs;
+package org.dcsa.conformance.standards.ovs.checks;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
-import org.dcsa.conformance.core.check.ActionCheck;
-import org.dcsa.conformance.core.check.JsonAttributeBasedCheck;
import org.dcsa.conformance.core.toolkit.JsonToolkit;
-import org.dcsa.conformance.standards.ovs.checks.OvsChecks;
import org.dcsa.conformance.standards.ovs.party.OvsFilterParameter;
-import org.dcsa.conformance.standards.ovs.party.SuppliedScenarioParameters;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import java.io.IOException;
import java.time.LocalDate;
import java.util.*;
-import java.util.function.Supplier;
import java.util.stream.Stream;
-import static org.junit.jupiter.api.Assertions.*;
-public class OvsChecksTest {
+import static org.dcsa.conformance.core.toolkit.JsonToolkit.OBJECT_MAPPER;
+import static org.junit.jupiter.api.Assertions.*;
- private final ObjectMapper objectMapper = new ObjectMapper();
+class OvsChecksTest {
private JsonNode serviceNodes;
@@ -73,16 +68,9 @@ void testResponseContentChecks_withWrongStructureResponse() {
private Set executeResponseChecks(
Map ovsFilterParameterStringMap, JsonNode serviceNodes) {
- UUID matchedId = UUID.randomUUID();
- String standardVersion = "3.0.0";
- Supplier sspSupplier =
- () -> SuppliedScenarioParameters.fromMap(ovsFilterParameterStringMap);
Set issues = new HashSet<>();
- ActionCheck actionCheck =
- OvsChecks.responseContentChecks(matchedId, standardVersion, sspSupplier);
- ((JsonAttributeBasedCheck) actionCheck)
- .getValidators()
+ OvsChecks.buildResponseContentChecks(ovsFilterParameterStringMap)
.forEach(
validator -> {
issues.addAll(validator.validate(serviceNodes));
@@ -325,7 +313,7 @@ void testValidateUniqueTransportCallReference_duplicate() {
JsonNode transportCalls = vesselSchedules.get(0).get("transportCalls");
ObjectNode newTransportCall =
- new ObjectMapper().createObjectNode().put("transportCallReference", "TCREF1");
+ OBJECT_MAPPER.createObjectNode().put("transportCallReference", "TCREF1");
((ArrayNode) transportCalls).add(newTransportCall);
Set result = OvsChecks.validateUniqueTransportCallReference(serviceNodes);
@@ -342,14 +330,14 @@ void testValidateUniqueTransportCallReference_noVesselSchedules() {
@Test
void testFindMatchingNodes_rootMatch() {
- JsonNode root = objectMapper.createObjectNode().put("value", "test");
+ JsonNode root = OBJECT_MAPPER.createObjectNode().put("value", "test");
Stream result = OvsChecks.findMatchingNodes(root, "/").map(Map.Entry::getValue);
assertEquals(1, result.count());
}
@Test
void testFindMatchingNodes_arrayMatch() throws IOException {
- JsonNode root = objectMapper.readTree("[{\"a\": 1}, {\"b\": 2}]");
+ JsonNode root = OBJECT_MAPPER.readTree("[{\"a\": 1}, {\"b\": 2}]");
Stream result = OvsChecks.findMatchingNodes(root, "*").map(Map.Entry::getValue);
;
assertEquals(2, result.count());
@@ -357,7 +345,7 @@ void testFindMatchingNodes_arrayMatch() throws IOException {
@Test
void testFindMatchingNodes_emptyArrayMatch() throws IOException {
- JsonNode root = objectMapper.readTree("[]");
+ JsonNode root = OBJECT_MAPPER.readTree("[]");
Stream result = OvsChecks.findMatchingNodes(root, "*").map(Map.Entry::getValue);
;
assertEquals(0, result.count());
@@ -365,7 +353,7 @@ void testFindMatchingNodes_emptyArrayMatch() throws IOException {
@Test
void testCheckServiceSchedulesExist_emptyServiceSchedules() {
- JsonNode body = objectMapper.createArrayNode();
+ JsonNode body = OBJECT_MAPPER.createArrayNode();
Set result = OvsChecks.checkServiceSchedulesExist(body);
assertEquals(1, result.size());
}
@@ -390,7 +378,7 @@ void testValidateDate_invalidDateFormat() {
.get("timestamps");
ObjectNode invalidTimeStamp =
- new ObjectMapper().createObjectNode().put("eventDateTime", "TCREF1");
+ OBJECT_MAPPER.createObjectNode().put("eventDateTime", "TCREF1");
((ArrayNode) timeStamps).add(invalidTimeStamp);
Set result =
@@ -405,13 +393,12 @@ private JsonNode createServiceNodes(
String carrierServiceCode,
String universalServiceReference,
JsonNode vesselSchedules) {
- ObjectMapper objectMapper = new ObjectMapper();
// Create the root ArrayNode
- ArrayNode rootArrayNode = objectMapper.createArrayNode();
+ ArrayNode rootArrayNode = OBJECT_MAPPER.createArrayNode();
// Create the first ObjectNode
- ObjectNode firstObjectNode = objectMapper.createObjectNode();
+ ObjectNode firstObjectNode = OBJECT_MAPPER.createObjectNode();
firstObjectNode.put("carrierServiceName", carrierServiceName);
firstObjectNode.put("carrierServiceCode", carrierServiceCode);
firstObjectNode.put("universalServiceReference", universalServiceReference);
@@ -422,8 +409,8 @@ private JsonNode createServiceNodes(
}
private JsonNode createServiceVesselSchedules(String vesselIMONumber, String vesselName) {
- ArrayNode vesselSchedulesArrayNode = objectMapper.createArrayNode();
- ObjectNode vesselSchedule = objectMapper.createObjectNode();
+ ArrayNode vesselSchedulesArrayNode = OBJECT_MAPPER.createArrayNode();
+ ObjectNode vesselSchedule = OBJECT_MAPPER.createObjectNode();
vesselSchedule.put("vesselIMONumber", vesselIMONumber);
vesselSchedule.put("vesselName", vesselName);
vesselSchedule.set(
@@ -441,8 +428,8 @@ private JsonNode createTransportCalls(
String universalExportVoyageReference,
String UNLocationCode) {
// Create the transportCalls ArrayNode for the vesselSchedule
- ArrayNode transportCallsArrayNode = objectMapper.createArrayNode();
- ObjectNode transportCall = objectMapper.createObjectNode();
+ ArrayNode transportCallsArrayNode = OBJECT_MAPPER.createArrayNode();
+ ObjectNode transportCall = OBJECT_MAPPER.createObjectNode();
transportCall.put("transportCallReference", transportCallReference);
transportCall.put("carrierImportVoyageNumber", carrierImportVoyageNumber);
transportCall.put("carrierExportVoyageNumber", carrierExportVoyageNumber);
@@ -450,7 +437,7 @@ private JsonNode createTransportCalls(
transportCall.put("universalExportVoyageReference", universalExportVoyageReference);
// Create the location ObjectNode for the first transportCall
- ObjectNode location = objectMapper.createObjectNode();
+ ObjectNode location = OBJECT_MAPPER.createObjectNode();
location.put("UNLocationCode", UNLocationCode);
transportCall.set("location", location);
transportCall.set("timestamps", createTimestamps());
@@ -460,7 +447,7 @@ private JsonNode createTransportCalls(
private JsonNode createEventDateTime(String eventDateTime) {
// Create a timestamp for timestamps ArrayNode
- ObjectNode timestamp = objectMapper.createObjectNode();
+ ObjectNode timestamp = OBJECT_MAPPER.createObjectNode();
timestamp.put("eventTypeCode", "ARRI");
timestamp.put("eventClassifierCode", "PLN");
timestamp.put("eventDateTime", eventDateTime);
@@ -469,7 +456,7 @@ private JsonNode createEventDateTime(String eventDateTime) {
private JsonNode createTimestamps() {
// Create the timestamps ArrayNode
- ArrayNode timestampsArrayNode = objectMapper.createArrayNode();
+ ArrayNode timestampsArrayNode = OBJECT_MAPPER.createArrayNode();
timestampsArrayNode.add(createEventDateTime("2024-07-21T10:00:00Z"));
timestampsArrayNode.add(createEventDateTime("2024-07-22T10:00:00Z"));
timestampsArrayNode.add(createEventDateTime("2024-07-23T10:00:00Z"));
From 738fd35d6262509a6297b853bbd28d33cd4ecd6e Mon Sep 17 00:00:00 2001
From: preetamnpr <128618622+preetamnpr@users.noreply.github.com>
Date: Wed, 4 Dec 2024 21:21:51 +0100
Subject: [PATCH 8/8] SD-1823 handled null sspSupplier and removed
validationErrors condition to return empty set.
---
.../dcsa/conformance/standards/ovs/checks/OvsChecks.java | 7 +++----
1 file changed, 3 insertions(+), 4 deletions(-)
diff --git a/ovs/src/main/java/org/dcsa/conformance/standards/ovs/checks/OvsChecks.java b/ovs/src/main/java/org/dcsa/conformance/standards/ovs/checks/OvsChecks.java
index a0c101a7..b02fbb4d 100644
--- a/ovs/src/main/java/org/dcsa/conformance/standards/ovs/checks/OvsChecks.java
+++ b/ovs/src/main/java/org/dcsa/conformance/standards/ovs/checks/OvsChecks.java
@@ -38,9 +38,6 @@ public List buildResponseContentChecks(Map
validationErrors.add(
"CheckServiceSchedules failed: %s".formatted(validationError)));
- if (validationErrors.isEmpty()) {
- return Set.of();
- }
return validationErrors;
}));
@@ -117,7 +114,9 @@ public List buildResponseContentChecks(Map sspSupplier) {
- Map filterParametersMap = sspSupplier.get().getMap();
+ Map filterParametersMap = sspSupplier.get() != null
+ ? sspSupplier.get().getMap()
+ : Map.of();
var checks = buildResponseContentChecks(filterParametersMap);
return JsonAttribute.contentChecks(
OvsRole::isPublisher, matched, HttpMessageType.RESPONSE, standardVersion, checks);