From 3450e0153775e6d186e287e9a111c691a679b38d Mon Sep 17 00:00:00 2001 From: Sushant Mane Date: Tue, 17 Dec 2024 00:15:08 -0800 Subject: [PATCH] Add UTs for testLeaderControllerResponse - Add javadoc --- .../GrpcRequestResponseConverter.java | 59 +++++++++++++++++++ .../LeaderControllerResponseTest.java | 59 +++++++++++++++++++ .../integration/utils/ServiceFactory.java | 2 +- .../utils/VeniceClusterWrapper.java | 6 ++ 4 files changed, 125 insertions(+), 1 deletion(-) create mode 100644 internal/venice-common/src/test/java/com/linkedin/venice/controllerapi/LeaderControllerResponseTest.java diff --git a/internal/venice-common/src/main/java/com/linkedin/venice/controllerapi/transport/GrpcRequestResponseConverter.java b/internal/venice-common/src/main/java/com/linkedin/venice/controllerapi/transport/GrpcRequestResponseConverter.java index 70bc260888d..6269db16046 100644 --- a/internal/venice-common/src/main/java/com/linkedin/venice/controllerapi/transport/GrpcRequestResponseConverter.java +++ b/internal/venice-common/src/main/java/com/linkedin/venice/controllerapi/transport/GrpcRequestResponseConverter.java @@ -36,6 +36,41 @@ public static ClusterStoreGrpcInfo getClusterStoreGrpcInfo(ControllerRequest req return builder.build(); } + /** + * Sends an error response to the client using the provided error details. + * + *

This method constructs a detailed gRPC error response, including the error type, status code, + * message, and optional cluster or store-specific information. The constructed error is sent + * to the client via the provided {@link StreamObserver}.

+ * + * @param code The gRPC status code representing the error (e.g., {@link io.grpc.Status.Code}). + * @param errorType The specific controller error type represented by {@link ControllerGrpcErrorType}. + * @param e The exception containing the error message. + * @param clusterName The name of the cluster associated with the error (can be null). + * @param storeName The name of the store associated with the error (can be null). + * @param responseObserver The {@link StreamObserver} to send the error response back to the client. + * + *

Example usage:

+ *
+   * {@code
+   * sendErrorResponse(
+   *     Status.Code.INTERNAL,
+   *     ControllerGrpcErrorType.UNKNOWN_ERROR,
+   *     new RuntimeException("Something went wrong"),
+   *     "test-cluster",
+   *     "test-store",
+   *     responseObserver);
+   * }
+   * 
+ * + *

The error response includes the following:

+ * + */ public static void sendErrorResponse( Code code, ControllerGrpcErrorType errorType, @@ -62,6 +97,30 @@ public static void sendErrorResponse( responseObserver.onError(StatusProto.toStatusRuntimeException(status)); } + /** + * Parses a {@link StatusRuntimeException} to extract a {@link VeniceControllerGrpcErrorInfo} object. + * + *

This method processes the gRPC error details embedded within a {@link StatusRuntimeException}. + * If the error details contain a {@link VeniceControllerGrpcErrorInfo}, it unpacks and returns it. + * If no valid details are found or the unpacking fails, a {@link VeniceClientException} is thrown.

+ * + * @param e The {@link StatusRuntimeException} containing the gRPC error details. + * @return A {@link VeniceControllerGrpcErrorInfo} object extracted from the error details. + * @throws VeniceClientException If the error details cannot be unpacked or no valid information is found. + * + *

Example usage:

+ *
+   * {@code
+   * try {
+   *     // Call a gRPC method that might throw StatusRuntimeException
+   * } catch (StatusRuntimeException e) {
+   *     VeniceControllerGrpcErrorInfo errorInfo = parseControllerGrpcError(e);
+   *     System.out.println("Error Type: " + errorInfo.getErrorType());
+   *     System.out.println("Error Message: " + errorInfo.getErrorMessage());
+   * }
+   * }
+   * 
+ */ public static VeniceControllerGrpcErrorInfo parseControllerGrpcError(StatusRuntimeException e) { com.google.rpc.Status status = StatusProto.fromThrowable(e); if (status != null) { diff --git a/internal/venice-common/src/test/java/com/linkedin/venice/controllerapi/LeaderControllerResponseTest.java b/internal/venice-common/src/test/java/com/linkedin/venice/controllerapi/LeaderControllerResponseTest.java new file mode 100644 index 00000000000..2653643b516 --- /dev/null +++ b/internal/venice-common/src/test/java/com/linkedin/venice/controllerapi/LeaderControllerResponseTest.java @@ -0,0 +1,59 @@ +package com.linkedin.venice.controllerapi; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertNull; + +import org.testng.annotations.Test; + + +public class LeaderControllerResponseTest { + @Test + public void testLeaderControllerResponse() { + LeaderControllerResponse response = new LeaderControllerResponse(); + + // Testing getters and setters + String cluster = "test-cluster"; + String url = "http://leader-url"; + String secureUrl = "https://secure-leader-url"; + String grpcUrl = "grpc://leader-grpc-url"; + String secureGrpcUrl = "grpcs://secure-leader-grpc-url"; + + response.setCluster(cluster); + assertEquals(response.getCluster(), cluster, "Cluster name should match the set value"); + + response.setUrl(url); + assertEquals(response.getUrl(), url, "URL should match the set value"); + + response.setSecureUrl(secureUrl); + assertEquals(response.getSecureUrl(), secureUrl, "Secure URL should match the set value"); + + response.setGrpcUrl(grpcUrl); + assertEquals(response.getGrpcUrl(), grpcUrl, "gRPC URL should match the set value"); + + response.setSecureGrpcUrl(secureGrpcUrl); + assertEquals(response.getSecureGrpcUrl(), secureGrpcUrl, "Secure gRPC URL should match the set value"); + + // Testing default constructor + LeaderControllerResponse defaultResponse = new LeaderControllerResponse(); + assertNull(defaultResponse.getCluster(), "Cluster should be null by default"); + assertNull(defaultResponse.getUrl(), "URL should be null by default"); + assertNull(defaultResponse.getSecureUrl(), "Secure URL should be null by default"); + assertNull(defaultResponse.getGrpcUrl(), "gRPC URL should be null by default"); + assertNull(defaultResponse.getSecureGrpcUrl(), "Secure gRPC URL should be null by default"); + + // Testing combined constructor + LeaderControllerResponse combinedResponse = new LeaderControllerResponse(); + + combinedResponse.setCluster(cluster); + combinedResponse.setUrl(url); + combinedResponse.setSecureUrl(secureUrl); + combinedResponse.setGrpcUrl(grpcUrl); + combinedResponse.setSecureGrpcUrl(secureGrpcUrl); + + assertEquals(combinedResponse.getCluster(), cluster, "Cluster should match the set value"); + assertEquals(combinedResponse.getUrl(), url, "URL should match the set value"); + assertEquals(combinedResponse.getSecureUrl(), secureUrl, "Secure URL should match the set value"); + assertEquals(combinedResponse.getGrpcUrl(), grpcUrl, "gRPC URL should match the set value"); + assertEquals(combinedResponse.getSecureGrpcUrl(), secureGrpcUrl, "Secure gRPC URL should match the set value"); + } +} diff --git a/internal/venice-test-common/src/integrationTest/java/com/linkedin/venice/integration/utils/ServiceFactory.java b/internal/venice-test-common/src/integrationTest/java/com/linkedin/venice/integration/utils/ServiceFactory.java index f73a96ad066..6a51152ef6e 100644 --- a/internal/venice-test-common/src/integrationTest/java/com/linkedin/venice/integration/utils/ServiceFactory.java +++ b/internal/venice-test-common/src/integrationTest/java/com/linkedin/venice/integration/utils/ServiceFactory.java @@ -171,7 +171,7 @@ public static AdminSparkServer getMockAdminSparkServer( null, false, new PubSubTopicRepository(), - requestHandler); // Change this. + requestHandler); server.start(); return server; }); diff --git a/internal/venice-test-common/src/integrationTest/java/com/linkedin/venice/integration/utils/VeniceClusterWrapper.java b/internal/venice-test-common/src/integrationTest/java/com/linkedin/venice/integration/utils/VeniceClusterWrapper.java index 7a268d606d2..13b70864623 100644 --- a/internal/venice-test-common/src/integrationTest/java/com/linkedin/venice/integration/utils/VeniceClusterWrapper.java +++ b/internal/venice-test-common/src/integrationTest/java/com/linkedin/venice/integration/utils/VeniceClusterWrapper.java @@ -536,6 +536,12 @@ public final synchronized String getAllControllersURLs() { .collect(Collectors.joining(",")); } + /** + * Retrieves the gRPC URLs of all available Venice controllers as a comma-separated string. + * + * @return A comma-separated string of gRPC URLs for all controllers. If no controllers are available, + * the {@code externalControllerDiscoveryURL} is returned. + */ public final synchronized String getAllControllersGrpcURLs() { return veniceControllerWrappers.isEmpty() ? externalControllerDiscoveryURL