From 0c9266270ee2d3014254983ca63312017c9a532d Mon Sep 17 00:00:00 2001 From: Krzysztof Witukiewicz <krzysztof.witukiewicz@mgm-tp.com> Date: Thu, 30 Jan 2025 15:38:34 +0100 Subject: [PATCH] OZG-7262 OZG-7627 Don't call sendChunks() after exception --- .../GrpcBinaryFileServerDownloader.java | 31 ++- .../GrpcBinaryFileServerDownloaderTest.java | 239 +++++++----------- 2 files changed, 103 insertions(+), 167 deletions(-) diff --git a/ozgcloud-common-lib/src/main/java/de/ozgcloud/common/binaryfile/GrpcBinaryFileServerDownloader.java b/ozgcloud-common-lib/src/main/java/de/ozgcloud/common/binaryfile/GrpcBinaryFileServerDownloader.java index b2d2df3..8acac69 100644 --- a/ozgcloud-common-lib/src/main/java/de/ozgcloud/common/binaryfile/GrpcBinaryFileServerDownloader.java +++ b/ozgcloud-common-lib/src/main/java/de/ozgcloud/common/binaryfile/GrpcBinaryFileServerDownloader.java @@ -57,7 +57,7 @@ public class GrpcBinaryFileServerDownloader<T> { private final AtomicBoolean started = new AtomicBoolean(false); private final AtomicBoolean downloadFinished = new AtomicBoolean(false); private final AtomicBoolean requestFinished = new AtomicBoolean(false); - private final AtomicReference<TechnicalException> error = new AtomicReference<>(); + private final AtomicReference<TechnicalException> downloadError = new AtomicReference<>(); private PipedInputStream inputStream; private PipedOutputStream outputStream; @@ -107,11 +107,12 @@ public class GrpcBinaryFileServerDownloader<T> { void startDownload() { try { doDownload(); + sendChunks(); } catch (Exception e) { - error.set(new TechnicalException("Error while downloading file contents", e)); + LOG.error("Error while downloading file contents", e); + downloadError.set(new TechnicalException("Error while downloading file contents", e)); } finally { closeOutputStream(); - sendChunks(); } } @@ -136,10 +137,6 @@ public class GrpcBinaryFileServerDownloader<T> { } int bytesRead; while (isReady()) { - if (Objects.nonNull(error.get())) { - completeRequestWithError(error.get()); - break; - } if ((bytesRead = inputStream.read(buffer)) == -1) { tryCompleteRequest(); break; @@ -154,26 +151,28 @@ public class GrpcBinaryFileServerDownloader<T> { } void tryCompleteRequest() { - if (shouldCompleteRequest()) { - completeRequest(); + if (Objects.nonNull(downloadError.get())) { + throw downloadError.get(); + } else if (downloadFinished.get()) { + completeRequestNormally(); } } - boolean shouldCompleteRequest() { - return downloadFinished.get() && requestFinished.compareAndSet(false, true); + void completeRequestWithError(TechnicalException e) { + LOG.debug("Complete download request with error"); + finishRequest(); + throw e; } - void completeRequest() { + void completeRequestNormally() { LOG.debug("Complete download request"); - closeInputStream(); + finishRequest(); callObserver.onCompleted(); } - void completeRequestWithError(TechnicalException e) { - LOG.debug("Complete download request with error", e); + private void finishRequest() { requestFinished.set(true); closeInputStream(); - callObserver.onError(e); } void closeOutputStream() { diff --git a/ozgcloud-common-lib/src/test/java/de/ozgcloud/common/binaryfile/GrpcBinaryFileServerDownloaderTest.java b/ozgcloud-common-lib/src/test/java/de/ozgcloud/common/binaryfile/GrpcBinaryFileServerDownloaderTest.java index 81da69e..8c3ad5a 100644 --- a/ozgcloud-common-lib/src/test/java/de/ozgcloud/common/binaryfile/GrpcBinaryFileServerDownloaderTest.java +++ b/ozgcloud-common-lib/src/test/java/de/ozgcloud/common/binaryfile/GrpcBinaryFileServerDownloaderTest.java @@ -229,7 +229,7 @@ class GrpcBinaryFileServerDownloaderTest { @Test void shouldErrorBeInitiallyNull() { - assertThat(getError()).isNull(); + assertThat(getDownloadError()).isNull(); } @Nested @@ -256,7 +256,6 @@ class GrpcBinaryFileServerDownloaderTest { } @Test - @DisplayName("should send chunks here to not wait for callObserver to change its ready status") void shouldSendChunks() { downloader.startDownload(); @@ -278,7 +277,7 @@ class GrpcBinaryFileServerDownloaderTest { void shouldSetError() { downloader.startDownload(); - assertThat(getError()).isInstanceOf(TechnicalException.class).hasCause(exception); + assertThat(getDownloadError()).isInstanceOf(TechnicalException.class).hasCause(exception); } @SneakyThrows @@ -288,14 +287,6 @@ class GrpcBinaryFileServerDownloaderTest { verify(outputStream).close(); } - - @Test - @DisplayName("should send chunks here to communicate error to callObserver") - void shouldSendChunks() { - downloader.startDownload(); - - verify(downloader).sendChunks(); - } } } @@ -362,6 +353,7 @@ class GrpcBinaryFileServerDownloaderTest { @BeforeEach void init() { doThrow(exception).when(downloader).doSendChunks(); + doNothing().when(downloader).completeRequestWithError(any()); } @Test @@ -416,87 +408,52 @@ class GrpcBinaryFileServerDownloaderTest { @Nested class OnReady { + @Mock + private PipedInputStream inputStream; + @Captor + private ArgumentCaptor<ByteString> byteStringCaptor; + + private final int readBytes = 20; + private final byte[] buffer = new byte[readBytes]; + private final GrpcResponseDummy grpcResponseDummy = new GrpcResponseDummy(); + + @SneakyThrows @BeforeEach - void init() { + void mock() { + doNothing().when(downloader).tryCompleteRequest(); when(callObserver.isReady()).thenReturn(true); + when(inputStream.read(any())).thenReturn(readBytes, -1); + setInputStreamField(inputStream); + new Random().nextBytes(buffer); + ReflectionTestUtils.setField(downloader, "buffer", buffer); } - @Nested - class OnHasError { + @Test + void shouldCallChunkBuilder() { + doSendChunks(); - private final TechnicalException exception = new TechnicalException("error"); + verify(chunkBuilder).apply(byteStringCaptor.capture()); + assertThat(byteStringCaptor.getValue().toByteArray()).isEqualTo(buffer); + } - @BeforeEach - void init() { - setErrorField(exception); - doNothing().when(downloader).completeRequestWithError(any()); - } + @DisplayName("should send next chunk if callObserver is ready and stream already received data") + @Test + void shouldCallOnNext() { + when(chunkBuilder.apply(any())).thenReturn(grpcResponseDummy); - @Test - void shouldOnlyCallIsReadyOnObserver() { - doSendChunks(); + doSendChunks(); - verify(callObserver).isReady(); - verifyNoMoreInteractions(callObserver); - } + verify(callObserver).onNext(grpcResponseDummy); + } - @Test - void shouldCompleteRequestWithError() { - doSendChunks(); + @DisplayName("should call complete grpc stream if download has finished and stream has no data left") + @Test + void shouldTryCompleteRequest() { + setDownloadFinishedField(true); - verify(downloader).completeRequestWithError(exception); - } - } + doSendChunks(); - @Nested - class OnHasNoError { - @Mock - private PipedInputStream inputStream; - @Captor - private ArgumentCaptor<ByteString> byteStringCaptor; - - private final int readBytes = 20; - private final byte[] buffer = new byte[readBytes]; - private final GrpcResponseDummy grpcResponseDummy = new GrpcResponseDummy(); - - @SneakyThrows - @BeforeEach - void mock() { - doNothing().when(downloader).tryCompleteRequest(); - when(callObserver.isReady()).thenReturn(true); - when(inputStream.read(any())).thenReturn(readBytes, -1); - setInputStreamField(inputStream); - new Random().nextBytes(buffer); - ReflectionTestUtils.setField(downloader, "buffer", buffer); - } - - @Test - void shouldCallChunkBuilder() { - doSendChunks(); - - verify(chunkBuilder).apply(byteStringCaptor.capture()); - assertThat(byteStringCaptor.getValue().toByteArray()).isEqualTo(buffer); - } - - @DisplayName("should send next chunk if callObserver is ready and stream already received data") - @Test - void shouldCallOnNext() { - when(chunkBuilder.apply(any())).thenReturn(grpcResponseDummy); - - doSendChunks(); - - verify(callObserver).onNext(grpcResponseDummy); - } - - @DisplayName("should call complete grpc stream if download has finished and stream has no data left") - @Test - void shouldTryCompleteRequest() { - setDownloadFinishedField(true); - - doSendChunks(); - - verify(downloader).tryCompleteRequest(); - } + verify(downloader).tryCompleteRequest(); } } } @@ -510,69 +467,48 @@ class GrpcBinaryFileServerDownloaderTest { @Nested class TestTryCompleteRequest { - @Test - void shouldCallShouldCompleteRequest() { - downloader.tryCompleteRequest(); - - verify(downloader).shouldCompleteRequest(); - } - - @Test - void shouldCallCompleteRequest() { - doReturn(true).when(downloader).shouldCompleteRequest(); - - downloader.tryCompleteRequest(); - - verify(downloader).completeRequest(); - } + @Nested + class OnError { - @Test - void shouldNotCallCompleteRequest() { - doReturn(false).when(downloader).shouldCompleteRequest(); + private final TechnicalException exception = new TechnicalException("error"); - downloader.tryCompleteRequest(); + @BeforeEach + void init() { + setDownloadErrorField(exception); + } - verify(downloader, never()).completeRequest(); + @Test + void shouldThrowException() { + assertThatThrownBy(downloader::tryCompleteRequest).isSameAs(exception); + } } - } - - @Nested - class TestShouldCompleteRequest { @Nested - class TestWhenDownloadFinished { + class OnDownloadFinished { @BeforeEach void init() { setDownloadFinishedField(true); + doNothing().when(downloader).completeRequestNormally(); } @Test - void shouldReturnTrue() { - var result = downloader.shouldCompleteRequest(); + void shouldNotCompleteRequestWithError() { + downloader.tryCompleteRequest(); - assertThat(result).isTrue(); + verify(downloader, never()).completeRequestWithError(any()); } @Test - void shouldReturnFalseIfRequestFinished() { - setRequestFinishedField(true); - - var result = downloader.shouldCompleteRequest(); + void shouldCompleteRequestNormally() { + downloader.tryCompleteRequest(); - assertThat(result).isFalse(); - } - - @Test - void shouldUpdateRequestFinished() { - downloader.shouldCompleteRequest(); - - assertThat(getRequestFinished()).isTrue(); + verify(downloader).completeRequestNormally(); } } @Nested - class TestWhenDownloadRunning { + class OnDownloadNotFinished { @BeforeEach void init() { @@ -580,45 +516,48 @@ class GrpcBinaryFileServerDownloaderTest { } @Test - void shouldReturnFalse() { - var result = downloader.shouldCompleteRequest(); + void shouldNotCompleteRequestNormally() { + downloader.tryCompleteRequest(); - assertThat(result).isFalse(); + verify(downloader, never()).completeRequestNormally(); } @Test - void shouldNotUpdateRequestFinished() { - downloader.shouldCompleteRequest(); + void shouldNotCompleteRequestWithError() { + downloader.tryCompleteRequest(); - assertThat(getRequestFinished()).isFalse(); + verify(downloader, never()).completeRequestWithError(any()); } } } @Nested - class TestCompleteRequest { - - @Mock - private PipedInputStream inputStream; + class TestCompleteRequestNormally { @BeforeEach - void mock() { - setRequestFinishedField(false); - setDownloadFinishedField(true); - setInputStreamField(inputStream); + void init() { + doNothing().when(downloader).closeInputStream(); } - @SneakyThrows @Test - void shouldCallCloseInputStream() { - downloader.completeRequest(); + void shouldSetRequestFinished() { + assertThat(getRequestFinished()).isFalse(); + + downloader.completeRequestNormally(); + + assertThat(getRequestFinished()).isTrue(); + } + + @Test + void shouldCloseInputStream() { + downloader.completeRequestNormally(); verify(downloader).closeInputStream(); } @Test - void shouldCallOnCompleted() { - downloader.completeRequest(); + void shouldNotifyObserver() { + downloader.completeRequestNormally(); verify(callObserver).onCompleted(); } @@ -638,23 +577,21 @@ class GrpcBinaryFileServerDownloaderTest { void shouldSetRequestFinished() { assertThat(getRequestFinished()).isFalse(); - downloader.completeRequestWithError(error); + catchException(() -> downloader.completeRequestWithError(error)); assertThat(getRequestFinished()).isTrue(); } @Test void shouldCloseInputStream() { - downloader.completeRequestWithError(error); + catchException(() -> downloader.completeRequestWithError(error)); verify(downloader).closeInputStream(); } @Test - void shouldNotifyCallObserver() { - downloader.completeRequestWithError(error); - - verify(callObserver).onError(error); + void shouldThrowException() { + assertThatThrownBy(() -> downloader.completeRequestWithError(error)).isSameAs(error); } } @@ -674,12 +611,12 @@ class GrpcBinaryFileServerDownloaderTest { return ReflectionTestUtils.getField(downloader, "requestFinished", AtomicBoolean.class).get(); } - private void setErrorField(TechnicalException error) { - ReflectionTestUtils.setField(downloader, "error", new AtomicReference<>(error)); + private void setDownloadErrorField(TechnicalException error) { + ReflectionTestUtils.setField(downloader, "downloadError", new AtomicReference<>(error)); } - private TechnicalException getError() { - return (TechnicalException) ReflectionTestUtils.getField(downloader, "error", AtomicReference.class).get(); + private TechnicalException getDownloadError() { + return (TechnicalException) ReflectionTestUtils.getField(downloader, "downloadError", AtomicReference.class).get(); } private void setDownloadFinishedField(boolean downloadFinished) { -- GitLab