Skip to content
Snippets Groups Projects

Ozg 7262 fix unfinished downloads

Merged Krzysztof Witukiewicz requested to merge OZG-7262-fix-unfinished-downloads into main
All threads resolved!
3 files
+ 393
121
Compare changes
  • Side-by-side
  • Inline

Files

@@ -23,6 +23,7 @@
*/
package de.ozgcloud.common.binaryfile;
import static de.ozgcloud.common.binaryfile.GrpcBinaryFileServerDownloader.*;
import static org.assertj.core.api.Assertions.*;
import static org.mockito.ArgumentMatchers.*;
import static org.mockito.Mockito.*;
@@ -37,11 +38,14 @@ import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.stream.Stream;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Nested;
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.Captor;
import org.mockito.Mock;
@@ -300,18 +304,6 @@ class GrpcBinaryFileServerDownloaderTest {
verify(downloadConsumer).accept(outputStream);
}
@Test
void shouldDownloadFinishedBeInitiallyFalse() {
assertThat(getDownloadFinished()).isFalse();
}
@Test
void shouldSetDownloadFinished() {
downloader.doDownload();
assertThat(getDownloadFinished()).isTrue();
}
}
@Nested
@@ -346,14 +338,14 @@ class GrpcBinaryFileServerDownloaderTest {
@BeforeEach
void init() {
doThrow(exception).when(downloader).doSendChunks();
doNothing().when(downloader).completeRequestWithError(any());
doNothing().when(downloader).handleError(any());
}
@Test
void shouldCompleteRequestWithError() {
void shouldHandleError() {
downloader.sendChunks();
verify(downloader).completeRequestWithError(argumentCaptor.capture());
verify(downloader).handleError(argumentCaptor.capture());
assertThat(argumentCaptor.getValue()).isInstanceOf(TechnicalException.class).hasCause(exception);
}
}
@@ -362,103 +354,174 @@ class GrpcBinaryFileServerDownloaderTest {
@Nested
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
class OnRequestFinished {
class TestProcessDataFromInputStream {
@Mock
private PipedInputStream inputStream;
@Nested
class OnEndOfStreamReached {
@SneakyThrows
@BeforeEach
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
void shouldNotInteractWithCallObserver() {
doSendChunks();
void shouldNotSendBytesToCallObserver() {
downloader.processDataFromInputStream();
verifyNoInteractions(callObserver);
verify(downloader, never()).sendBytesToCallObserver(anyInt());
}
}
@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
class OnNotReady {
class OnBytesWereReceived {
private final int bytesRead = 20;
@SneakyThrows
@BeforeEach
void init() {
when(callObserver.isReady()).thenReturn(false);
void mock() {
when(inputStream.read(any())).thenReturn(bytesRead);
setInputStreamField(inputStream);
}
@SneakyThrows
@Test
void shouldOnlyCallIsReadyOnObserver() {
doSendChunks();
void shouldSendBytesToCallObserver() {
downloader.processDataFromInputStream();
verify(callObserver).isReady();
verifyNoMoreInteractions(callObserver);
verify(downloader).sendBytesToCallObserver(bytesRead);
}
}
}
@Nested
class OnReady {
class TestSendBytesToCallObserver {
@Mock
private PipedInputStream inputStream;
@Captor
private ArgumentCaptor<ByteString> byteStringCaptor;
private final int readBytes = 20;
private final byte[] buffer = new byte[readBytes];
private final int bytesRead = 20;
private final byte[] buffer = new byte[bytesRead];
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);
void init() {
new Random().nextBytes(buffer);
ReflectionTestUtils.setField(downloader, "buffer", buffer);
}
@SneakyThrows
@Test
void shouldCallChunkBuilder() {
doSendChunks();
downloader.sendBytesToCallObserver(bytesRead);
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")
@SneakyThrows
@Test
void shouldCallOnNext() {
when(chunkBuilder.apply(any())).thenReturn(grpcResponseDummy);
doSendChunks();
downloader.sendBytesToCallObserver(bytesRead);
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);
@Nested
class TestCanSendChunks {
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 void doSendChunks() {
downloader.doSendChunks();
private static Stream<Arguments> provideArguments() {
return Stream.of(
Arguments.of(false, false, false),
Arguments.of(false, true, true),
Arguments.of(true, false, false),
Arguments.of(true, true, false)
);
}
}
@Nested
class TestTryCompleteRequest {
class TestFinishProcessing {
@Nested
class OnError {
@@ -472,92 +535,52 @@ class GrpcBinaryFileServerDownloaderTest {
@Test
void shouldThrowException() {
assertThatThrownBy(downloader::tryCompleteRequest).isSameAs(exception);
assertThatThrownBy(downloader::finishProcessing).isSameAs(exception);
}
}
@Nested
class OnDownloadFinished {
class OnNoError {
@BeforeEach
void init() {
setDownloadFinishedField(true);
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();
doNothing().when(downloader).closeInputStream();
}
@Test
void shouldNotCompleteRequestWithError() {
downloader.tryCompleteRequest();
downloader.finishProcessing();
verify(downloader, never()).completeRequestWithError(any());
}
}
}
@Nested
class TestCompleteRequestNormally {
@BeforeEach
void init() {
doNothing().when(downloader).closeInputStream();
verify(downloader, never()).handleError(any());
}
@Test
void shouldSetRequestFinished() {
assertThat(getRequestFinished()).isFalse();
downloader.completeRequestNormally();
downloader.finishProcessing();
assertThat(getRequestFinished()).isTrue();
}
@Test
void shouldCloseInputStream() {
downloader.completeRequestNormally();
downloader.finishProcessing();
verify(downloader).closeInputStream();
}
@Test
void shouldNotifyObserver() {
downloader.completeRequestNormally();
downloader.finishProcessing();
verify(callObserver).onCompleted();
}
}
}
@Nested
class TestCompleteRequestWithError {
class TestHandleError {
private final TechnicalException error = new TechnicalException("error");
@@ -570,21 +593,21 @@ class GrpcBinaryFileServerDownloaderTest {
void shouldSetRequestFinished() {
assertThat(getRequestFinished()).isFalse();
catchException(() -> downloader.completeRequestWithError(error));
catchException(() -> downloader.handleError(error));
assertThat(getRequestFinished()).isTrue();
}
@Test
void shouldCloseInputStream() {
catchException(() -> downloader.completeRequestWithError(error));
catchException(() -> downloader.handleError(error));
verify(downloader).closeInputStream();
}
@Test
void shouldThrowException() {
assertThatThrownBy(() -> downloader.completeRequestWithError(error)).isSameAs(error);
assertThatThrownBy(() -> downloader.handleError(error)).isSameAs(error);
}
}
@@ -612,14 +635,6 @@ class GrpcBinaryFileServerDownloaderTest {
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 {
}
}
\ No newline at end of file
Loading