From 8cf51f319ff8c7e163d799d27cf0672b70cf7e33 Mon Sep 17 00:00:00 2001 From: Klemens Muthmann Date: Thu, 21 Mar 2024 04:29:37 +0100 Subject: [PATCH] [LEIP-210] Fix error message on wrongly sized upload (#172) Also added a test to verify this in the future. --- .../cloud/GoogleCloudStorageService.kt | 8 ++-- .../ContentRangeNotMatchingFileSize.kt | 14 +++++- .../storage/cloud/GoogleCloudStorageTest.kt | 47 +++++++++++++++++++ 3 files changed, 65 insertions(+), 4 deletions(-) diff --git a/src/main/kotlin/de/cyface/collector/storage/cloud/GoogleCloudStorageService.kt b/src/main/kotlin/de/cyface/collector/storage/cloud/GoogleCloudStorageService.kt index 43ca0d0d..5bb87dfa 100644 --- a/src/main/kotlin/de/cyface/collector/storage/cloud/GoogleCloudStorageService.kt +++ b/src/main/kotlin/de/cyface/collector/storage/cloud/GoogleCloudStorageService.kt @@ -67,7 +67,8 @@ class GoogleCloudStorageService( val subscriber = CloudStorageSubscriber(cloud) targetStream.subscribe(subscriber) - pipe.to(targetStream).onSuccess { + val pipeToProcess = pipe.to(targetStream) + pipeToProcess.onSuccess { // if finished store the metadata to Mongo and delete the tmp file. val bytesUploaded = cloud.bytesUploaded() val contentRange = uploadMetaData.contentRange @@ -76,7 +77,7 @@ class GoogleCloudStorageService( ContentRangeNotMatchingFileSize( String.format( Locale.getDefault(), - "Response: 500, Content-Range ({}) not matching file size ({})", + "Response: 500, Content-Range (%s) not matching file size (%s)", contentRange, bytesUploaded ) @@ -100,7 +101,8 @@ class GoogleCloudStorageService( ) ) } - }.onFailure(ret::fail) + } + pipeToProcess.onFailure(ret::fail) return ret.future() } diff --git a/src/main/kotlin/de/cyface/collector/storage/exception/ContentRangeNotMatchingFileSize.kt b/src/main/kotlin/de/cyface/collector/storage/exception/ContentRangeNotMatchingFileSize.kt index 88e2e348..025f8b54 100644 --- a/src/main/kotlin/de/cyface/collector/storage/exception/ContentRangeNotMatchingFileSize.kt +++ b/src/main/kotlin/de/cyface/collector/storage/exception/ContentRangeNotMatchingFileSize.kt @@ -18,6 +18,8 @@ */ package de.cyface.collector.storage.exception +import java.util.Objects + /** * An `Exception` thrown when the provided content range of an uploaded file does not correspond to the content range, * provided via the HTTP header. @@ -26,4 +28,14 @@ package de.cyface.collector.storage.exception * @version 1.0.1 * @constructor Provide an explanation for the error via the `format` parameter. */ -class ContentRangeNotMatchingFileSize(format: String) : Exception(format) +class ContentRangeNotMatchingFileSize(format: String) : Exception(format) { + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other !is ContentRangeNotMatchingFileSize) return false + return this.message == other.message + } + + override fun hashCode(): Int { + return Objects.hash(javaClass.hashCode(), message) + } +} diff --git a/src/test/kotlin/de/cyface/collector/storage/cloud/GoogleCloudStorageTest.kt b/src/test/kotlin/de/cyface/collector/storage/cloud/GoogleCloudStorageTest.kt index 697333b6..1cfc3c76 100644 --- a/src/test/kotlin/de/cyface/collector/storage/cloud/GoogleCloudStorageTest.kt +++ b/src/test/kotlin/de/cyface/collector/storage/cloud/GoogleCloudStorageTest.kt @@ -27,11 +27,13 @@ import de.cyface.collector.model.RequestMetaData import de.cyface.collector.model.User import de.cyface.collector.storage.StatusType import de.cyface.collector.storage.UploadMetaData +import de.cyface.collector.storage.exception.ContentRangeNotMatchingFileSize import io.vertx.core.Future import io.vertx.core.Handler import io.vertx.core.Vertx import io.vertx.core.buffer.Buffer import io.vertx.core.streams.Pipe +import io.vertx.core.streams.WriteStream import io.vertx.ext.reactivestreams.ReactiveWriteStream import org.mockito.kotlin.any import org.mockito.kotlin.argumentCaptor @@ -210,6 +212,51 @@ class GoogleCloudStorageTest { verify(mockStorage).delete(testDataFile) } + /** + * Tests that uploading a file with a wrong size actually produces the correct error message. + */ + @Test + fun `A Mismatch between file size and content range should fail`() { + // Arrange + val database: Database = mock() + val vertx: Vertx = mock() + val mockCloudStorage: CloudStorage = mock { + on { bytesUploaded() } doReturn 4 + } + val cloudStorageFactory: CloudStorageFactory = mock { + on { create(any()) } doReturn mockCloudStorage + } + val oocut = GoogleCloudStorageService(database, vertx, cloudStorageFactory) + val mockToFuture: Future = mock() + val mockPipe: Pipe = mock { + on { to(any>()) } doReturn mockToFuture + } + val metadata: UploadMetaData = mock { + on { contentRange } doReturn ContentRange(5, 7, 3) + on { uploadIdentifier } doReturn UUID.randomUUID() + } + + // Act + val result = oocut.store(mockPipe, metadata) + + // Assert + argumentCaptor> { + verify(mockToFuture).onSuccess(capture()) + + firstValue.handle(null) + } + assertTrue(result.isComplete) + assertTrue(result.failed()) + assertEquals( + ContentRangeNotMatchingFileSize( + """ + Response: 500, Content-Range (ContentRange(fromIndex=5, toIndex=7, totalBytes=3)) not matching file size (4) + """.trim() + ), + result.cause(), + ) + } + /** * Provide some example metadata usable by tests. */