Skip to content
Snippets Groups Projects

Ozg 7262 fix unfinished downloads

Merged Krzysztof Witukiewicz requested to merge OZG-7262-fix-unfinished-downloads into main
1 file
+ 1
69
Compare changes
  • Side-by-side
  • Inline
@@ -23,6 +23,7 @@
@@ -23,6 +23,7 @@
*/
*/
package de.ozgcloud.common.binaryfile;
package de.ozgcloud.common.binaryfile;
 
import static de.ozgcloud.common.binaryfile.GrpcBinaryFileServerDownloader.*;
import static org.assertj.core.api.Assertions.*;
import static org.assertj.core.api.Assertions.*;
import static org.mockito.ArgumentMatchers.*;
import static org.mockito.ArgumentMatchers.*;
import static org.mockito.Mockito.*;
import static org.mockito.Mockito.*;
@@ -37,11 +38,14 @@ import java.util.concurrent.atomic.AtomicBoolean;
@@ -37,11 +38,14 @@ import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Consumer;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Function;
 
import java.util.stream.Stream;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.Test;
 
import org.junit.jupiter.params.ParameterizedTest;
 
import org.junit.jupiter.params.provider.Arguments;
 
import org.junit.jupiter.params.provider.MethodSource;
import org.mockito.ArgumentCaptor;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.Mock;
@@ -300,18 +304,6 @@ class GrpcBinaryFileServerDownloaderTest {
@@ -300,18 +304,6 @@ class GrpcBinaryFileServerDownloaderTest {
verify(downloadConsumer).accept(outputStream);
verify(downloadConsumer).accept(outputStream);
}
}
@Test
void shouldDownloadFinishedBeInitiallyFalse() {
assertThat(getDownloadFinished()).isFalse();
}
@Test
void shouldSetDownloadFinished() {
downloader.doDownload();
assertThat(getDownloadFinished()).isTrue();
}
}
}
@Nested
@Nested
@@ -346,14 +338,14 @@ class GrpcBinaryFileServerDownloaderTest {
@@ -346,14 +338,14 @@ class GrpcBinaryFileServerDownloaderTest {
@BeforeEach
@BeforeEach
void init() {
void init() {
doThrow(exception).when(downloader).doSendChunks();
doThrow(exception).when(downloader).doSendChunks();
doNothing().when(downloader).completeRequestWithError(any());
doNothing().when(downloader).handleError(any());
}
}
@Test
@Test
void shouldCompleteRequestWithError() {
void shouldHandleError() {
downloader.sendChunks();
downloader.sendChunks();
verify(downloader).completeRequestWithError(argumentCaptor.capture());
verify(downloader).handleError(argumentCaptor.capture());
assertThat(argumentCaptor.getValue()).isInstanceOf(TechnicalException.class).hasCause(exception);
assertThat(argumentCaptor.getValue()).isInstanceOf(TechnicalException.class).hasCause(exception);
}
}
}
}
@@ -362,103 +354,174 @@ class GrpcBinaryFileServerDownloaderTest {
@@ -362,103 +354,174 @@ class GrpcBinaryFileServerDownloaderTest {
@Nested
@Nested
class TestDoSendChunks {
class TestDoSendChunks {
 
@SneakyThrows
 
@Test
 
void shouldCheckIfCanSendChunks() {
 
doReturn(false).when(downloader).canSendChunks();
 
 
downloader.doSendChunks();
 
 
verify(downloader).canSendChunks();
 
}
 
 
@SneakyThrows
 
@Test
 
void shouldNotProcessIfCannotSendChunks() {
 
doReturn(false).when(downloader).canSendChunks();
 
 
downloader.doSendChunks();
 
 
verify(downloader, never()).processDataFromInputStream();
 
}
 
 
@SneakyThrows
 
@Test
 
void shouldProcessAsLongAsCanSendChunks() {
 
doReturn(true, true, false).when(downloader).canSendChunks();
 
doNothing().when(downloader).processDataFromInputStream();
 
 
downloader.doSendChunks();
 
 
verify(downloader, times(2)).processDataFromInputStream();
 
}
 
}
 
@Nested
@Nested
class OnRequestFinished {
class TestProcessDataFromInputStream {
 
 
@Mock
 
private PipedInputStream inputStream;
 
@Nested
 
class OnEndOfStreamReached {
 
 
@SneakyThrows
@BeforeEach
@BeforeEach
void init() {
void init() {
setRequestFinishedField(true);
doNothing().when(downloader).finishProcessing();
 
when(inputStream.read(any())).thenReturn(END_OF_STREAM);
 
setInputStreamField(inputStream);
}
}
 
@SneakyThrows
 
@Test
 
void shouldFinishProcessing() {
 
downloader.processDataFromInputStream();
 
 
verify(downloader).finishProcessing();
 
}
 
 
@SneakyThrows
@Test
@Test
void shouldNotInteractWithCallObserver() {
void shouldNotSendBytesToCallObserver() {
doSendChunks();
downloader.processDataFromInputStream();
verifyNoInteractions(callObserver);
verify(downloader, never()).sendBytesToCallObserver(anyInt());
}
}
}
}
@Nested
@Nested
class OnRequestNotFinished {
class OnNoBytesWereReceived {
 
 
@SneakyThrows
 
@BeforeEach
 
void init() {
 
when(inputStream.read(any())).thenReturn(NOTHING_READ);
 
setInputStreamField(inputStream);
 
}
 
 
@SneakyThrows
 
@Test
 
void shouldNotSendBytesToCallObserver() {
 
downloader.processDataFromInputStream();
 
 
verify(downloader, never()).sendBytesToCallObserver(anyInt());
 
}
 
}
@Nested
@Nested
class OnNotReady {
class OnBytesWereReceived {
 
 
private final int bytesRead = 20;
 
@SneakyThrows
@BeforeEach
@BeforeEach
void init() {
void mock() {
when(callObserver.isReady()).thenReturn(false);
when(inputStream.read(any())).thenReturn(bytesRead);
 
setInputStreamField(inputStream);
}
}
 
@SneakyThrows
@Test
@Test
void shouldOnlyCallIsReadyOnObserver() {
void shouldSendBytesToCallObserver() {
doSendChunks();
downloader.processDataFromInputStream();
verify(callObserver).isReady();
verify(downloader).sendBytesToCallObserver(bytesRead);
verifyNoMoreInteractions(callObserver);
}
}
}
}
}
@Nested
@Nested
class OnReady {
class TestSendBytesToCallObserver {
@Mock
private PipedInputStream inputStream;
@Captor
@Captor
private ArgumentCaptor<ByteString> byteStringCaptor;
private ArgumentCaptor<ByteString> byteStringCaptor;
private final int bytesRead = 20;
private final int readBytes = 20;
private final byte[] buffer = new byte[bytesRead];
private final byte[] buffer = new byte[readBytes];
private final GrpcResponseDummy grpcResponseDummy = new GrpcResponseDummy();
private final GrpcResponseDummy grpcResponseDummy = new GrpcResponseDummy();
@SneakyThrows
@BeforeEach
@BeforeEach
void mock() {
void init() {
doNothing().when(downloader).tryCompleteRequest();
when(callObserver.isReady()).thenReturn(true);
when(inputStream.read(any())).thenReturn(readBytes, -1);
setInputStreamField(inputStream);
new Random().nextBytes(buffer);
new Random().nextBytes(buffer);
ReflectionTestUtils.setField(downloader, "buffer", buffer);
ReflectionTestUtils.setField(downloader, "buffer", buffer);
}
}
 
@SneakyThrows
@Test
@Test
void shouldCallChunkBuilder() {
void shouldCallChunkBuilder() {
doSendChunks();
downloader.sendBytesToCallObserver(bytesRead);
verify(chunkBuilder).apply(byteStringCaptor.capture());
verify(chunkBuilder).apply(byteStringCaptor.capture());
assertThat(byteStringCaptor.getValue().toByteArray()).isEqualTo(buffer);
assertThat(byteStringCaptor.getValue().toByteArray()).isEqualTo(buffer);
}
}
@DisplayName("should send next chunk if callObserver is ready and stream already received data")
@SneakyThrows
@Test
@Test
void shouldCallOnNext() {
void shouldCallOnNext() {
when(chunkBuilder.apply(any())).thenReturn(grpcResponseDummy);
when(chunkBuilder.apply(any())).thenReturn(grpcResponseDummy);
doSendChunks();
downloader.sendBytesToCallObserver(bytesRead);
verify(callObserver).onNext(grpcResponseDummy);
verify(callObserver).onNext(grpcResponseDummy);
}
}
 
}
@DisplayName("should call complete grpc stream if download has finished and stream has no data left")
@Nested
@Test
class TestCanSendChunks {
void shouldTryCompleteRequest() {
setDownloadFinishedField(true);
doSendChunks();
@ParameterizedTest
 
@MethodSource("provideArguments")
 
void shouldReturnValue(boolean requestFinished, boolean ready, boolean expected) {
 
setRequestFinishedField(requestFinished);
 
lenient().when(callObserver.isReady()).thenReturn(ready);
verify(downloader).tryCompleteRequest();
var canSendChunks = downloader.canSendChunks();
}
}
assertThat(canSendChunks).isEqualTo(expected);
}
}
@SneakyThrows
private static Stream<Arguments> provideArguments() {
private void doSendChunks() {
return Stream.of(
downloader.doSendChunks();
Arguments.of(false, false, false),
 
Arguments.of(false, true, true),
 
Arguments.of(true, false, false),
 
Arguments.of(true, true, false)
 
);
}
}
}
}
@Nested
@Nested
class TestTryCompleteRequest {
class TestFinishProcessing {
@Nested
@Nested
class OnError {
class OnError {
@@ -472,92 +535,52 @@ class GrpcBinaryFileServerDownloaderTest {
@@ -472,92 +535,52 @@ class GrpcBinaryFileServerDownloaderTest {
@Test
@Test
void shouldThrowException() {
void shouldThrowException() {
assertThatThrownBy(downloader::tryCompleteRequest).isSameAs(exception);
assertThatThrownBy(downloader::finishProcessing).isSameAs(exception);
}
}
}
}
@Nested
@Nested
class OnDownloadFinished {
class OnNoError {
@BeforeEach
@BeforeEach
void init() {
void init() {
setDownloadFinishedField(true);
doNothing().when(downloader).closeInputStream();
doNothing().when(downloader).completeRequestNormally();
}
@Test
void shouldNotCompleteRequestWithError() {
downloader.tryCompleteRequest();
verify(downloader, never()).completeRequestWithError(any());
}
@Test
void shouldCompleteRequestNormally() {
downloader.tryCompleteRequest();
verify(downloader).completeRequestNormally();
}
}
@Nested
class OnDownloadNotFinished {
@BeforeEach
void init() {
setDownloadFinishedField(false);
}
@Test
void shouldNotCompleteRequestNormally() {
downloader.tryCompleteRequest();
verify(downloader, never()).completeRequestNormally();
}
}
@Test
@Test
void shouldNotCompleteRequestWithError() {
void shouldNotCompleteRequestWithError() {
downloader.tryCompleteRequest();
downloader.finishProcessing();
verify(downloader, never()).completeRequestWithError(any());
verify(downloader, never()).handleError(any());
}
}
}
@Nested
class TestCompleteRequestNormally {
@BeforeEach
void init() {
doNothing().when(downloader).closeInputStream();
}
}
@Test
@Test
void shouldSetRequestFinished() {
void shouldSetRequestFinished() {
assertThat(getRequestFinished()).isFalse();
assertThat(getRequestFinished()).isFalse();
downloader.completeRequestNormally();
downloader.finishProcessing();
assertThat(getRequestFinished()).isTrue();
assertThat(getRequestFinished()).isTrue();
}
}
@Test
@Test
void shouldCloseInputStream() {
void shouldCloseInputStream() {
downloader.completeRequestNormally();
downloader.finishProcessing();
verify(downloader).closeInputStream();
verify(downloader).closeInputStream();
}
}
@Test
@Test
void shouldNotifyObserver() {
void shouldNotifyObserver() {
downloader.completeRequestNormally();
downloader.finishProcessing();
verify(callObserver).onCompleted();
verify(callObserver).onCompleted();
}
}
}
}
 
}
@Nested
@Nested
class TestCompleteRequestWithError {
class TestHandleError {
private final TechnicalException error = new TechnicalException("error");
private final TechnicalException error = new TechnicalException("error");
@@ -570,21 +593,21 @@ class GrpcBinaryFileServerDownloaderTest {
@@ -570,21 +593,21 @@ class GrpcBinaryFileServerDownloaderTest {
void shouldSetRequestFinished() {
void shouldSetRequestFinished() {
assertThat(getRequestFinished()).isFalse();
assertThat(getRequestFinished()).isFalse();
catchException(() -> downloader.completeRequestWithError(error));
catchException(() -> downloader.handleError(error));
assertThat(getRequestFinished()).isTrue();
assertThat(getRequestFinished()).isTrue();
}
}
@Test
@Test
void shouldCloseInputStream() {
void shouldCloseInputStream() {
catchException(() -> downloader.completeRequestWithError(error));
catchException(() -> downloader.handleError(error));
verify(downloader).closeInputStream();
verify(downloader).closeInputStream();
}
}
@Test
@Test
void shouldThrowException() {
void shouldThrowException() {
assertThatThrownBy(() -> downloader.completeRequestWithError(error)).isSameAs(error);
assertThatThrownBy(() -> downloader.handleError(error)).isSameAs(error);
}
}
}
}
@@ -612,14 +635,6 @@ class GrpcBinaryFileServerDownloaderTest {
@@ -612,14 +635,6 @@ class GrpcBinaryFileServerDownloaderTest {
return (TechnicalException) ReflectionTestUtils.getField(downloader, "downloadError", AtomicReference.class).get();
return (TechnicalException) ReflectionTestUtils.getField(downloader, "downloadError", AtomicReference.class).get();
}
}
private void setDownloadFinishedField(boolean downloadFinished) {
ReflectionTestUtils.setField(downloader, "downloadFinished", new AtomicBoolean(downloadFinished));
}
private boolean getDownloadFinished() {
return ReflectionTestUtils.getField(downloader, "downloadFinished", AtomicBoolean.class).get();
}
private static class GrpcResponseDummy {
private static class GrpcResponseDummy {
}
}
}
}
\ No newline at end of file
Loading