|
16 | 16 | package software.amazon.awssdk.services.s3.internal.multipart; |
17 | 17 |
|
18 | 18 | import static org.assertj.core.api.Assertions.assertThat; |
| 19 | +import static org.assertj.core.api.Assertions.assertThatThrownBy; |
19 | 20 | import static org.mockito.ArgumentMatchers.any; |
20 | 21 | import static org.mockito.ArgumentMatchers.eq; |
21 | 22 | import static org.mockito.Mockito.mock; |
|
24 | 25 | import static org.mockito.Mockito.when; |
25 | 26 | import static software.amazon.awssdk.services.s3.internal.multipart.utils.MultipartUploadTestUtils.stubSuccessfulCompleteMultipartCall; |
26 | 27 | import static software.amazon.awssdk.services.s3.internal.multipart.utils.MultipartUploadTestUtils.stubSuccessfulCreateMultipartCall; |
| 28 | +import static software.amazon.awssdk.services.s3.internal.multipart.utils.MultipartUploadTestUtils.stubSuccessfulPutObjectCall; |
27 | 29 | import static software.amazon.awssdk.services.s3.internal.multipart.utils.MultipartUploadTestUtils.stubSuccessfulUploadPartCalls; |
28 | 30 |
|
29 | 31 | import java.io.FileInputStream; |
|
55 | 57 | import software.amazon.awssdk.services.s3.model.PutObjectResponse; |
56 | 58 | import software.amazon.awssdk.services.s3.model.UploadPartRequest; |
57 | 59 | import software.amazon.awssdk.testutils.RandomTempFile; |
| 60 | +import software.amazon.awssdk.utils.StringInputStream; |
58 | 61 |
|
59 | 62 | public class UploadWithUnknownContentLengthHelperTest { |
60 | 63 | private static final String BUCKET = "bucket"; |
@@ -116,13 +119,63 @@ void uploadObject_withMissingContentLength_shouldFailRequest() { |
116 | 119 | verifyFailureWithMessage(future, "Content length is missing on the AsyncRequestBody for part number"); |
117 | 120 | } |
118 | 121 |
|
| 122 | + @Test |
| 123 | + void uploadObject_emptyBody_shouldSucceed() { |
| 124 | + stubSuccessfulPutObjectCall(s3AsyncClient); |
| 125 | + |
| 126 | + BlockingInputStreamAsyncRequestBody body = AsyncRequestBody.forBlockingInputStream(null); |
| 127 | + CompletableFuture<PutObjectResponse> future = helper.uploadObject(createPutObjectRequest(), body); |
| 128 | + body.writeInputStream(new StringInputStream("")); |
| 129 | + future.join(); |
| 130 | + |
| 131 | + ArgumentCaptor<PutObjectRequest> requestArgumentCaptor = ArgumentCaptor.forClass(PutObjectRequest.class); |
| 132 | + ArgumentCaptor<AsyncRequestBody> requestBodyArgumentCaptor = ArgumentCaptor.forClass(AsyncRequestBody.class); |
| 133 | + verify(s3AsyncClient, times(1)).putObject(requestArgumentCaptor.capture(), |
| 134 | + requestBodyArgumentCaptor.capture()); |
| 135 | + |
| 136 | + List<PutObjectRequest> actualRequests = requestArgumentCaptor.getAllValues(); |
| 137 | + List<AsyncRequestBody> actualRequestBodies = requestBodyArgumentCaptor.getAllValues(); |
| 138 | + assertThat(actualRequestBodies).hasSize(1); |
| 139 | + assertThat(actualRequests).hasSize(1); |
| 140 | + |
| 141 | + PutObjectRequest putObjectRequest = actualRequests.get(0); |
| 142 | + assertThat(putObjectRequest.bucket()).isEqualTo(BUCKET); |
| 143 | + assertThat(putObjectRequest.key()).isEqualTo(KEY); |
| 144 | + assertThat(actualRequestBodies.get(0).contentLength()).hasValue(0L); |
| 145 | + } |
| 146 | + |
119 | 147 | @Test |
120 | 148 | void uploadObject_withPartSizeExceedingLimit_shouldFailRequest() { |
121 | 149 | CloseableAsyncRequestBody asyncRequestBody = createMockAsyncRequestBody(PART_SIZE + 1); |
122 | 150 | CompletableFuture<PutObjectResponse> future = setupAndTriggerUploadFailure(asyncRequestBody); |
123 | 151 | verifyFailureWithMessage(future, "Content length must not be greater than part size"); |
124 | 152 | } |
125 | 153 |
|
| 154 | + @Test |
| 155 | + void uploadObject_nullAsyncRequestBody_shouldFailRequest() { |
| 156 | + CloseableAsyncRequestBody asyncRequestBody = createMockAsyncRequestBody(PART_SIZE); |
| 157 | + SdkPublisher<CloseableAsyncRequestBody> mockPublisher = mock(SdkPublisher.class); |
| 158 | + when(asyncRequestBody.splitCloseable(any(Consumer.class))).thenReturn(mockPublisher); |
| 159 | + |
| 160 | + ArgumentCaptor<Subscriber<CloseableAsyncRequestBody>> subscriberCaptor = ArgumentCaptor.forClass(Subscriber.class); |
| 161 | + CompletableFuture<PutObjectResponse> future = helper.uploadObject(createPutObjectRequest(), asyncRequestBody); |
| 162 | + |
| 163 | + verify(mockPublisher).subscribe(subscriberCaptor.capture()); |
| 164 | + Subscriber<CloseableAsyncRequestBody> subscriber = subscriberCaptor.getValue(); |
| 165 | + |
| 166 | + Subscription subscription = mock(Subscription.class); |
| 167 | + subscriber.onSubscribe(subscription); |
| 168 | + assertThatThrownBy(() -> subscriber.onNext(null)).isInstanceOf(NullPointerException.class).hasMessageContaining( |
| 169 | + "asyncRequestBody"); |
| 170 | + |
| 171 | + assertThat(future).isCompletedExceptionally(); |
| 172 | + future.exceptionally(throwable -> { |
| 173 | + assertThat(throwable.getCause()).isInstanceOf(NullPointerException.class); |
| 174 | + assertThat(throwable.getCause().getMessage()).contains("MUST NOT be null"); |
| 175 | + return null; |
| 176 | + }).join(); |
| 177 | + } |
| 178 | + |
126 | 179 | private PutObjectRequest createPutObjectRequest() { |
127 | 180 | return PutObjectRequest.builder() |
128 | 181 | .bucket(BUCKET) |
|
0 commit comments