39
39
import org .mockito .junit .jupiter .MockitoExtension ;
40
40
41
41
import static org .assertj .core .api .Assertions .assertThat ;
42
+ import static org .assertj .core .api .Assertions .assertThatCode ;
42
43
import static org .assertj .core .api .Assertions .assertThatThrownBy ;
43
44
import static org .mockito .ArgumentMatchers .any ;
44
45
import static org .mockito .Mockito .never ;
@@ -76,29 +77,31 @@ void sendAbortForAnyExceptionWhileWriting() {
76
77
when (mockedS3 .uploadPart (any ()))
77
78
.thenThrow (testException );
78
79
79
- assertThatThrownBy (() -> {
80
- try (final S3MultiPartOutputStream out =
81
- new S3MultiPartOutputStream (BUCKET_NAME , FILE_KEY , 100 , mockedS3 )) {
82
- out .write (new byte [] {1 , 2 , 3 });
83
- }
84
- }).isInstanceOf (IOException .class ).hasCause (testException );
80
+ final var out = new S3MultiPartOutputStream (BUCKET_NAME , FILE_KEY , 1 , mockedS3 );
81
+ assertThatThrownBy (() -> out .write (new byte [] {1 , 2 , 3 }))
82
+ .isInstanceOf (IOException .class )
83
+ .hasRootCause (testException );
84
+
85
+ assertThat (out .isClosed ()).isTrue ();
86
+ // retry close to validate no exception is thrown and number of calls to complete/upload does not change
87
+ assertThatCode (out ::close ).doesNotThrowAnyException ();
85
88
86
89
verify (mockedS3 ).initiateMultipartUpload (any (InitiateMultipartUploadRequest .class ));
87
90
verify (mockedS3 ).uploadPart (any (UploadPartRequest .class ));
91
+ verify (mockedS3 , never ()).completeMultipartUpload (any (CompleteMultipartUploadRequest .class ));
88
92
verify (mockedS3 ).abortMultipartUpload (abortMultipartUploadRequestCaptor .capture ());
89
93
90
94
assertAbortMultipartUploadRequest (abortMultipartUploadRequestCaptor .getValue ());
91
95
}
92
96
93
97
@ Test
94
- void sendAbortForAnyExceptionWhenClose () throws Exception {
98
+ void sendAbortForAnyExceptionWhenClosingUpload () throws Exception {
95
99
when (mockedS3 .initiateMultipartUpload (any ()))
96
100
.thenReturn (newInitiateMultipartUploadResult ());
97
-
98
101
when (mockedS3 .uploadPart (any ()))
99
102
.thenThrow (RuntimeException .class );
100
103
101
- final S3MultiPartOutputStream out = new S3MultiPartOutputStream (BUCKET_NAME , FILE_KEY , 10 , mockedS3 );
104
+ final var out = new S3MultiPartOutputStream (BUCKET_NAME , FILE_KEY , 10 , mockedS3 );
102
105
103
106
final byte [] buffer = new byte [5 ];
104
107
random .nextBytes (buffer );
@@ -109,12 +112,45 @@ void sendAbortForAnyExceptionWhenClose() throws Exception {
109
112
.rootCause ()
110
113
.isInstanceOf (RuntimeException .class );
111
114
115
+ assertThat (out .isClosed ()).isTrue ();
116
+ assertThatCode (out ::close ).doesNotThrowAnyException ();
117
+
112
118
verify (mockedS3 , never ()).completeMultipartUpload (any (CompleteMultipartUploadRequest .class ));
113
119
verify (mockedS3 ).abortMultipartUpload (abortMultipartUploadRequestCaptor .capture ());
114
120
115
121
assertAbortMultipartUploadRequest (abortMultipartUploadRequestCaptor .getValue ());
116
122
}
117
123
124
+ @ Test
125
+ void sendAbortForAnyExceptionWhenClosingComplete () throws Exception {
126
+ when (mockedS3 .initiateMultipartUpload (any ()))
127
+ .thenReturn (newInitiateMultipartUploadResult ());
128
+ when (mockedS3 .uploadPart (any ()))
129
+ .thenReturn (newUploadPartResult (1 , "SOME_ETAG#1" ));
130
+ when (mockedS3 .completeMultipartUpload (any ()))
131
+ .thenThrow (RuntimeException .class );
132
+
133
+ final var out = new S3MultiPartOutputStream (BUCKET_NAME , FILE_KEY , 10 , mockedS3 );
134
+
135
+ final byte [] buffer = new byte [5 ];
136
+ random .nextBytes (buffer );
137
+ out .write (buffer , 0 , buffer .length );
138
+
139
+ assertThatThrownBy (out ::close )
140
+ .isInstanceOf (IOException .class )
141
+ .rootCause ()
142
+ .isInstanceOf (RuntimeException .class );
143
+
144
+ assertThat (out .isClosed ()).isTrue ();
145
+ assertThatCode (out ::close ).doesNotThrowAnyException ();
146
+
147
+ verify (mockedS3 ).uploadPart (any (UploadPartRequest .class ));
148
+ verify (mockedS3 ).completeMultipartUpload (any (CompleteMultipartUploadRequest .class ));
149
+ verify (mockedS3 ).abortMultipartUpload (abortMultipartUploadRequestCaptor .capture ());
150
+
151
+ assertAbortMultipartUploadRequest (abortMultipartUploadRequestCaptor .getValue ());
152
+ }
153
+
118
154
@ Test
119
155
void writesOneByte () throws Exception {
120
156
when (mockedS3 .initiateMultipartUpload (any ()))
@@ -124,9 +160,12 @@ void writesOneByte() throws Exception {
124
160
when (mockedS3 .completeMultipartUpload (any ()))
125
161
.thenReturn (new CompleteMultipartUploadResult ());
126
162
127
- try (final S3MultiPartOutputStream out = new S3MultiPartOutputStream (BUCKET_NAME , FILE_KEY , 100 , mockedS3 )) {
128
- out .write (1 );
129
- }
163
+ final var out = new S3MultiPartOutputStream (BUCKET_NAME , FILE_KEY , 100 , mockedS3 );
164
+ out .write (1 );
165
+ out .close ();
166
+
167
+ assertThat (out .isClosed ()).isTrue ();
168
+ assertThatCode (out ::close ).doesNotThrowAnyException ();
130
169
131
170
verify (mockedS3 ).initiateMultipartUpload (any (InitiateMultipartUploadRequest .class ));
132
171
verify (mockedS3 ).uploadPart (uploadPartRequestCaptor .capture ());
@@ -136,7 +175,8 @@ void writesOneByte() throws Exception {
136
175
uploadPartRequestCaptor .getValue (),
137
176
1 ,
138
177
1 ,
139
- new byte [] {1 });
178
+ new byte [] {1 }
179
+ );
140
180
assertCompleteMultipartUploadRequest (
141
181
completeMultipartUploadRequestCaptor .getValue (),
142
182
List .of (new PartETag (1 , "SOME_ETAG" ))
@@ -159,14 +199,16 @@ void writesMultipleMessages() throws Exception {
159
199
.thenReturn (new CompleteMultipartUploadResult ());
160
200
161
201
final List <byte []> expectedMessagesList = new ArrayList <>();
162
- try (final S3MultiPartOutputStream out =
163
- new S3MultiPartOutputStream (BUCKET_NAME , FILE_KEY , bufferSize , mockedS3 )) {
164
- for (int i = 0 ; i < 3 ; i ++) {
165
- random .nextBytes (message );
166
- out .write (message , 0 , message .length );
167
- expectedMessagesList .add (message );
168
- }
202
+ final var out = new S3MultiPartOutputStream (BUCKET_NAME , FILE_KEY , bufferSize , mockedS3 );
203
+ for (int i = 0 ; i < 3 ; i ++) {
204
+ random .nextBytes (message );
205
+ out .write (message , 0 , message .length );
206
+ expectedMessagesList .add (message );
169
207
}
208
+ out .close ();
209
+
210
+ assertThat (out .isClosed ()).isTrue ();
211
+ assertThatCode (out ::close ).doesNotThrowAnyException ();
170
212
171
213
verify (mockedS3 ).initiateMultipartUpload (any (InitiateMultipartUploadRequest .class ));
172
214
verify (mockedS3 , times (3 )).uploadPart (uploadPartRequestCaptor .capture ());
@@ -215,8 +257,7 @@ void writesTailMessages() throws Exception {
215
257
final byte [] expectedFullMessage = new byte [messageSize + 10 ];
216
258
final byte [] expectedTailMessage = new byte [10 ];
217
259
218
- final S3MultiPartOutputStream
219
- out = new S3MultiPartOutputStream (BUCKET_NAME , FILE_KEY , messageSize + 10 , mockedS3 );
260
+ final var out = new S3MultiPartOutputStream (BUCKET_NAME , FILE_KEY , messageSize + 10 , mockedS3 );
220
261
random .nextBytes (message );
221
262
out .write (message );
222
263
System .arraycopy (message , 0 , expectedFullMessage , 0 , message .length );
@@ -226,16 +267,51 @@ void writesTailMessages() throws Exception {
226
267
System .arraycopy (message , 10 , expectedTailMessage , 0 , 10 );
227
268
out .close ();
228
269
270
+ assertThat (out .isClosed ()).isTrue ();
271
+ assertThatCode (out ::close ).doesNotThrowAnyException ();
272
+
229
273
assertUploadPartRequest (uploadPartRequests .get (0 ), 30 , 1 , expectedFullMessage );
230
274
assertUploadPartRequest (uploadPartRequests .get (1 ), 10 , 2 , expectedTailMessage );
231
275
232
276
verify (mockedS3 ).initiateMultipartUpload (any (InitiateMultipartUploadRequest .class ));
233
277
verify (mockedS3 , times (2 )).uploadPart (any (UploadPartRequest .class ));
234
- verify (mockedS3 ).completeMultipartUpload (completeMultipartUploadRequestCaptor .capture ());
278
+ verify (mockedS3 , times ( 1 ) ).completeMultipartUpload (completeMultipartUploadRequestCaptor .capture ());
235
279
assertCompleteMultipartUploadRequest (completeMultipartUploadRequestCaptor .getValue (),
236
280
List .of (new PartETag (1 , "SOME_ETAG#1" ), new PartETag (2 , "SOME_ETAG#2" )));
237
281
}
238
282
283
+ @ Test
284
+ void sendAbortIfNoWritingHappened () throws IOException {
285
+ when (mockedS3 .initiateMultipartUpload (any ()))
286
+ .thenReturn (newInitiateMultipartUploadResult ());
287
+
288
+ final var out = new S3MultiPartOutputStream (BUCKET_NAME , FILE_KEY , 100 , mockedS3 );
289
+ out .close ();
290
+
291
+ verify (mockedS3 ).abortMultipartUpload (abortMultipartUploadRequestCaptor .capture ());
292
+ assertAbortMultipartUploadRequest (abortMultipartUploadRequestCaptor .getValue ());
293
+ assertThat (out .isClosed ()).isTrue ();
294
+ assertThatCode (out ::close ).doesNotThrowAnyException ();
295
+ }
296
+
297
+ @ Test
298
+ void failWhenUploadingPartAfterStreamIsClosed () throws IOException {
299
+ when (mockedS3 .initiateMultipartUpload (any ()))
300
+ .thenReturn (newInitiateMultipartUploadResult ());
301
+
302
+ final var out = new S3MultiPartOutputStream (BUCKET_NAME , FILE_KEY , 100 , mockedS3 );
303
+ out .close ();
304
+
305
+ verify (mockedS3 ).abortMultipartUpload (abortMultipartUploadRequestCaptor .capture ());
306
+ assertAbortMultipartUploadRequest (abortMultipartUploadRequestCaptor .getValue ());
307
+ assertThat (out .isClosed ()).isTrue ();
308
+ assertThatCode (out ::close ).doesNotThrowAnyException ();
309
+
310
+ assertThatThrownBy (() -> out .write (1 ))
311
+ .isInstanceOf (IllegalStateException .class )
312
+ .hasMessage ("Already closed" );
313
+ }
314
+
239
315
private static InitiateMultipartUploadResult newInitiateMultipartUploadResult () {
240
316
final InitiateMultipartUploadResult initiateMultipartUploadResult = new InitiateMultipartUploadResult ();
241
317
initiateMultipartUploadResult .setUploadId (UPLOAD_ID );
0 commit comments