@@ -11,6 +11,7 @@ import (
11
11
"encoding/hex"
12
12
"flag"
13
13
"fmt"
14
+ "io"
14
15
"io/ioutil"
15
16
"log"
16
17
"net/http/httptest"
@@ -26,11 +27,18 @@ import (
26
27
"github.com/aws/aws-sdk-go/aws/credentials"
27
28
"github.com/aws/aws-sdk-go/aws/session"
28
29
"github.com/aws/aws-sdk-go/service/s3"
30
+ "github.com/aws/aws-sdk-go/service/s3/s3manager"
29
31
"github.com/johannesboyne/gofakes3"
30
32
"github.com/johannesboyne/gofakes3/backend/s3mem"
31
33
)
32
34
33
- const defaultBucket = "mybucket"
35
+ const (
36
+ defaultBucket = "mybucket"
37
+
38
+ // docs say MB, client SDK uses MiB, gofakes3 assumes MB as the lowest common denominator
39
+ // to accept, but we need to satisfy the client SDK in the test suite so this is MiB:
40
+ defaultUploadPartSize = 5 * 1024 * 1024
41
+ )
34
42
35
43
var (
36
44
logFile string
@@ -196,7 +204,7 @@ func (ts *testServer) objectExists(bucket, key string) bool {
196
204
197
205
func (ts * testServer ) putString (bucket , key string , meta map [string ]string , in string ) {
198
206
ts .Helper ()
199
- ts .OK (ts .backend .PutObject (bucket , key , meta , strings .NewReader (in )))
207
+ ts .OK (ts .backend .PutObject (bucket , key , meta , strings .NewReader (in ), int64 ( len ( in )) ))
200
208
}
201
209
202
210
func (ts * testServer ) objectAsString (bucket , key string ) string {
@@ -243,19 +251,146 @@ func (ts *testServer) assertLs(bucket string, prefix string, expectedPrefixes []
243
251
ls .assertContents (ts .TT , expectedPrefixes , expectedObjects )
244
252
}
245
253
254
+ type multipartUploadOptions struct {
255
+ partSize int64
256
+ }
257
+
258
+ func (ts * testServer ) assertMultipartUpload (bucket , object string , body interface {}, options * multipartUploadOptions ) {
259
+ if options == nil {
260
+ options = & multipartUploadOptions {}
261
+ }
262
+ if options .partSize <= 0 {
263
+ options .partSize = defaultUploadPartSize
264
+ }
265
+
266
+ s3 := ts .s3Client ()
267
+ uploader := s3manager .NewUploaderWithClient (s3 )
268
+
269
+ contents := readBody (ts .TT , body )
270
+ upParams := & s3manager.UploadInput {
271
+ Bucket : aws .String (defaultBucket ),
272
+ Key : aws .String ("uploadtest" ),
273
+ Body : bytes .NewReader (contents ),
274
+ ContentMD5 : aws .String (hashMD5Bytes (contents ).Base64 ()),
275
+ }
276
+
277
+ out , err := uploader .Upload (upParams , func (u * s3manager.Uploader ) {
278
+ u .LeavePartsOnError = true
279
+ u .PartSize = options .partSize
280
+ })
281
+ ts .OK (err )
282
+ _ = out
283
+
284
+ ts .assertObject (defaultBucket , "uploadtest" , nil , body )
285
+ }
286
+
287
+ func (ts * testServer ) createMultipartUpload (bucket , object string , meta map [string ]string ) (uploadID string ) {
288
+ svc := ts .s3Client ()
289
+ mpu , err := svc .CreateMultipartUpload (& s3.CreateMultipartUploadInput {
290
+ Bucket : aws .String (bucket ),
291
+ Key : aws .String (object ),
292
+ })
293
+ ts .OK (err )
294
+ return * mpu .UploadId
295
+ }
296
+
297
+ // assertListMultipartUploads
298
+ //
299
+ // If marker is not an empty string, it should be in the format "[<object>][/<uploadID>]".
300
+ // Each item in expectedUploads must be in the format "<object>/<uploadID>".
301
+ func (ts * testServer ) assertListMultipartUploads (
302
+ bucket string ,
303
+ marker string ,
304
+ prefix * gofakes3.Prefix ,
305
+ limit int64 ,
306
+ expectedUploads ... string ,
307
+ ) {
308
+ ts .Helper ()
309
+ svc := ts .s3Client ()
310
+ rq := & s3.ListMultipartUploadsInput {
311
+ Bucket : aws .String (bucket ),
312
+ MaxUploads : aws .Int64 (limit ),
313
+ }
314
+
315
+ var expectedKeyMarker , expectedUploadIDMarker string
316
+ if marker != "" {
317
+ parts := strings .SplitN (marker , "/" , 2 )
318
+ expectedKeyMarker = parts [0 ]
319
+ if len (parts ) == 2 {
320
+ expectedUploadIDMarker = parts [1 ]
321
+ }
322
+ rq .KeyMarker = aws .String (expectedKeyMarker )
323
+ rq .UploadIdMarker = aws .String (expectedUploadIDMarker )
324
+ }
325
+
326
+ var expectedPrefix , expectedDelimiter string
327
+ if prefix != nil {
328
+ rq .Prefix = aws .String (prefix .Prefix )
329
+ rq .Delimiter = aws .String (prefix .Delimiter )
330
+ expectedPrefix , expectedDelimiter = prefix .Prefix , prefix .Delimiter
331
+ }
332
+
333
+ rs , err := svc .ListMultipartUploads (rq )
334
+ ts .OK (err )
335
+
336
+ { // assert response fields match input
337
+ var foundPrefix , foundDelimiter , foundKeyMarker , foundUploadIDMarker string
338
+ if rs .Delimiter != nil {
339
+ foundDelimiter = * rs .Delimiter
340
+ }
341
+ if rs .Prefix != nil {
342
+ foundPrefix = * rs .Prefix
343
+ }
344
+ if rs .KeyMarker != nil {
345
+ foundKeyMarker = * rs .KeyMarker
346
+ }
347
+ if rs .UploadIdMarker != nil {
348
+ foundUploadIDMarker = * rs .UploadIdMarker
349
+ }
350
+ if foundPrefix != expectedPrefix {
351
+ ts .Fatal ("unexpected prefix" , foundPrefix , "!=" , expectedPrefix )
352
+ }
353
+ if foundDelimiter != expectedDelimiter {
354
+ ts .Fatal ("unexpected delimiter" , foundDelimiter , "!=" , expectedDelimiter )
355
+ }
356
+ if foundKeyMarker != expectedKeyMarker {
357
+ ts .Fatal ("unexpected key marker" , foundKeyMarker , "!=" , expectedKeyMarker )
358
+ }
359
+ if foundUploadIDMarker != expectedUploadIDMarker {
360
+ ts .Fatal ("unexpected upload ID marker" , foundUploadIDMarker , "!=" , expectedUploadIDMarker )
361
+ }
362
+ var foundUploads int64
363
+ if rs .MaxUploads != nil {
364
+ foundUploads = * rs .MaxUploads
365
+ }
366
+ if limit > 0 && foundUploads != limit {
367
+ ts .Fatal ("unexpected max uploads" , foundUploads , "!=" , limit )
368
+ }
369
+ }
370
+
371
+ var foundUploads []string
372
+ for _ , up := range rs .Uploads {
373
+ foundUploads = append (foundUploads , fmt .Sprintf ("%s/%s" , * up .Key , * up .UploadId ))
374
+ }
375
+
376
+ if ! reflect .DeepEqual (foundUploads , expectedUploads ) {
377
+ ts .Fatal ("upload list mismatch:" , foundUploads , "!=" , expectedUploads )
378
+ }
379
+ }
380
+
246
381
// If meta is nil, the metadata is not checked.
247
382
// If meta is map[string]string{}, it is checked against the empty map.
248
383
//
249
- // If contents is a string or a []byte, it is compared against the object's contents,
250
- // otherwise a panic occurs.
384
+ // If contents is a string, a []byte or an io.Reader , it is compared against
385
+ // the object's contents, otherwise a panic occurs.
251
386
func (ts * testServer ) assertObject (bucket string , object string , meta map [string ]string , contents interface {}) {
252
387
ts .Helper ()
253
388
254
389
obj , err := ts .backend .GetObject (bucket , object )
255
390
ts .OK (err )
256
391
defer obj .Contents .Close ()
257
392
258
- data , err := ioutil .ReadAll (obj .Contents )
393
+ data , err := gofakes3 .ReadAll (obj .Contents , obj . Size )
259
394
ts .OK (err )
260
395
261
396
if meta != nil {
@@ -264,17 +399,7 @@ func (ts *testServer) assertObject(bucket string, object string, meta map[string
264
399
}
265
400
}
266
401
267
- var checkContents []byte
268
- switch contents := contents .(type ) {
269
- case nil :
270
- case string :
271
- checkContents = []byte (contents )
272
- case []byte :
273
- checkContents = contents
274
- default :
275
- panic ("unexpected contents" )
276
- }
277
-
402
+ checkContents := readBody (ts .TT , contents )
278
403
if ! bytes .Equal (checkContents , data ) {
279
404
ts .Fatal ("data mismatch" ) // FIXME: more detail
280
405
}
@@ -288,6 +413,12 @@ func (ts *testServer) Close() {
288
413
ts .server .Close ()
289
414
}
290
415
416
+ func hashMD5Bytes (body []byte ) hashValue {
417
+ h := md5 .New ()
418
+ h .Write (body )
419
+ return hashValue (h .Sum (nil ))
420
+ }
421
+
291
422
type hashValue []byte
292
423
293
424
func (h hashValue ) Base64 () string { return base64 .StdEncoding .EncodeToString (h ) }
@@ -298,7 +429,7 @@ var (
298
429
randMu sync.Mutex
299
430
)
300
431
301
- func randomFileBody (size int64 ) ( []byte , hashValue ) {
432
+ func randomFileBody (size int64 ) []byte {
302
433
randMu .Lock ()
303
434
defer randMu .Unlock ()
304
435
@@ -320,7 +451,22 @@ func randomFileBody(size int64) ([]byte, hashValue) {
320
451
}
321
452
322
453
b = b [:size ]
323
- h := md5 .New ()
324
- h .Write (b )
325
- return b , hashValue (h .Sum (nil ))
454
+ return b
455
+ }
456
+
457
+ func readBody (tt gofakes3.TT , body interface {}) []byte {
458
+ switch body := body .(type ) {
459
+ case nil :
460
+ return []byte {}
461
+ case string :
462
+ return []byte (body )
463
+ case []byte :
464
+ return body
465
+ case io.Reader :
466
+ out , err := ioutil .ReadAll (body )
467
+ tt .OK (err )
468
+ return out
469
+ default :
470
+ panic ("unexpected contents" )
471
+ }
326
472
}
0 commit comments