From 780c4d0b3a534058bfbc0b2ee0454839b78ad4b7 Mon Sep 17 00:00:00 2001 From: Kai Hudalla Date: Wed, 3 Sep 2025 16:57:53 +0200 Subject: [PATCH] Use an exception to indicate validation errors Replaced ValidationResult with a Java runtime exception that is being thrown in case of validation errors. This is more in line with the principles of exception handling in Java and allows for better error propagation and usage of existing tooling around exceptions, e.g. in unit tests. --- .../transport/builder/UMessageBuilder.java | 13 +- .../validate/UAttributesValidator.java | 269 ++++++++---------- .../uuid/validate/UuidValidator.java | 69 +++-- .../validation/ValidationException.java | 64 +++++ .../validation/ValidationResult.java | 108 ------- .../uprotocol/validation/ValidationUtils.java | 52 ++++ .../communication/TestUTransport.java | 13 +- .../validator/UAttributeValidatorTest.java | 163 ++++++----- .../uuid/factory/UUIDFactoryTest.java | 3 - .../uuid/validator/UuidValidatorTest.java | 76 +++-- .../validation/ValidationExceptionTest.java | 45 +++ 11 files changed, 476 insertions(+), 399 deletions(-) create mode 100644 src/main/java/org/eclipse/uprotocol/validation/ValidationException.java delete mode 100644 src/main/java/org/eclipse/uprotocol/validation/ValidationResult.java create mode 100644 src/main/java/org/eclipse/uprotocol/validation/ValidationUtils.java create mode 100644 src/test/java/org/eclipse/uprotocol/validation/ValidationExceptionTest.java diff --git a/src/main/java/org/eclipse/uprotocol/transport/builder/UMessageBuilder.java b/src/main/java/org/eclipse/uprotocol/transport/builder/UMessageBuilder.java index 3d50c0b5..5f73da0d 100644 --- a/src/main/java/org/eclipse/uprotocol/transport/builder/UMessageBuilder.java +++ b/src/main/java/org/eclipse/uprotocol/transport/builder/UMessageBuilder.java @@ -13,6 +13,7 @@ package org.eclipse.uprotocol.transport.builder; import org.eclipse.uprotocol.v1.UUri; +import org.eclipse.uprotocol.validation.ValidationException; import org.eclipse.uprotocol.v1.UMessage; import org.eclipse.uprotocol.v1.UAttributes; import org.eclipse.uprotocol.v1.UCode; @@ -139,8 +140,10 @@ public static UMessageBuilder response(UUri source, UUri sink, UUID reqid) { throw new IllegalArgumentException("sink must be a response and source must be an rpc method."); } - if (UuidValidator.Validators.UPROTOCOL.validator().validate(reqid).getCode() != UCode.OK) { - throw new IllegalArgumentException("reqid is not a valid UUID."); + try { + UuidValidator.Validators.UPROTOCOL.validator().validate(reqid); + } catch (ValidationException e) { + throw new IllegalArgumentException("reqid is not a valid UUID.", e); } return new UMessageBuilder(source, UuidFactory.Factories.UPROTOCOL.factory().create(), @@ -159,8 +162,10 @@ public static UMessageBuilder response(UAttributes request) { Objects.requireNonNull(request, "request cannot be null."); // Validate the request - if (UAttributesValidator.Validators.REQUEST.validator().validate(request).isFailure()) { - throw new IllegalArgumentException("request must contain valid request attributes."); + try { + UAttributesValidator.Validators.REQUEST.validator().validate(request); + } catch (ValidationException e) { + throw new IllegalArgumentException("request is not a valid request attributes.", e); } return new UMessageBuilder( diff --git a/src/main/java/org/eclipse/uprotocol/transport/validate/UAttributesValidator.java b/src/main/java/org/eclipse/uprotocol/transport/validate/UAttributesValidator.java index d6350924..7e0d6462 100644 --- a/src/main/java/org/eclipse/uprotocol/transport/validate/UAttributesValidator.java +++ b/src/main/java/org/eclipse/uprotocol/transport/validate/UAttributesValidator.java @@ -19,12 +19,11 @@ import org.eclipse.uprotocol.v1.UPriority; import org.eclipse.uprotocol.v1.UUri; import org.eclipse.uprotocol.v1.UUID; -import org.eclipse.uprotocol.validation.ValidationResult; +import org.eclipse.uprotocol.validation.ValidationException; +import org.eclipse.uprotocol.validation.ValidationUtils; import java.util.Objects; import java.util.Optional; -import java.util.stream.Collectors; -import java.util.stream.Stream; /** * {@link UAttributes} is the class that defines the Payload. It is the place @@ -65,20 +64,25 @@ public static UAttributesValidator getValidator(UAttributes attribute) { } /** - * Take a {@link UAttributes} object and run validations. + * Checks if a given set of attributes complies with the rules specified for + * the type of message they describe. * - * @param attributes The UAttriubes to validate. - * @return Returns a {@link ValidationResult} that is success or failed with a - * message containing all validation - * errors for - * invalid configurations. + * @param attributes The attributes to validate. + * @throws ValidationException if the attributes are not consistent with the rules specified for the message type. */ - public ValidationResult validate(UAttributes attributes) { - final String errorMessage = Stream.of(validateType(attributes), - validateTtl(attributes), validateSink(attributes), validatePriority(attributes), - validatePermissionLevel(attributes), validateReqId(attributes), validateId(attributes)) - .filter(ValidationResult::isFailure).map(ValidationResult::getMessage).collect(Collectors.joining(",")); - return errorMessage.isBlank() ? ValidationResult.success() : ValidationResult.failure(errorMessage); + public void validate(UAttributes attributes) { + final var errors = ValidationUtils.collectErrors(attributes, + this::validateType, + this::validateTtl, + this::validateSink, + this::validatePriority, + this::validatePermissionLevel, + this::validateReqId, + this::validateId + ); + if (!errors.isEmpty()) { + throw new ValidationException(errors); + } } /** @@ -113,15 +117,12 @@ public boolean isExpired(UAttributes uAttributes) { * * @param attributes UAttributes object containing the message time to live * configuration to validate. - * @return Returns a {@link ValidationResult} that is success or failed with a - * failure message. + * @throws ValidationException if TTL <= 0. */ - public ValidationResult validateTtl(UAttributes attributes) { + public void validateTtl(UAttributes attributes) { int ttl = attributes.getTtl(); if (attributes.hasTtl() && ttl <= 0) { - return ValidationResult.failure(String.format("Invalid TTL [%s]", ttl)); - } else { - return ValidationResult.success(); + throw new ValidationException(String.format("Invalid TTL [%s]", ttl)); } } @@ -130,10 +131,9 @@ public ValidationResult validateTtl(UAttributes attributes) { * Validate the sink UriPart. * * @param attributes UAttributes object containing the sink to validate. - * @return Returns a {@link ValidationResult} that is success or failed with a - * failure message. + * @throws ValidationException if sink is invalid. */ - public abstract ValidationResult validateSink(UAttributes attributes); + public abstract void validateSink(UAttributes attributes); /** * Validate the permissionLevel for the default case. If the UAttributes does @@ -142,14 +142,11 @@ public ValidationResult validateTtl(UAttributes attributes) { * * @param attributes UAttributes object containing the permission level to * validate. - * @return Returns a ValidationResult indicating if the permissionLevel is valid - * or not. + * @throws ValidationException if the permission level is invalid. */ - public ValidationResult validatePermissionLevel(UAttributes attributes) { - if (!attributes.hasPermissionLevel() || attributes.getPermissionLevel() > 0) { - return ValidationResult.success(); - } else { - return ValidationResult.failure("Invalid Permission Level"); + public void validatePermissionLevel(UAttributes attributes) { + if (attributes.hasPermissionLevel() && attributes.getPermissionLevel() <= 0) { + throw new ValidationException("Invalid Permission Level"); } } @@ -158,25 +155,24 @@ public ValidationResult validatePermissionLevel(UAttributes attributes) { * should have a reqid. * * @param attributes Attributes object containing the request id to validate. - * @return Returns a {@link ValidationResult} that is success or failed with a - * failure message. + * @throws ValidationException if the request id is invalid. */ - public ValidationResult validateReqId(UAttributes attributes) { - return attributes.hasReqid() ? ValidationResult.failure("Message should not have a reqid") - : ValidationResult.success(); + public void validateReqId(UAttributes attributes) { + if (attributes.hasReqid()) { + throw new ValidationException("Message should not have a reqid"); + } } /** * Validate the priority value to ensure it is one of the known CS values. * * @param attributes Attributes object containing the Priority to validate. - * @return Returns a {@link ValidationResult} that is success or failed with a - * failure message. + * @throws ValidationException if the priority is invalid. */ - public ValidationResult validatePriority(UAttributes attributes) { - return attributes.getPriority().getNumber() >= UPriority.UPRIORITY_CS1_VALUE ? ValidationResult.success() - : ValidationResult.failure( - String.format("Invalid UPriority [%s]", attributes.getPriority().name())); + public void validatePriority(UAttributes attributes) { + if (attributes.getPriority().getNumber() < UPriority.UPRIORITY_CS1_VALUE) { + throw new ValidationException(String.format("Invalid UPriority [%s]", attributes.getPriority().name())); + } } /** @@ -185,17 +181,14 @@ public ValidationResult validatePriority(UAttributes attributes) { * the ValidationResult is failed. * * @param attributes Attributes object containing the id to validate. - * @return Returns a {@link ValidationResult} that is success or failed with a - * failure message. + * @throws ValidationException if the message ID is invalid. */ - public ValidationResult validateId(UAttributes attributes) { + public void validateId(UAttributes attributes) { if (!attributes.hasId()) { - return ValidationResult.failure("Missing id"); + throw new ValidationException("Missing id"); } if (!UuidUtils.isUuid(attributes.getId())) { - return ValidationResult.failure("Attributes must contain valid uProtocol UUID in id property"); - } else { - return ValidationResult.success(); + throw new ValidationException("Attributes must contain valid uProtocol UUID in id property"); } } @@ -203,10 +196,9 @@ public ValidationResult validateId(UAttributes attributes) { * Validate the {@link UMessageType} attribute, it is required. * * @param attributes UAttributes object containing the message type to validate. - * @return Returns a {@link ValidationResult} that is success or failed with a - * failure message. + * @throws ValidationException if this validator is inappropriate for the message's type. */ - public abstract ValidationResult validateType(UAttributes attributes); + public abstract void validateType(UAttributes attributes); /** * Validators Factory. Example: @@ -241,29 +233,31 @@ private static class Publish extends UAttributesValidator { * the correct type. * * @param attributes UAttributes object containing the message type to validate. - * @return Returns a {@link ValidationResult} that is success or failed with a + * @return Returns a {@link ValidationException} that is success or failed with a * failure message. */ @Override - public ValidationResult validateType(UAttributes attributes) { - return UMessageType.UMESSAGE_TYPE_PUBLISH == attributes.getType() ? ValidationResult.success() - : ValidationResult.failure( - String.format("Wrong Attribute Type [%s]", attributes.getType())); + public void validateType(UAttributes attributes) { + if (UMessageType.UMESSAGE_TYPE_PUBLISH != attributes.getType()) { + throw new ValidationException( + String.format("Wrong Attribute Type [%s]", attributes.getType())); + } } /** * Validate the sink UriPart for Publish events. Publish should not have a sink. * * @param attributes UAttributes object containing the sink to validate. - * @return Returns a {@link ValidationResult} that is success or failed with a + * @return Returns a {@link ValidationException} that is success or failed with a * failure message. */ @Override - public ValidationResult validateSink(UAttributes attributes) { - return attributes.hasSink() ? ValidationResult.failure("Sink should not be present") - : ValidationResult.success(); + public void validateSink(UAttributes attributes) { + if (attributes.hasSink()) { + throw new ValidationException("Sink should not be present"); + } } - + @Override public String toString() { return "UAttributesValidator.Publish"; @@ -277,71 +271,64 @@ public String toString() { private static class Request extends UAttributesValidator { /** + * {@inheritDoc} + * * Validates that attributes for a message meant for an RPC request has the * correct type. - * - * @param attributes UAttributes object containing the message type to validate. - * @return Returns a {@link ValidationResult} that is success or failed with a - * failure message. */ @Override - public ValidationResult validateType(UAttributes attributes) { - return UMessageType.UMESSAGE_TYPE_REQUEST == attributes.getType() ? ValidationResult.success() - : ValidationResult.failure( - String.format("Wrong Attribute Type [%s]", attributes.getType())); + public void validateType(UAttributes attributes) { + if (UMessageType.UMESSAGE_TYPE_REQUEST != attributes.getType()) { + throw new ValidationException( + String.format("Wrong Attribute Type [%s]", attributes.getType())); + } } /** + * {@inheritDoc} + * * Validates that attributes for a message meant for an RPC request has a * destination sink. * In the case of an RPC request, the sink is required. - * - * @param attributes UAttributes object containing the sink to validate. - * @return Returns a {@link ValidationResult} that is success or failed with a - * failure message. */ @Override - public ValidationResult validateSink(UAttributes attributes) { + public void validateSink(UAttributes attributes) { if (!attributes.hasSink()) { - return ValidationResult.failure("Missing Sink"); + throw new ValidationException("Missing Sink"); + } + if (!UriValidator.isRpcMethod(attributes.getSink())) { + throw new ValidationException("Invalid Sink Uri"); } - return UriValidator.isRpcMethod(attributes.getSink()) ? ValidationResult.success() - : ValidationResult.failure("Invalid Sink Uri"); } /** + * {@inheritDoc} + * * Validate the time to live configuration. * In the case of an RPC request, the time to live is required. - * - * @param attributes UAttributes object containing the time to live to validate. - * @return Returns a {@link ValidationResult} that is success or failed with a - * failure message. */ @Override - public ValidationResult validateTtl(UAttributes attributes) { + public void validateTtl(UAttributes attributes) { if (!attributes.hasTtl()) { - return ValidationResult.failure("Missing TTL"); + throw new ValidationException("Missing TTL"); } int ttl = attributes.getTtl(); if (ttl <= 0) { - return ValidationResult.failure(String.format("Invalid TTL [%s]", ttl)); - } else { - return ValidationResult.success(); + throw new ValidationException(String.format("Invalid TTL [%s]", ttl)); } } /** + * {@inheritDoc} + * * Validate the priority value to ensure it is one of the known CS values - * - * @param attributes Attributes object containing the Priority to validate. - * @return Returns a {@link ValidationResult} that is success or failed with a - * failure message. */ @Override - public ValidationResult validatePriority(UAttributes attributes) { - return attributes.getPriority().getNumber() >= UPriority.UPRIORITY_CS4_VALUE ? ValidationResult.success() - : ValidationResult.failure( - String.format("Invalid UPriority [%s]", attributes.getPriority().name())); + public void validatePriority(UAttributes attributes) { + if (attributes.getPriority().getNumber() < UPriority.UPRIORITY_CS4_VALUE) { + throw new ValidationException( + String.format("Invalid UPriority [%s]", attributes.getPriority().name())); + } } @Override @@ -357,73 +344,64 @@ public String toString() { private static class Response extends UAttributesValidator { /** + * {@inheritDoc} + * * Validates that attributes for a message meant for an RPC response has the * correct type. - * - * @param attributes UAttributes object containing the message type to validate. - * @return Returns a {@link ValidationResult} that is success or failed with a - * failure message. */ @Override - public ValidationResult validateType(UAttributes attributes) { - return UMessageType.UMESSAGE_TYPE_RESPONSE == attributes.getType() ? ValidationResult.success() - : ValidationResult.failure( - String.format("Wrong Attribute Type [%s]", attributes.getType())); + public void validateType(UAttributes attributes) { + if (UMessageType.UMESSAGE_TYPE_RESPONSE != attributes.getType()) { + throw new ValidationException( + String.format("Wrong Attribute Type [%s]", attributes.getType())); + } } /** + * {@inheritDoc} + * * Validates that attributes for a message meant for an RPC response has a * destination sink. * In the case of an RPC response, the sink is required. - * - * @param attributes UAttributes object containing the sink to validate. - * @return Returns a {@link ValidationResult} that is success or failed with a - * failure message. */ @Override - public ValidationResult validateSink(UAttributes attributes) { + public void validateSink(UAttributes attributes) { Objects.requireNonNull(attributes, "UAttributes cannot be null."); if (!attributes.hasSink() || attributes.getSink() == UUri.getDefaultInstance()) { - return ValidationResult.failure("Missing Sink"); + throw new ValidationException("Missing Sink"); + } + if (!UriValidator.isRpcResponse(attributes.getSink())) { + throw new ValidationException("Invalid Sink Uri"); } - return UriValidator.isRpcResponse(attributes.getSink()) ? ValidationResult.success() - : ValidationResult.failure("Invalid Sink Uri"); } /** + * {@inheritDoc} + * * Validate the correlationId. n the case of an RPC response, the correlation id * is required. - * - * @param attributes UAttributes object containing the correlation id to - * validate. - * @return Returns a {@link ValidationResult} that is success or failed with a - * failure message. */ @Override - public ValidationResult validateReqId(UAttributes attributes) { + public void validateReqId(UAttributes attributes) { if (!attributes.hasReqid() || attributes.getReqid() == UUID.getDefaultInstance()) { - return ValidationResult.failure("Missing correlationId"); + throw new ValidationException("Missing correlationId"); } if (!UuidUtils.isUuid(attributes.getReqid())) { - return ValidationResult.failure("Invalid correlation UUID"); - } else { - return ValidationResult.success(); + throw new ValidationException("Invalid correlation UUID"); } - } /** + * {@inheritDoc} + * * Validate the priority value to ensure it is one of the known CS values - * - * @param attributes Attributes object containing the Priority to validate. - * @return Returns a {@link ValidationResult} that is success or failed with a - * failure message. */ @Override - public ValidationResult validatePriority(UAttributes attributes) { - return attributes.getPriority().getNumber() >= UPriority.UPRIORITY_CS4_VALUE ? ValidationResult.success() - : ValidationResult.failure( - String.format("Invalid UPriority [%s]", attributes.getPriority().name())); + public void validatePriority(UAttributes attributes) { + if (attributes.getPriority().getNumber() < UPriority.UPRIORITY_CS4_VALUE) { + throw new ValidationException( + String.format("Invalid UPriority [%s]", attributes.getPriority().name())); + } } @Override @@ -439,37 +417,35 @@ public String toString() { private static class Notification extends UAttributesValidator { /** + * {@inheritDoc} + * * Validates that attributes for a message meant to Notification state changes * has the correct type. - * - * @param attributes UAttributes object containing the message type to validate. - * @return Returns a {@link ValidationResult} that is success or failed with a - * failure message. */ @Override - public ValidationResult validateType(UAttributes attributes) { - return UMessageType.UMESSAGE_TYPE_NOTIFICATION == attributes.getType() ? ValidationResult.success() - : ValidationResult.failure( - String.format("Wrong Attribute Type [%s]", attributes.getType())); + public void validateType(UAttributes attributes) { + if (UMessageType.UMESSAGE_TYPE_NOTIFICATION != attributes.getType()) { + throw new ValidationException( + String.format("Wrong Attribute Type [%s]", attributes.getType())); + } } /** + * {@inheritDoc} + * * Validates that attributes for a message meant for notifications has a * destination sink. * In the case of a notification, the sink is required. - * - * @param attributes UAttributes object containing the sink to validate. - * @return Returns a {@link ValidationResult} that is success or failed with a - * failure message. */ @Override - public ValidationResult validateSink(UAttributes attributes) { + public void validateSink(UAttributes attributes) { Objects.requireNonNull(attributes, "UAttributes cannot be null."); if (!attributes.hasSink() || attributes.getSink() == UUri.getDefaultInstance()) { - return ValidationResult.failure("Missing Sink"); + throw new ValidationException("Missing Sink"); + } + if (!UriValidator.isDefaultResourceId(attributes.getSink())) { + throw new ValidationException("Invalid Sink Uri"); } - return UriValidator.isDefaultResourceId(attributes.getSink()) ? ValidationResult.success() - : ValidationResult.failure("Invalid Sink Uri"); } @Override @@ -477,5 +453,4 @@ public String toString() { return "UAttributesValidator.Notification"; } } - } diff --git a/src/main/java/org/eclipse/uprotocol/uuid/validate/UuidValidator.java b/src/main/java/org/eclipse/uprotocol/uuid/validate/UuidValidator.java index c851f362..6dcf05b3 100644 --- a/src/main/java/org/eclipse/uprotocol/uuid/validate/UuidValidator.java +++ b/src/main/java/org/eclipse/uprotocol/uuid/validate/UuidValidator.java @@ -14,13 +14,11 @@ import org.eclipse.uprotocol.uuid.factory.UuidUtils; import org.eclipse.uprotocol.v1.UUID; -import org.eclipse.uprotocol.v1.UStatus; -import org.eclipse.uprotocol.v1.UCode; -import org.eclipse.uprotocol.validation.ValidationResult; +import org.eclipse.uprotocol.validation.ValidationException; +import org.eclipse.uprotocol.validation.ValidationUtils; +import java.util.Objects; import java.util.Optional; -import java.util.stream.Collectors; -import java.util.stream.Stream; /** * A Validator for uProtocol UUIDs. @@ -53,48 +51,63 @@ public UuidValidator validator() { } } - public UStatus validate(UUID uuid) { - final String errorMessage = Stream.of( - validateVersion(uuid), - validateTime(uuid)) - .filter(ValidationResult::isFailure) - .map(ValidationResult::getMessage) - .collect(Collectors.joining(",")); - return errorMessage.isBlank() ? ValidationResult.success().toStatus() - : UStatus.newBuilder().setCode(UCode.INVALID_ARGUMENT).setMessage(errorMessage).build(); + /** + * Checks if a UUID is valid according to the specific variant/version of the UUID. + * + * @param uuid The UUID to validate. + * @throws NullPointerException if the UUID is null. + * @throws ValidationException if the UUID is invalid. + */ + public void validate(UUID uuid) { + Objects.requireNonNull(uuid); + final var errors = ValidationUtils.collectErrors(uuid, + this::validateVersion, + this::validateTime + ); + if (!errors.isEmpty()) { + throw new ValidationException(errors); + } } - public abstract ValidationResult validateVersion(UUID uuid); + /** + * Validates the version of the UUID. + * + * @param uuid The UUID to check. + * @throws ValidationException if the UUID is invalid. + */ + public abstract void validateVersion(UUID uuid); - public ValidationResult validateTime(UUID uuid) { + public void validateTime(UUID uuid) { final Optional time = UuidUtils.getTime(uuid); - return time.isPresent() && (time.get() > 0) ? ValidationResult.success() - : ValidationResult.failure(String.format("Invalid UUID Time")); + if (time.isPresent() && (time.get() > 0)) { + return; + } + throw new ValidationException("Invalid UUID Time"); } private static class InvalidValidator extends UuidValidator { @Override - public ValidationResult validateVersion(UUID uuid) { - return ValidationResult.failure(String.format("Invalid UUID Version")); + public void validateVersion(UUID uuid) { + throw new ValidationException("Invalid UUID Version"); } } private static class UUIDv6Validator extends UuidValidator { @Override - public ValidationResult validateVersion(UUID uuid) { - return UuidUtils.isUuidv6(uuid) - ? ValidationResult.success() - : ValidationResult.failure(String.format("Not a UUIDv6 Version")); + public void validateVersion(UUID uuid) { + if (!UuidUtils.isUuidv6(uuid)) { + throw new ValidationException("Not a UUIDv6 Version"); + } } } private static class UUIDv7Validator extends UuidValidator { @Override - public ValidationResult validateVersion(UUID uuid) { - return UuidUtils.isUProtocol(uuid) - ? ValidationResult.success() - : ValidationResult.failure(String.format("Invalid UUIDv7 Version")); + public void validateVersion(UUID uuid) { + if (!UuidUtils.isUProtocol(uuid)) { + throw new ValidationException("Invalid UUIDv7 Version"); + } } } } diff --git a/src/main/java/org/eclipse/uprotocol/validation/ValidationException.java b/src/main/java/org/eclipse/uprotocol/validation/ValidationException.java new file mode 100644 index 00000000..79cc11f6 --- /dev/null +++ b/src/main/java/org/eclipse/uprotocol/validation/ValidationException.java @@ -0,0 +1,64 @@ +/** + * SPDX-FileCopyrightText: 2025 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + */ +package org.eclipse.uprotocol.validation; + +import java.util.List; +import java.util.stream.Collectors; + +/** + * Indicates an error that has occurred during validation of a uProtocol type. + */ +public class ValidationException extends RuntimeException { + + private static final long serialVersionUID = 1L; + private final List causes; + + /** + * Creates a new exception for an error message. + * + * @param message The message. + */ + public ValidationException(String message) { + super(message); + this.causes = List.of(); + } + + /** + * Creates a new exception for multiple causes. + * + * @param causes The causes for the validation failure. + */ + public ValidationException(List causes) { + super("Multiple validation errors"); + this.causes = List.copyOf(causes); + } + + @Override + public String getMessage() { + if (causes.isEmpty()) { + return super.getMessage(); + } + return causes.stream() + .map(Exception::getMessage) + .collect(Collectors.joining(",")); + } + + /** + * Gets the causes of this exception. + * + * @return An unmodifiable view of the causes. + */ + public final List getCauses() { + return causes; + } +} diff --git a/src/main/java/org/eclipse/uprotocol/validation/ValidationResult.java b/src/main/java/org/eclipse/uprotocol/validation/ValidationResult.java deleted file mode 100644 index b34c1ea4..00000000 --- a/src/main/java/org/eclipse/uprotocol/validation/ValidationResult.java +++ /dev/null @@ -1,108 +0,0 @@ -/** - * SPDX-FileCopyrightText: 2024 Contributors to the Eclipse Foundation - * - * See the NOTICE file(s) distributed with this work for additional - * information regarding copyright ownership. - * - * This program and the accompanying materials are made available under the - * terms of the Apache License Version 2.0 which is available at - * https://www.apache.org/licenses/LICENSE-2.0 - * - * SPDX-License-Identifier: Apache-2.0 - */ -package org.eclipse.uprotocol.validation; - -import org.eclipse.uprotocol.v1.UStatus; -import org.eclipse.uprotocol.v1.UCode; - -import java.util.Objects; - -/** - * Class wrapping a ValidationResult of success or failure wrapping the value of - * a google.rpc.Status. - */ -public abstract class ValidationResult { - - public static final UStatus STATUS_SUCCESS = UStatus.newBuilder().setCode(UCode.OK).setMessage("OK").build(); - - private static final ValidationResult SUCCESS = new Success(); - - private ValidationResult() { - } - - public abstract UStatus toStatus(); - - public abstract boolean isSuccess(); - - public boolean isFailure() { - return !isSuccess(); - } - - public abstract String getMessage(); - - /** - * Implementation for failure, wrapping the message. - */ - private static class Failure extends ValidationResult { - private final String message; - - private Failure(String message) { - this.message = Objects.requireNonNullElse(message, "Validation Failed."); - } - - @Override - public UStatus toStatus() { - return UStatus.newBuilder().setCode(UCode.INVALID_ARGUMENT).setMessage(message).build(); - } - - @Override - public boolean isSuccess() { - return false; - } - - @Override - public String getMessage() { - return message; - } - - @Override - public String toString() { - return "ValidationResult.Failure(" + "message='" + message + '\'' + ')'; - } - } - - /** - * Implementation for success, wrapping a UStatus with Code 0 for success. - */ - private static class Success extends ValidationResult { - - @Override - public UStatus toStatus() { - return STATUS_SUCCESS; - } - - @Override - public boolean isSuccess() { - return true; - } - - @Override - public String getMessage() { - return ""; - } - - @Override - public String toString() { - return "ValidationResult.Success()"; - } - } - - public static ValidationResult success() { - return SUCCESS; - } - - public static ValidationResult failure(String message) { - return new Failure(message); - } - -} diff --git a/src/main/java/org/eclipse/uprotocol/validation/ValidationUtils.java b/src/main/java/org/eclipse/uprotocol/validation/ValidationUtils.java new file mode 100644 index 00000000..0c84bb57 --- /dev/null +++ b/src/main/java/org/eclipse/uprotocol/validation/ValidationUtils.java @@ -0,0 +1,52 @@ +/** + * SPDX-FileCopyrightText: 2024 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + */ +package org.eclipse.uprotocol.validation; + +import java.util.Arrays; +import java.util.List; +import java.util.Objects; +import java.util.function.Consumer; +import java.util.stream.Collectors; + +public final class ValidationUtils { + + private ValidationUtils() { + // Utility class + } + + /** + * Collects all exception thrown by checks performed on a given subject. + * + * @param The type of the subject. + * @param subject The subject to perform checks on. + * @param checks The validation checks to perform. + * @return A list of exceptions thrown during validation. + */ + @SafeVarargs + public static List collectErrors(T subject, Consumer... checks) { + Objects.requireNonNull(subject); + Objects.requireNonNull(checks); + + return Arrays.stream(checks) + .map(check -> { + try { + check.accept(subject); + return null; // No exception = valid + } catch (Exception e) { + return e; + } + }) + .filter(Objects::nonNull) + .collect(Collectors.toList()); + } +} diff --git a/src/test/java/org/eclipse/uprotocol/communication/TestUTransport.java b/src/test/java/org/eclipse/uprotocol/communication/TestUTransport.java index ba2031df..410c8c90 100644 --- a/src/test/java/org/eclipse/uprotocol/communication/TestUTransport.java +++ b/src/test/java/org/eclipse/uprotocol/communication/TestUTransport.java @@ -32,7 +32,7 @@ import org.eclipse.uprotocol.v1.UMessageType; import org.eclipse.uprotocol.v1.UStatus; import org.eclipse.uprotocol.v1.UUri; -import org.eclipse.uprotocol.validation.ValidationResult; +import org.eclipse.uprotocol.validation.ValidationException; import com.google.protobuf.InvalidProtocolBufferException; @@ -85,9 +85,16 @@ public TestUTransport(UUri source) { @Override public CompletionStage send(UMessage message) { + if (message == null) { + return CompletableFuture.completedFuture(UStatus.newBuilder() + .setCode(UCode.INVALID_ARGUMENT) + .setMessage("Message cannot be null") + .build()); + } UAttributesValidator validator = UAttributesValidator.getValidator(message.getAttributes()); - - if ( (message == null) || validator.validate(message.getAttributes()) != ValidationResult.success()) { + try { + validator.validate(message.getAttributes()); + } catch (ValidationException e) { return CompletableFuture.completedFuture(UStatus.newBuilder() .setCode(UCode.INVALID_ARGUMENT) .setMessage("Invalid message attributes") diff --git a/src/test/java/org/eclipse/uprotocol/transport/validator/UAttributeValidatorTest.java b/src/test/java/org/eclipse/uprotocol/transport/validator/UAttributeValidatorTest.java index 2c98da10..c35fb49c 100644 --- a/src/test/java/org/eclipse/uprotocol/transport/validator/UAttributeValidatorTest.java +++ b/src/test/java/org/eclipse/uprotocol/transport/validator/UAttributeValidatorTest.java @@ -1,5 +1,18 @@ +/** + * SPDX-FileCopyrightText: 2024 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + */ package org.eclipse.uprotocol.transport.validator; +import static org.junit.Assert.assertThrows; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -13,7 +26,7 @@ import org.eclipse.uprotocol.transport.validate.UAttributesValidator; import org.eclipse.uprotocol.uuid.factory.UuidFactory; import org.eclipse.uprotocol.v1.UUri; -import org.eclipse.uprotocol.validation.ValidationResult; +import org.eclipse.uprotocol.validation.ValidationException; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -40,8 +53,7 @@ public void testUAttributeValidatorHappyPath() { UMessage message = UMessageBuilder.publish(buildTopicUUri()).build(); UAttributesValidator validator = UAttributesValidator.getValidator(message.getAttributes()); - ValidationResult result = validator.validate(message.getAttributes()); - assertTrue(result.isSuccess()); + validator.validate(message.getAttributes()); assertEquals(validator.toString(), "UAttributesValidator.Publish"); } @@ -51,8 +63,7 @@ public void testUAttributeValidatorNotification() { UMessage message = UMessageBuilder.notification(buildTopicUUri(), buildDefaultUUri()).build(); UAttributesValidator validator = UAttributesValidator.getValidator(message.getAttributes()); - ValidationResult result = validator.validate(message.getAttributes()); - assertTrue(result.isSuccess()); + validator.validate(message.getAttributes()); assertEquals(validator.toString(), "UAttributesValidator.Notification"); } @@ -62,8 +73,7 @@ public void testUAttributeValidatorRequest() { UMessage message = UMessageBuilder.request(buildDefaultUUri(), buildMethodUUri(), 1000).build(); UAttributesValidator validator = UAttributesValidator.getValidator(message.getAttributes()); - ValidationResult result = validator.validate(message.getAttributes()); - assertTrue(result.isSuccess()); + validator.validate(message.getAttributes()); assertEquals(validator.toString(), "UAttributesValidator.Request"); } @@ -78,10 +88,8 @@ public void testUAttributeValidatorResponse() { .build(); UAttributesValidator validator = UAttributesValidator.getValidator(response.getAttributes()); - ValidationResult result = validator.validate(response.getAttributes()); - assertTrue(result.isSuccess()); + validator.validate(response.getAttributes()); assertEquals(validator.toString(), "UAttributesValidator.Response"); - assertEquals(result.getMessage(), ""); } @Test @@ -91,8 +99,7 @@ public void testUAttributeValidatorResponseWithRequestAttributes() { UMessage response = UMessageBuilder.response(request.getAttributes()).build(); UAttributesValidator validator = UAttributesValidator.getValidator(response.getAttributes()); - ValidationResult result = validator.validate(response.getAttributes()); - assertTrue(result.isSuccess()); + validator.validate(response.getAttributes()); assertEquals(validator.toString(), "UAttributesValidator.Response"); } @@ -102,10 +109,13 @@ public void testUAttributeValidatorRequestWithPublishValidator() { UMessage message = UMessageBuilder.request(buildDefaultUUri(), buildMethodUUri(), 1000).build(); UAttributesValidator validator = UAttributesValidator.Validators.PUBLISH.validator(); - ValidationResult result = validator.validate(message.getAttributes()); - assertTrue(result.isFailure()); + ValidationException result = assertThrows( + ValidationException.class, + () -> validator.validate(message.getAttributes())); assertEquals(validator.toString(), "UAttributesValidator.Publish"); - assertEquals(result.getMessage(), "Wrong Attribute Type [UMESSAGE_TYPE_REQUEST],Sink should not be present"); + assertEquals( + result.getMessage(), + "Wrong Attribute Type [UMESSAGE_TYPE_REQUEST],Sink should not be present"); } @Test @@ -114,8 +124,9 @@ public void testUAttributeValidatorPublishWithNotificationValidator() { UMessage message = UMessageBuilder.publish(buildTopicUUri()).build(); UAttributesValidator validator = UAttributesValidator.Validators.NOTIFICATION.validator(); - ValidationResult result = validator.validate(message.getAttributes()); - assertTrue(result.isFailure()); + ValidationException result = assertThrows( + ValidationException.class, + () -> validator.validate(message.getAttributes())); assertEquals(validator.toString(), "UAttributesValidator.Notification"); assertEquals(result.getMessage(), "Wrong Attribute Type [UMESSAGE_TYPE_PUBLISH],Missing Sink"); } @@ -127,8 +138,9 @@ public void testUAttributeValidatorResponseWithRequestValidator() { UMessage response = UMessageBuilder.response(request.getAttributes()).build(); UAttributesValidator validator = UAttributesValidator.Validators.REQUEST.validator(); - ValidationResult result = validator.validate(response.getAttributes()); - assertTrue(result.isFailure()); + ValidationException result = assertThrows( + ValidationException.class, + () -> validator.validate(response.getAttributes())); assertEquals(validator.toString(), "UAttributesValidator.Request"); assertEquals( result.getMessage(), @@ -143,8 +155,9 @@ public void testUAttributeValidatorNotificationWithResponseValidator() { UMessage message = UMessageBuilder.notification(buildTopicUUri(), buildDefaultUUri()).build(); UAttributesValidator validator = UAttributesValidator.Validators.RESPONSE.validator(); - ValidationResult result = validator.validate(message.getAttributes()); - assertTrue(result.isFailure()); + ValidationException result = assertThrows( + ValidationException.class, + () -> validator.validate(message.getAttributes())); assertEquals(validator.toString(), "UAttributesValidator.Response"); assertEquals( result.getMessage(), @@ -166,8 +179,9 @@ public void testUAttributeValidatorRequestMissingSink() { .build(); UAttributesValidator validator = UAttributesValidator.getValidator(attributes); - ValidationResult result = validator.validate(attributes); - assertTrue(result.isFailure()); + ValidationException result = assertThrows( + ValidationException.class, + () -> validator.validate(attributes)); assertEquals(validator.toString(), "UAttributesValidator.Request"); assertEquals(result.getMessage(), "Invalid Sink Uri"); } @@ -183,8 +197,9 @@ public void testUAttributeValidatorRequestInvalidPermissionLevel() { .build(); UAttributesValidator validator = UAttributesValidator.getValidator(message.getAttributes()); - ValidationResult result = validator.validate(message.getAttributes()); - assertTrue(result.isFailure()); + ValidationException result = assertThrows( + ValidationException.class, + () -> validator.validate(message.getAttributes())); assertEquals(validator.toString(), "UAttributesValidator.Request"); assertEquals(result.getMessage(), "Invalid Permission Level"); } @@ -200,8 +215,7 @@ public void testUAttributeValidatorRequestValidPermissionLevel() { .build(); UAttributesValidator validator = UAttributesValidator.getValidator(message.getAttributes()); - ValidationResult result = validator.validate(message.getAttributes()); - assertTrue(result.isSuccess()); + validator.validate(message.getAttributes()); assertFalse(validator.isExpired(message.getAttributes())); assertEquals(validator.toString(), "UAttributesValidator.Request"); } @@ -212,8 +226,9 @@ public void testUAttributeValidatorRequestInvalidTTL() { UMessage message = UMessageBuilder.publish(buildTopicUUri()).withTtl(-1).build(); UAttributesValidator validator = UAttributesValidator.getValidator(message.getAttributes()); - ValidationResult result = validator.validate(message.getAttributes()); - assertTrue(result.isFailure()); + ValidationException result = assertThrows( + ValidationException.class, + () -> validator.validate(message.getAttributes())); assertFalse(validator.isExpired(message.getAttributes())); assertEquals(validator.toString(), "UAttributesValidator.Publish"); assertEquals(result.getMessage(), "Invalid TTL [-1]"); @@ -249,8 +264,9 @@ public void testUAttributeValidatorPublishWithReqId() { .build(); UAttributesValidator validator = UAttributesValidator.getValidator(attributes); - ValidationResult result = validator.validate(attributes); - assertTrue(result.isFailure()); + ValidationException result = assertThrows( + ValidationException.class, + () -> validator.validate(attributes)); assertEquals(validator.toString(), "UAttributesValidator.Publish"); assertEquals(result.getMessage(), "Message should not have a reqid"); } @@ -261,9 +277,10 @@ public void testUAttributeValidatorNotificationMissingSink() { final UMessage message = UMessageBuilder.notification(buildTopicUUri(), buildDefaultUUri()).build(); final UAttributes attributes = UAttributes.newBuilder().mergeFrom(message.getAttributes()).clearSink().build(); final UAttributesValidator validator = UAttributesValidator.getValidator(attributes); - final ValidationResult result = validator.validate(attributes); + final ValidationException result = assertThrows( + ValidationException.class, + () -> validator.validate(attributes)); - assertTrue(result.isFailure()); assertEquals(validator.toString(), "UAttributesValidator.Notification"); assertEquals(result.getMessage(), "Missing Sink"); } @@ -277,9 +294,10 @@ public void testUAttributeValidatorNotificationDefaultSink() { .setSink(UUri.getDefaultInstance()) .build(); final UAttributesValidator validator = UAttributesValidator.getValidator(attributes); - final ValidationResult result = validator.validate(attributes); + final ValidationException result = assertThrows( + ValidationException.class, + () -> validator.validate(attributes)); - assertTrue(result.isFailure()); assertEquals(validator.toString(), "UAttributesValidator.Notification"); assertEquals(result.getMessage(), "Missing Sink"); } @@ -295,9 +313,10 @@ public void testUAttributeValidatorNotificationDefaultResourceId() { .setPriority(UPriority.UPRIORITY_CS1) .build(); final UAttributesValidator validator = UAttributesValidator.getValidator(attributes); - final ValidationResult result = validator.validate(attributes); + final ValidationException result = assertThrows( + ValidationException.class, + () -> validator.validate(attributes)); - assertTrue(result.isFailure()); assertEquals(validator.toString(), "UAttributesValidator.Notification"); assertEquals(result.getMessage(), "Invalid Sink Uri"); } @@ -311,9 +330,10 @@ public void testUAttributeValidatorValidatePriorityLessThanCS0() { .setPriority(UPriority.UPRIORITY_UNSPECIFIED) .build(); final UAttributesValidator validator = UAttributesValidator.getValidator(attributes); - final ValidationResult result = validator.validate(attributes); + final ValidationException result = assertThrows( + ValidationException.class, + () -> validator.validate(attributes)); - assertTrue(result.isFailure()); assertEquals(validator.toString(), "UAttributesValidator.Publish"); assertEquals(result.getMessage(), "Invalid UPriority [UPRIORITY_UNSPECIFIED]"); } @@ -325,9 +345,10 @@ public void testUAttributeValidatorValidatePriorityIsCS0() { final UAttributes attributes = UAttributes.newBuilder(). mergeFrom(message.getAttributes()).setPriority(UPriority.UPRIORITY_CS0).build(); final UAttributesValidator validator = UAttributesValidator.getValidator(attributes); - final ValidationResult result = validator.validate(attributes); + final ValidationException result = assertThrows( + ValidationException.class, + () -> validator.validate(attributes)); - assertTrue(result.isFailure()); assertEquals(validator.toString(), "UAttributesValidator.Publish"); assertEquals(result.getMessage(), "Invalid UPriority [UPRIORITY_CS0]"); } @@ -338,9 +359,10 @@ public void testUAttributeValidatorValidateIdMissing() { final UMessage message = UMessageBuilder.publish(buildTopicUUri()).build(); final UAttributes attributes = UAttributes.newBuilder().mergeFrom(message.getAttributes()).clearId().build(); final UAttributesValidator validator = UAttributesValidator.getValidator(attributes); - final ValidationResult result = validator.validate(attributes); + final ValidationException result = assertThrows( + ValidationException.class, + () -> validator.validate(attributes)); - assertTrue(result.isFailure()); assertEquals(validator.toString(), "UAttributesValidator.Publish"); assertEquals(result.getMessage(), "Missing id"); } @@ -354,9 +376,10 @@ public void testUAttributeValidatorValidateIdDefault() { .setId(UUID.getDefaultInstance()) .build(); final UAttributesValidator validator = UAttributesValidator.getValidator(attributes); - final ValidationResult result = validator.validate(attributes); + final ValidationException result = assertThrows( + ValidationException.class, + () -> validator.validate(attributes)); - assertTrue(result.isFailure()); assertEquals(validator.toString(), "UAttributesValidator.Publish"); assertEquals(result.getMessage(), "Attributes must contain valid uProtocol UUID in id property"); } @@ -370,9 +393,10 @@ public void testUAttributeValidatorValidateSinkNotEmpty() { .setSink(buildDefaultUUri()) .build(); final UAttributesValidator validator = UAttributesValidator.getValidator(attributes); - final ValidationResult result = validator.validate(attributes); + final ValidationException result = assertThrows( + ValidationException.class, + () -> validator.validate(attributes)); - assertTrue(result.isFailure()); assertEquals(validator.toString(), "UAttributesValidator.Publish"); assertEquals(result.getMessage(), "Sink should not be present"); } @@ -383,9 +407,10 @@ public void testUAttributeValidatorValidateSinkMissing() { final UMessage message = UMessageBuilder.request(buildDefaultUUri(), buildMethodUUri(), 1000).build(); final UAttributes attributes = UAttributes.newBuilder().mergeFrom(message.getAttributes()).clearSink().build(); final UAttributesValidator validator = UAttributesValidator.getValidator(attributes); - final ValidationResult result = validator.validate(attributes); + final ValidationException result = assertThrows( + ValidationException.class, + () -> validator.validate(attributes)); - assertTrue(result.isFailure()); assertEquals(validator.toString(), "UAttributesValidator.Request"); assertEquals(result.getMessage(), "Missing Sink"); } @@ -401,9 +426,10 @@ public void testUAttributeValidatorValidateTtlLessThanZero() { .setType(UMessageType.UMESSAGE_TYPE_REQUEST) .setPriority(UPriority.UPRIORITY_CS4).build(); final UAttributesValidator validator = UAttributesValidator.getValidator(attributes); - final ValidationResult result = validator.validate(attributes); + final ValidationException result = assertThrows( + ValidationException.class, + () -> validator.validate(attributes)); - assertTrue(result.isFailure()); assertEquals(validator.toString(), "UAttributesValidator.Request"); assertEquals(result.getMessage(), "Invalid TTL [-1]"); } @@ -417,9 +443,10 @@ public void testUAttributeValidatorValidatePriorityLessThanCS4() { .setPriority(UPriority.UPRIORITY_CS3) .build(); final UAttributesValidator validator = UAttributesValidator.getValidator(attributes); - final ValidationResult result = validator.validate(attributes); + final ValidationException result = assertThrows( + ValidationException.class, + () -> validator.validate(attributes)); - assertTrue(result.isFailure()); assertEquals(validator.toString(), "UAttributesValidator.Request"); assertEquals(result.getMessage(), "Invalid UPriority [UPRIORITY_CS3]"); } @@ -431,9 +458,10 @@ public void testUAttributeValidatorValidateSinkResponseMissing() { final UMessage response = UMessageBuilder.response(request.getAttributes()).build(); final UAttributes attributes = UAttributes.newBuilder().mergeFrom(response.getAttributes()).clearSink().build(); final UAttributesValidator validator = UAttributesValidator.getValidator(attributes); - final ValidationResult result = validator.validate(attributes); + final ValidationException result = assertThrows( + ValidationException.class, + () -> validator.validate(attributes)); - assertTrue(result.isFailure()); assertEquals(validator.toString(), "UAttributesValidator.Response"); assertEquals(result.getMessage(), "Missing Sink"); } @@ -448,9 +476,10 @@ public void testUAttributeValidatorValidateSinkResponseDefault() { .setSink(UUri.getDefaultInstance()) .build(); final UAttributesValidator validator = UAttributesValidator.getValidator(attributes); - final ValidationResult result = validator.validate(attributes); + final ValidationException result = assertThrows( + ValidationException.class, + () -> validator.validate(attributes)); - assertTrue(result.isFailure()); assertEquals(validator.toString(), "UAttributesValidator.Response"); assertEquals(result.getMessage(), "Missing Sink"); } @@ -469,9 +498,10 @@ public void testUAttributeValidatorValidateSinkResponseDefaultResourceId() { .build(); final UAttributesValidator validator = UAttributesValidator.getValidator(attributes); - final ValidationResult result = validator.validate(attributes); + final ValidationException result = assertThrows( + ValidationException.class, + () -> validator.validate(attributes)); - assertTrue(result.isFailure()); assertEquals(validator.toString(), "UAttributesValidator.Response"); assertEquals(result.getMessage(), "Invalid Sink Uri"); } @@ -486,9 +516,10 @@ public void testUAttributeValidatorValidateReqIdMissing() { .clearReqid() .build(); final UAttributesValidator validator = UAttributesValidator.getValidator(attributes); - final ValidationResult result = validator.validate(attributes); + final ValidationException result = assertThrows( + ValidationException.class, + () -> validator.validate(attributes)); - assertTrue(result.isFailure()); assertEquals(validator.toString(), "UAttributesValidator.Response"); assertEquals(result.getMessage(), "Missing correlationId"); } @@ -503,9 +534,10 @@ public void testUAttributeValidatorValidateReqIdDefault() { .setReqid(UUID.getDefaultInstance()) .build(); final UAttributesValidator validator = UAttributesValidator.getValidator(attributes); - final ValidationResult result = validator.validate(attributes); + final ValidationException result = assertThrows( + ValidationException.class, + () -> validator.validate(attributes)); - assertTrue(result.isFailure()); assertEquals(validator.toString(), "UAttributesValidator.Response"); assertEquals(result.getMessage(), "Missing correlationId"); } @@ -520,9 +552,10 @@ public void testUAttributeValidatorValidateReqIdInvalid() { .setReqid(UUID.newBuilder().setLsb(0xbeadbeef).setMsb(0xdeadbeef)) .build(); final UAttributesValidator validator = UAttributesValidator.getValidator(attributes); - final ValidationResult result = validator.validate(attributes); + final ValidationException result = assertThrows( + ValidationException.class, + () -> validator.validate(attributes)); - assertTrue(result.isFailure()); assertEquals(validator.toString(), "UAttributesValidator.Response"); assertEquals(result.getMessage(), "Invalid correlation UUID"); } diff --git a/src/test/java/org/eclipse/uprotocol/uuid/factory/UUIDFactoryTest.java b/src/test/java/org/eclipse/uprotocol/uuid/factory/UUIDFactoryTest.java index 9077e890..2e7ca849 100644 --- a/src/test/java/org/eclipse/uprotocol/uuid/factory/UUIDFactoryTest.java +++ b/src/test/java/org/eclipse/uprotocol/uuid/factory/UUIDFactoryTest.java @@ -253,7 +253,4 @@ void testCreateUuidv7WithTheSameTimeToConfirmTheUuidsAreNotTheSame() { assertNotEquals(uuid, uuid1); assertEquals(UuidUtils.getTime(uuid1).get(), UuidUtils.getTime(uuid).get()); } - - } - diff --git a/src/test/java/org/eclipse/uprotocol/uuid/validator/UuidValidatorTest.java b/src/test/java/org/eclipse/uprotocol/uuid/validator/UuidValidatorTest.java index c9c79433..c84d92a3 100644 --- a/src/test/java/org/eclipse/uprotocol/uuid/validator/UuidValidatorTest.java +++ b/src/test/java/org/eclipse/uprotocol/uuid/validator/UuidValidatorTest.java @@ -17,9 +17,7 @@ import org.eclipse.uprotocol.uuid.serializer.UuidSerializer; import org.eclipse.uprotocol.uuid.validate.UuidValidator; import org.eclipse.uprotocol.v1.UUID; -import org.eclipse.uprotocol.v1.UCode; -import org.eclipse.uprotocol.v1.UStatus; -import org.eclipse.uprotocol.validation.ValidationResult; +import org.eclipse.uprotocol.validation.ValidationException; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -32,34 +30,35 @@ public class UuidValidatorTest { void testValidatorWithGoodUuid() { //final UuidValidator validator = new UuidValidator(); final UUID uuid = UuidFactory.Factories.UPROTOCOL.factory().create(); - final UStatus status = UuidValidator.getValidator(uuid).validate(uuid); - assertEquals(ValidationResult.STATUS_SUCCESS, status); + UuidValidator.getValidator(uuid).validate(uuid); } @Test @DisplayName("Test Good uuid Check") void testGoodUuidString() { - final UStatus status = UuidValidator.Validators.UPROTOCOL.validator() - .validate(UuidFactory.Factories.UPROTOCOL.factory().create()); - assertEquals(status, ValidationResult.STATUS_SUCCESS); + final var uuid = UuidFactory.Factories.UPROTOCOL.factory().create(); + UuidValidator.Validators.UPROTOCOL.validator().validate(uuid); } @Test @DisplayName("Test fetching the invalid Validator for when UUID passed is garbage") void testInvalidUuid() { final UUID uuid = UUID.newBuilder().setMsb(0L).setLsb(0L).build(); - final UStatus status = UuidValidator.getValidator(uuid).validate(uuid); - assertEquals(UCode.INVALID_ARGUMENT, status.getCode()); - assertEquals("Invalid UUID Version,Invalid UUID Time", status.getMessage()); + final UuidValidator validator = UuidValidator.getValidator(uuid); + final var validationException = assertThrows(ValidationException.class, () -> validator.validate(uuid)); + assertEquals( + "Invalid UUID Version,Invalid UUID Time", + validationException.getMessage()); } @Test @DisplayName("Test invalid time uuid") void testInvalidTimeUuid() { final UUID uuid = UuidFactory.Factories.UPROTOCOL.factory().create(Instant.ofEpochSecond(0)); - final UStatus status = UuidValidator.Validators.UPROTOCOL.validator().validate(uuid); - assertEquals(UCode.INVALID_ARGUMENT, status.getCode()); - assertEquals("Invalid UUID Time", status.getMessage()); + final var validationException = assertThrows( + ValidationException.class, + () -> UuidValidator.Validators.UPROTOCOL.validator().validate(uuid)); + assertEquals("Invalid UUID Time", validationException.getMessage()); } @Test @@ -67,9 +66,7 @@ void testInvalidTimeUuid() { void testUuidv7WithInvalidUuids() { final UuidValidator validator = UuidValidator.Validators.UPROTOCOL.validator(); assertNotNull(validator); - final UStatus status = validator.validate(null); - assertEquals(UCode.INVALID_ARGUMENT, status.getCode()); - assertEquals("Invalid UUIDv7 Version,Invalid UUID Time", status.getMessage()); + assertThrows(NullPointerException.class, () -> validator.validate(null)); } @Test @@ -77,6 +74,7 @@ void testUuidv7WithInvalidUuids() { void testUuidv7WithInvalidTypes() { final UUID uuidv6 = UuidFactory.Factories.UUIDV6.factory().create(); final UUID uuid = UUID.newBuilder().setMsb(0L).setLsb(0L).build(); + final java.util.UUID uuid_java = java.util.UUID.randomUUID(); final UUID uuidv4 = UUID.newBuilder().setMsb(uuid_java.getMostSignificantBits()) .setLsb(uuid_java.getLeastSignificantBits()).build(); @@ -84,17 +82,16 @@ void testUuidv7WithInvalidTypes() { final UuidValidator validator = UuidValidator.Validators.UPROTOCOL.validator(); assertNotNull(validator); - final UStatus status = validator.validate(uuidv6); - assertEquals(UCode.INVALID_ARGUMENT, status.getCode()); - assertEquals("Invalid UUIDv7 Version", status.getMessage()); + var validationException = assertThrows(ValidationException.class, () -> validator.validate(uuidv6)); + assertEquals("Invalid UUIDv7 Version", validationException.getMessage()); - final UStatus status1 = validator.validate(uuid); - assertEquals(UCode.INVALID_ARGUMENT, status1.getCode()); - assertEquals("Invalid UUIDv7 Version,Invalid UUID Time", status1.getMessage()); + validationException = assertThrows(ValidationException.class, () -> validator.validate(uuid)); + assertEquals( + "Invalid UUIDv7 Version,Invalid UUID Time", + validationException.getMessage()); - final UStatus status2 = validator.validate(uuidv4); - assertEquals(UCode.INVALID_ARGUMENT, status2.getCode()); - assertEquals("Invalid UUIDv7 Version,Invalid UUID Time", status2.getMessage()); + validationException = assertThrows(ValidationException.class, () -> validator.validate(uuidv4)); + assertEquals("Invalid UUIDv7 Version,Invalid UUID Time", validationException.getMessage()); } @Test @@ -105,10 +102,9 @@ void testGoodUuidv6() { UuidValidator validator = UuidValidator.getValidator(uuid); assertNotNull(validator); assertTrue(UuidUtils.isUuidv6(uuid)); - assertEquals(UCode.OK, validator.validate(uuid).getCode()); + validator.validate(uuid); } - @Test @DisplayName("Test UUIDv6 with bad variant") void testUuidv6WithBadVariant() { @@ -116,9 +112,10 @@ void testUuidv6WithBadVariant() { assertFalse(uuid.equals(UUID.getDefaultInstance())); final UuidValidator validator = UuidValidator.getValidator(uuid); assertNotNull(validator); - final UStatus status = validator.validate(uuid); - assertEquals("Invalid UUID Version,Invalid UUID Time", status.getMessage()); - assertEquals(UCode.INVALID_ARGUMENT, status.getCode()); + final var validationException = assertThrows(ValidationException.class, () -> validator.validate(uuid)); + assertEquals( + "Invalid UUID Version,Invalid UUID Time", + validationException.getMessage()); } @Test @@ -128,9 +125,10 @@ void testUuidv6WithInvalidUuid() { final UUID uuid = UUID.newBuilder().setMsb(9 << 12).setLsb(0L).build(); final UuidValidator validator = UuidValidator.Validators.UUIDV6.validator(); assertNotNull(validator); - final UStatus status = validator.validate(uuid); - assertEquals("Not a UUIDv6 Version,Invalid UUID Time", status.getMessage()); - assertEquals(UCode.INVALID_ARGUMENT, status.getCode()); + final var validationException = assertThrows(ValidationException.class, () -> validator.validate(uuid)); + assertEquals( + "Not a UUIDv6 Version,Invalid UUID Time", + validationException.getMessage()); } @@ -139,9 +137,7 @@ void testUuidv6WithInvalidUuid() { void testUuidv6WithNullUuid() { final UuidValidator validator = UuidValidator.Validators.UUIDV6.validator(); assertNotNull(validator); - final UStatus status = validator.validate(null); - assertEquals("Not a UUIDv6 Version,Invalid UUID Time", status.getMessage()); - assertEquals(UCode.INVALID_ARGUMENT, status.getCode()); + assertThrows(NullPointerException.class, () -> validator.validate(null)); } @Test @@ -150,9 +146,7 @@ void testUuidv6WithUuidv7() { final UUID uuid = UuidFactory.Factories.UPROTOCOL.factory().create(); final UuidValidator validator = UuidValidator.Validators.UUIDV6.validator(); assertNotNull(validator); - final UStatus status = validator.validate(uuid); - assertEquals("Not a UUIDv6 Version", status.getMessage()); - assertEquals(UCode.INVALID_ARGUMENT, status.getCode()); + final var validationException = assertThrows(ValidationException.class, () -> validator.validate(uuid)); + assertEquals("Not a UUIDv6 Version", validationException.getMessage()); } - } diff --git a/src/test/java/org/eclipse/uprotocol/validation/ValidationExceptionTest.java b/src/test/java/org/eclipse/uprotocol/validation/ValidationExceptionTest.java new file mode 100644 index 00000000..b6356b10 --- /dev/null +++ b/src/test/java/org/eclipse/uprotocol/validation/ValidationExceptionTest.java @@ -0,0 +1,45 @@ +/** + * SPDX-FileCopyrightText: 2025 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + */ +package org.eclipse.uprotocol.validation; + +import static org.junit.Assert.assertEquals; + +import java.util.List; + +import org.junit.jupiter.api.Test; + +import com.google.common.truth.Truth; + +class ValidationExceptionTest { + + @Test + void testGetCauses() { + final List causes = List.of( + new ValidationException("First cause"), + new IllegalArgumentException("Second cause") + ); + ValidationException exception = new ValidationException(causes); + Truth.assertThat(exception.getCauses()).containsExactlyElementsIn(causes); + } + + @Test + void testGetMessage() { + final List causes = List.of( + new ValidationException("First cause"), + new IllegalArgumentException("Second cause") + ); + ValidationException exception = new ValidationException(causes); + String errorMessages = exception.getMessage(); + assertEquals("First cause,Second cause", errorMessages); + } +}