From cd5591070511a091fc82a4e517025eb68aefafda Mon Sep 17 00:00:00 2001 From: andrey Date: Thu, 19 Oct 2023 21:10:14 +0300 Subject: [PATCH] fix NPE when for gRPC stream method was called complete without any onNext on a server side --- .../io/qameta/allure/grpc/AllureGrpc.java | 91 +++++++++++-------- .../io/qameta/allure/grpc/AllureGrpcTest.java | 19 ++++ 2 files changed, 70 insertions(+), 40 deletions(-) diff --git a/allure-grpc/src/main/java/io/qameta/allure/grpc/AllureGrpc.java b/allure-grpc/src/main/java/io/qameta/allure/grpc/AllureGrpc.java index d394a16aa..5f8224624 100644 --- a/allure-grpc/src/main/java/io/qameta/allure/grpc/AllureGrpc.java +++ b/allure-grpc/src/main/java/io/qameta/allure/grpc/AllureGrpc.java @@ -96,7 +96,7 @@ public ClientCall interceptCall(MethodDescriptor method, next.newCall(method, callOptions.withoutWaitForReady())) { private String stepUuid; - private List parsedResponses = new ArrayList<>(); + private final List parsedResponses = new ArrayList<>(); @SuppressWarnings("PMD.MethodArgumentCouldBeFinal") @Override @@ -138,49 +138,60 @@ protected Listener delegate() { @SuppressWarnings({"PMD.MethodArgumentCouldBeFinal", "PMD.AvoidLiteralsInIfCondition"}) @Override public void onClose(io.grpc.Status status, Metadata trailers) { - GrpcResponseAttachment.Builder responseAttachmentBuilder = null; - - if (parsedResponses.size() == 1) { - responseAttachmentBuilder = GrpcResponseAttachment.Builder - .create("gRPC response") - .setBody(parsedResponses.iterator().next()); - } else if (parsedResponses.size() > 1) { - responseAttachmentBuilder = GrpcResponseAttachment.Builder - .create("gRPC response (collection of elements from Server stream)") - .setBody("[" + String.join(",\n", parsedResponses) + "]"); - } - if (!status.isOk()) { - String description = status.getDescription(); - if (description == null) { - description = "No description provided"; + try { + GrpcResponseAttachment.Builder responseAttachmentBuilder; + + if (parsedResponses.isEmpty()) { + responseAttachmentBuilder = GrpcResponseAttachment.Builder + .create("gRPC response (empty stream)"); + } else if (parsedResponses.size() == 1) { + responseAttachmentBuilder = GrpcResponseAttachment.Builder + .create("gRPC response") + .setBody(parsedResponses.iterator().next()); + } else { + responseAttachmentBuilder = GrpcResponseAttachment.Builder + .create("gRPC response (collection of elements from Server stream)") + .setBody("[" + String.join(",\n", parsedResponses) + "]"); + } + if (!status.isOk()) { + String description = status.getDescription(); + if (description == null) { + description = "No description provided"; + } + responseAttachmentBuilder = GrpcResponseAttachment.Builder + .create(status.getCode().name()) + .setStatus(description); } - responseAttachmentBuilder = GrpcResponseAttachment.Builder - .create(status.getCode().name()) - .setStatus(description); - } - requireNonNull(responseAttachmentBuilder).setStatus(status.toString()); - if (interceptResponseMetadata) { - for (String key : headers.keys()) { - requireNonNull(responseAttachmentBuilder).setMetadata( - key, - headers.get(Metadata.Key.of(key, Metadata.ASCII_STRING_MARSHALLER)) - ); + requireNonNull(responseAttachmentBuilder).setStatus(status.toString()); + if (interceptResponseMetadata) { + for (String key : headers.keys()) { + requireNonNull(responseAttachmentBuilder).setMetadata( + key, + headers.get(Metadata.Key.of(key, Metadata.ASCII_STRING_MARSHALLER)) + ); + } } + processor.addAttachment( + requireNonNull(responseAttachmentBuilder).build(), + new FreemarkerAttachmentRenderer(responseTemplatePath) + ); + + if (status.isOk() || !markStepFailedOnNonZeroCode) { + Allure.getLifecycle().updateStep(stepUuid, step -> step.setStatus(Status.PASSED)); + } else { + Allure.getLifecycle().updateStep(stepUuid, step -> step.setStatus(Status.FAILED)); + } + } catch (RuntimeException e) { + Allure.getLifecycle().updateStep(step -> + step.setStatus(ResultsUtils.getStatus(e).orElse(Status.BROKEN)) + .setStatusDetails(ResultsUtils.getStatusDetails(e).orElse(null)) + ); + } finally { + Allure.getLifecycle().stopStep(stepUuid); + stepUuid = null; + super.onClose(status, trailers); } - processor.addAttachment( - requireNonNull(responseAttachmentBuilder).build(), - new FreemarkerAttachmentRenderer(responseTemplatePath) - ); - - if (status.isOk() || !markStepFailedOnNonZeroCode) { - Allure.getLifecycle().updateStep(stepUuid, step -> step.setStatus(Status.PASSED)); - } else { - Allure.getLifecycle().updateStep(stepUuid, step -> step.setStatus(Status.FAILED)); - } - Allure.getLifecycle().stopStep(stepUuid); - stepUuid = null; - super.onClose(status, trailers); } @SuppressWarnings("PMD.MethodArgumentCouldBeFinal") diff --git a/allure-grpc/src/test/java/io/qameta/allure/grpc/AllureGrpcTest.java b/allure-grpc/src/test/java/io/qameta/allure/grpc/AllureGrpcTest.java index f75df916b..2e6a8cd50 100644 --- a/allure-grpc/src/test/java/io/qameta/allure/grpc/AllureGrpcTest.java +++ b/allure-grpc/src/test/java/io/qameta/allure/grpc/AllureGrpcTest.java @@ -19,6 +19,8 @@ import io.grpc.ManagedChannelBuilder; import io.grpc.Status; import io.grpc.StatusRuntimeException; +import io.grpc.stub.CallStreamObserver; +import io.grpc.stub.StreamObservers; import io.qameta.allure.model.Attachment; import io.qameta.allure.model.StepResult; import io.qameta.allure.test.AllureResults; @@ -30,6 +32,7 @@ import org.junit.jupiter.api.extension.ExtendWith; import java.util.Iterator; +import java.util.List; import java.util.Optional; import static io.qameta.allure.test.RunUtils.runWithinTestContext; @@ -114,6 +117,22 @@ void shouldCreateResponseAttachmentForServerStreamingResponse() { .contains("gRPC response (collection of elements from Server stream)"); } + @Test + void shouldCreateResponseAttachmentForEmptyServerStreamingResponse() { + GrpcMock.stubFor(serverStreamingMethod(TestServiceGrpc.getCalculateServerStreamMethod()) + .willProxyTo((request, responseStreamObserver) -> + StreamObservers.copyWithFlowControl( + List.of(), (CallStreamObserver) responseStreamObserver)) + ); + + var results = executeStreaming(Request.newBuilder().setTopic("1").build()); + + assertThat(results.getTestResults().get(0).getSteps()) + .flatExtracting(StepResult::getAttachments) + .extracting(Attachment::getName) + .contains("gRPC response (empty stream)"); + } + @Test void shouldCreateResponseAttachmentOnStatusException() { final Status status = Status.NOT_FOUND;