16
16
17
17
package io .aiven .kafka .tieredstorage .storage .s3 ;
18
18
19
- import java .io .ByteArrayInputStream ;
20
19
import java .io .IOException ;
21
- import java .util .ArrayList ;
22
- import java .util .List ;
20
+ import java .util .HashMap ;
21
+ import java .util .Map ;
23
22
import java .util .Random ;
23
+ import java .util .concurrent .ConcurrentHashMap ;
24
+ import java .util .stream .Collectors ;
24
25
25
26
import com .amazonaws .services .s3 .AmazonS3 ;
26
27
import com .amazonaws .services .s3 .model .AbortMultipartUploadRequest ;
@@ -83,7 +84,7 @@ void sendAbortForAnyExceptionWhileWriting() {
83
84
new S3MultiPartOutputStream (BUCKET_NAME , FILE_KEY , 100 , mockedS3 )) {
84
85
out .write (new byte [] {1 , 2 , 3 });
85
86
}
86
- }).isInstanceOf (IOException .class ).hasCause (testException );
87
+ }).isInstanceOf (IOException .class ).hasRootCause (testException );
87
88
88
89
verify (mockedS3 ).initiateMultipartUpload (any (InitiateMultipartUploadRequest .class ));
89
90
verify (mockedS3 ).uploadPart (any (UploadPartRequest .class ));
@@ -135,102 +136,116 @@ void writesOneByte() throws Exception {
135
136
verify (mockedS3 ).uploadPart (any (UploadPartRequest .class ));
136
137
verify (mockedS3 ).completeMultipartUpload (any (CompleteMultipartUploadRequest .class ));
137
138
139
+ final UploadPartRequest value = uploadPartRequestCaptor .getValue ();
138
140
assertUploadPartRequest (
139
- uploadPartRequestCaptor .getValue (),
141
+ value ,
142
+ value .getInputStream ().readAllBytes (),
140
143
1 ,
141
144
1 ,
142
145
new byte [] {1 });
143
146
assertCompleteMultipartUploadRequest (
144
147
completeMultipartUploadRequestCaptor .getValue (),
145
- List .of (new PartETag ( 1 , "SOME_ETAG" ) )
148
+ Map .of (1 , "SOME_ETAG" )
146
149
);
147
150
}
148
151
149
152
@ Test
150
153
void writesMultipleMessages () throws Exception {
151
154
final int bufferSize = 10 ;
152
- final byte [] message = new byte [bufferSize ];
153
155
154
156
when (mockedS3 .initiateMultipartUpload (any (InitiateMultipartUploadRequest .class )))
155
157
.thenReturn (newInitiateMultipartUploadResult ());
158
+
159
+ final Map <Integer , UploadPartRequest > uploadPartRequests = new ConcurrentHashMap <>();
160
+ final Map <Integer , byte []> uploadPartContents = new ConcurrentHashMap <>();
156
161
when (mockedS3 .uploadPart (uploadPartRequestCaptor .capture ()))
157
- .thenAnswer (a -> {
158
- final UploadPartRequest up = a .getArgument (0 );
162
+ .thenAnswer (answer -> {
163
+ final UploadPartRequest up = answer .getArgument (0 );
164
+ //emulate behave of S3 client otherwise we will get wrong array in the memory
165
+ uploadPartRequests .put (up .getPartNumber (), up );
166
+ uploadPartContents .put (up .getPartNumber (), up .getInputStream ().readAllBytes ());
167
+
159
168
return newUploadPartResult (up .getPartNumber (), "SOME_TAG#" + up .getPartNumber ());
160
169
});
161
170
when (mockedS3 .completeMultipartUpload (completeMultipartUploadRequestCaptor .capture ()))
162
171
.thenReturn (new CompleteMultipartUploadResult ());
163
172
164
- final List < byte []> expectedMessagesList = new ArrayList <>();
173
+ final Map < Integer , byte []> expectedMessageParts = new HashMap <>();
165
174
try (final S3MultiPartOutputStream out =
166
175
new S3MultiPartOutputStream (BUCKET_NAME , FILE_KEY , bufferSize , mockedS3 )) {
167
176
for (int i = 0 ; i < 3 ; i ++) {
177
+ final byte [] message = new byte [bufferSize ];
168
178
random .nextBytes (message );
169
179
out .write (message , 0 , message .length );
170
- expectedMessagesList . add ( message );
180
+ expectedMessageParts . put ( i + 1 , message );
171
181
}
172
182
}
173
183
174
184
verify (mockedS3 ).initiateMultipartUpload (any (InitiateMultipartUploadRequest .class ));
175
185
verify (mockedS3 , times (3 )).uploadPart (any (UploadPartRequest .class ));
176
186
verify (mockedS3 ).completeMultipartUpload (any (CompleteMultipartUploadRequest .class ));
177
187
178
- final List <UploadPartRequest > uploadRequests = uploadPartRequestCaptor .getAllValues ();
179
- int counter = 0 ;
180
- for (final byte [] expectedMessage : expectedMessagesList ) {
188
+ for (final Integer part : expectedMessageParts .keySet ()) {
181
189
assertUploadPartRequest (
182
- uploadRequests .get (counter ),
190
+ uploadPartRequests .get (part ),
191
+ uploadPartContents .get (part ),
183
192
bufferSize ,
184
- counter + 1 ,
185
- expectedMessage );
186
- counter ++ ;
193
+ part ,
194
+ expectedMessageParts . get ( part )
195
+ ) ;
187
196
}
188
197
assertCompleteMultipartUploadRequest (
189
198
completeMultipartUploadRequestCaptor .getValue (),
190
- List .of (new PartETag ( 1 , "SOME_TAG#1" ) ,
191
- new PartETag ( 2 , "SOME_TAG#2" ) ,
192
- new PartETag ( 3 , "SOME_TAG#3" ) )
199
+ Map .of (1 , "SOME_TAG#1" ,
200
+ 2 , "SOME_TAG#2" ,
201
+ 3 , "SOME_TAG#3" )
193
202
);
194
203
}
195
204
196
205
@ Test
197
206
void writesTailMessages () throws Exception {
198
207
final int messageSize = 20 ;
199
208
200
- final List <UploadPartRequest > uploadPartRequests = new ArrayList <>();
209
+ final Map <Integer , UploadPartRequest > uploadPartRequests = new ConcurrentHashMap <>();
210
+ final Map <Integer , byte []> uploadPartContents = new ConcurrentHashMap <>();
201
211
202
212
when (mockedS3 .initiateMultipartUpload (any (InitiateMultipartUploadRequest .class )))
203
213
.thenReturn (newInitiateMultipartUploadResult ());
204
214
when (mockedS3 .uploadPart (any (UploadPartRequest .class )))
205
- .thenAnswer (a -> {
206
- final UploadPartRequest up = a .getArgument (0 );
215
+ .thenAnswer (answer -> {
216
+ final UploadPartRequest up = answer .getArgument (0 );
207
217
//emulate behave of S3 client otherwise we will get wrong array in the memory
208
- up . setInputStream ( new ByteArrayInputStream ( up .getInputStream (). readAllBytes ()) );
209
- uploadPartRequests . add (up );
218
+ uploadPartRequests . put ( up .getPartNumber (), up );
219
+ uploadPartContents . put (up . getPartNumber (), up . getInputStream (). readAllBytes () );
210
220
211
221
return newUploadPartResult (up .getPartNumber (), "SOME_TAG#" + up .getPartNumber ());
212
222
});
213
223
when (mockedS3 .completeMultipartUpload (completeMultipartUploadRequestCaptor .capture ()))
214
224
.thenReturn (new CompleteMultipartUploadResult ());
215
225
216
- final byte [] message = new byte [messageSize ];
217
226
218
227
final byte [] expectedFullMessage = new byte [messageSize + 10 ];
219
228
final byte [] expectedTailMessage = new byte [10 ];
220
229
221
- final S3MultiPartOutputStream
222
- out = new S3MultiPartOutputStream (BUCKET_NAME , FILE_KEY , messageSize + 10 , mockedS3 );
223
- random .nextBytes (message );
224
- out .write (message );
225
- System .arraycopy (message , 0 , expectedFullMessage , 0 , message .length );
226
- random .nextBytes (message );
227
- out .write (message );
228
- System .arraycopy (message , 0 , expectedFullMessage , 20 , 10 );
229
- System .arraycopy (message , 10 , expectedTailMessage , 0 , 10 );
230
+ final var out = new S3MultiPartOutputStream (BUCKET_NAME , FILE_KEY , messageSize + 10 , mockedS3 );
231
+ {
232
+ final byte [] message = new byte [messageSize ];
233
+ random .nextBytes (message );
234
+ out .write (message );
235
+ System .arraycopy (message , 0 , expectedFullMessage , 0 , message .length );
236
+ }
237
+ {
238
+ final byte [] message = new byte [messageSize ];
239
+ random .nextBytes (message );
240
+ out .write (message );
241
+ System .arraycopy (message , 0 , expectedFullMessage , 20 , 10 );
242
+ System .arraycopy (message , 10 , expectedTailMessage , 0 , 10 );
243
+ }
230
244
out .close ();
231
245
232
- assertUploadPartRequest (uploadPartRequests .get (0 ), 30 , 1 , expectedFullMessage );
233
- assertUploadPartRequest (uploadPartRequests .get (1 ), 10 , 2 , expectedTailMessage );
246
+ assertThat (uploadPartRequests ).hasSize (2 );
247
+ assertUploadPartRequest (uploadPartRequests .get (1 ), uploadPartContents .get (1 ), 30 , 1 , expectedFullMessage );
248
+ assertUploadPartRequest (uploadPartRequests .get (2 ), uploadPartContents .get (2 ), 10 , 2 , expectedTailMessage );
234
249
235
250
verify (mockedS3 ).initiateMultipartUpload (any (InitiateMultipartUploadRequest .class ));
236
251
verify (mockedS3 , times (2 )).uploadPart (any (UploadPartRequest .class ));
@@ -251,6 +266,7 @@ private static UploadPartResult newUploadPartResult(final int partNumber, final
251
266
}
252
267
253
268
private static void assertUploadPartRequest (final UploadPartRequest uploadPartRequest ,
269
+ final byte [] bytes ,
254
270
final int expectedPartSize ,
255
271
final int expectedPartNumber ,
256
272
final byte [] expectedBytes ) {
@@ -259,23 +275,17 @@ private static void assertUploadPartRequest(final UploadPartRequest uploadPartRe
259
275
assertThat (uploadPartRequest .getPartNumber ()).isEqualTo (expectedPartNumber );
260
276
assertThat (uploadPartRequest .getBucketName ()).isEqualTo (BUCKET_NAME );
261
277
assertThat (uploadPartRequest .getKey ()).isEqualTo (FILE_KEY );
262
- assertThat (uploadPartRequest . getInputStream ()). hasBinaryContent (expectedBytes );
278
+ assertThat (bytes ). isEqualTo (expectedBytes );
263
279
}
264
280
265
281
private static void assertCompleteMultipartUploadRequest (final CompleteMultipartUploadRequest request ,
266
- final List < PartETag > expectedETags ) {
282
+ final Map < Integer , String > expectedETags ) {
267
283
assertThat (request .getBucketName ()).isEqualTo (BUCKET_NAME );
268
284
assertThat (request .getKey ()).isEqualTo (FILE_KEY );
269
285
assertThat (request .getUploadId ()).isEqualTo (UPLOAD_ID );
270
- assertThat (request .getPartETags ()).hasSameSizeAs (expectedETags );
271
-
272
- for (int i = 0 ; i < expectedETags .size (); i ++) {
273
- final PartETag expectedETag = expectedETags .get (i );
274
- final PartETag etag = request .getPartETags ().get (i );
275
-
276
- assertThat (etag .getPartNumber ()).isEqualTo (expectedETag .getPartNumber ());
277
- assertThat (etag .getETag ()).isEqualTo (expectedETag .getETag ());
278
- }
286
+ final Map <Integer , String > tags = request .getPartETags ().stream ()
287
+ .collect (Collectors .toMap (PartETag ::getPartNumber , PartETag ::getETag ));
288
+ assertThat (tags ).containsExactlyInAnyOrderEntriesOf (expectedETags );
279
289
}
280
290
281
291
private static void assertAbortMultipartUploadRequest (final AbortMultipartUploadRequest request ) {
0 commit comments