github.com/minio/console@v1.3.0/api/user_objects_test.go (about) 1 // This file is part of MinIO Console Server 2 // Copyright (c) 2021 MinIO, Inc. 3 // 4 // This program is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU Affero General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // This program is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU Affero General Public License for more details. 13 // 14 // You should have received a copy of the GNU Affero General Public License 15 // along with this program. If not, see <http://www.gnu.org/licenses/>. 16 17 package api 18 19 import ( 20 "context" 21 "crypto/tls" 22 "encoding/json" 23 "errors" 24 "fmt" 25 "io" 26 "net/http" 27 "path/filepath" 28 "reflect" 29 "testing" 30 "time" 31 32 "github.com/go-openapi/runtime/middleware" 33 "github.com/minio/console/api/operations/object" 34 35 "github.com/go-openapi/swag" 36 "github.com/minio/console/models" 37 mc "github.com/minio/mc/cmd" 38 "github.com/minio/mc/pkg/probe" 39 "github.com/minio/minio-go/v7" 40 "github.com/minio/minio-go/v7/pkg/tags" 41 "github.com/stretchr/testify/assert" 42 ) 43 44 var ( 45 minioListObjectsMock func(ctx context.Context, bucket string, opts minio.ListObjectsOptions) <-chan minio.ObjectInfo 46 minioGetObjectLegalHoldMock func(ctx context.Context, bucketName, objectName string, opts minio.GetObjectLegalHoldOptions) (status *minio.LegalHoldStatus, err error) 47 minioGetObjectRetentionMock func(ctx context.Context, bucketName, objectName, versionID string) (mode *minio.RetentionMode, retainUntilDate *time.Time, err error) 48 minioPutObjectMock func(ctx context.Context, bucketName, objectName string, reader io.Reader, objectSize int64, opts minio.PutObjectOptions) (info minio.UploadInfo, err error) 49 minioPutObjectLegalHoldMock func(ctx context.Context, bucketName, objectName string, opts minio.PutObjectLegalHoldOptions) error 50 minioPutObjectRetentionMock func(ctx context.Context, bucketName, objectName string, opts minio.PutObjectRetentionOptions) error 51 minioGetObjectTaggingMock func(ctx context.Context, bucketName, objectName string, opts minio.GetObjectTaggingOptions) (*tags.Tags, error) 52 minioPutObjectTaggingMock func(ctx context.Context, bucketName, objectName string, otags *tags.Tags, opts minio.PutObjectTaggingOptions) error 53 minioStatObjectMock func(ctx context.Context, bucketName, prefix string, opts minio.GetObjectOptions) (objectInfo minio.ObjectInfo, err error) 54 ) 55 56 var ( 57 mcListMock func(ctx context.Context, opts mc.ListOptions) <-chan *mc.ClientContent 58 mcRemoveMock func(ctx context.Context, isIncomplete, isRemoveBucket, isBypass, forceDelete bool, contentCh <-chan *mc.ClientContent) <-chan mc.RemoveResult 59 mcGetMock func(ctx context.Context, opts mc.GetOptions) (io.ReadCloser, *probe.Error) 60 mcShareDownloadMock func(ctx context.Context, versionID string, expires time.Duration) (string, *probe.Error) 61 ) 62 63 // mock functions for minioClientMock 64 func (ac minioClientMock) listObjects(ctx context.Context, bucket string, opts minio.ListObjectsOptions) <-chan minio.ObjectInfo { 65 return minioListObjectsMock(ctx, bucket, opts) 66 } 67 68 func (ac minioClientMock) getObjectLegalHold(ctx context.Context, bucketName, objectName string, opts minio.GetObjectLegalHoldOptions) (status *minio.LegalHoldStatus, err error) { 69 return minioGetObjectLegalHoldMock(ctx, bucketName, objectName, opts) 70 } 71 72 func (ac minioClientMock) getObjectRetention(ctx context.Context, bucketName, objectName, versionID string) (mode *minio.RetentionMode, retainUntilDate *time.Time, err error) { 73 return minioGetObjectRetentionMock(ctx, bucketName, objectName, versionID) 74 } 75 76 func (ac minioClientMock) putObject(ctx context.Context, bucketName, objectName string, reader io.Reader, objectSize int64, opts minio.PutObjectOptions) (info minio.UploadInfo, err error) { 77 return minioPutObjectMock(ctx, bucketName, objectName, reader, objectSize, opts) 78 } 79 80 func (ac minioClientMock) putObjectLegalHold(ctx context.Context, bucketName, objectName string, opts minio.PutObjectLegalHoldOptions) error { 81 return minioPutObjectLegalHoldMock(ctx, bucketName, objectName, opts) 82 } 83 84 func (ac minioClientMock) putObjectRetention(ctx context.Context, bucketName, objectName string, opts minio.PutObjectRetentionOptions) error { 85 return minioPutObjectRetentionMock(ctx, bucketName, objectName, opts) 86 } 87 88 func (ac minioClientMock) getObjectTagging(ctx context.Context, bucketName, objectName string, opts minio.GetObjectTaggingOptions) (*tags.Tags, error) { 89 return minioGetObjectTaggingMock(ctx, bucketName, objectName, opts) 90 } 91 92 func (ac minioClientMock) putObjectTagging(ctx context.Context, bucketName, objectName string, otags *tags.Tags, opts minio.PutObjectTaggingOptions) error { 93 return minioPutObjectTaggingMock(ctx, bucketName, objectName, otags, opts) 94 } 95 96 func (ac minioClientMock) statObject(ctx context.Context, bucketName, prefix string, opts minio.GetObjectOptions) (objectInfo minio.ObjectInfo, err error) { 97 return minioStatObjectMock(ctx, bucketName, prefix, opts) 98 } 99 100 // mock functions for s3ClientMock 101 func (c s3ClientMock) list(ctx context.Context, opts mc.ListOptions) <-chan *mc.ClientContent { 102 return mcListMock(ctx, opts) 103 } 104 105 func (c s3ClientMock) remove(ctx context.Context, isIncomplete, isRemoveBucket, isBypass, forceDelete bool, contentCh <-chan *mc.ClientContent) <-chan mc.RemoveResult { 106 return mcRemoveMock(ctx, isIncomplete, isRemoveBucket, isBypass, forceDelete, contentCh) 107 } 108 109 func (c s3ClientMock) get(ctx context.Context, opts mc.GetOptions) (io.ReadCloser, *probe.Error) { 110 return mcGetMock(ctx, opts) 111 } 112 113 func (c s3ClientMock) shareDownload(ctx context.Context, versionID string, expires time.Duration) (string, *probe.Error) { 114 return mcShareDownloadMock(ctx, versionID, expires) 115 } 116 117 func Test_listObjects(t *testing.T) { 118 ctx, cancel := context.WithCancel(context.Background()) 119 defer cancel() 120 t1 := time.Now() 121 tretention := time.Now() 122 minClient := minioClientMock{} 123 type args struct { 124 bucketName string 125 prefix string 126 recursive bool 127 withVersions bool 128 withMetadata bool 129 limit *int32 130 listFunc func(ctx context.Context, bucket string, opts minio.ListObjectsOptions) <-chan minio.ObjectInfo 131 objectLegalHoldFunc func(ctx context.Context, bucketName, objectName string, opts minio.GetObjectLegalHoldOptions) (status *minio.LegalHoldStatus, err error) 132 objectRetentionFunc func(ctx context.Context, bucketName, objectName, versionID string) (mode *minio.RetentionMode, retainUntilDate *time.Time, err error) 133 objectGetTaggingFunc func(ctx context.Context, bucketName, objectName string, opts minio.GetObjectTaggingOptions) (*tags.Tags, error) 134 } 135 tests := []struct { 136 test string 137 args args 138 expectedResp []*models.BucketObject 139 wantError error 140 }{ 141 { 142 test: "Return objects", 143 args: args{ 144 bucketName: "bucket1", 145 prefix: "prefix", 146 recursive: true, 147 withVersions: false, 148 withMetadata: false, 149 listFunc: func(_ context.Context, _ string, _ minio.ListObjectsOptions) <-chan minio.ObjectInfo { 150 objectStatCh := make(chan minio.ObjectInfo, 1) 151 go func(objectStatCh chan<- minio.ObjectInfo) { 152 defer close(objectStatCh) 153 for _, bucket := range []minio.ObjectInfo{ 154 { 155 Key: "obj1", 156 LastModified: t1, 157 Size: int64(1024), 158 ContentType: "content", 159 }, 160 { 161 Key: "obj2", 162 LastModified: t1, 163 Size: int64(512), 164 ContentType: "content", 165 }, 166 } { 167 objectStatCh <- bucket 168 } 169 }(objectStatCh) 170 return objectStatCh 171 }, 172 objectLegalHoldFunc: func(_ context.Context, _, _ string, _ minio.GetObjectLegalHoldOptions) (status *minio.LegalHoldStatus, err error) { 173 s := minio.LegalHoldEnabled 174 return &s, nil 175 }, 176 objectRetentionFunc: func(_ context.Context, _, _, _ string) (mode *minio.RetentionMode, retainUntilDate *time.Time, err error) { 177 m := minio.Governance 178 return &m, &tretention, nil 179 }, 180 objectGetTaggingFunc: func(_ context.Context, _, _ string, _ minio.GetObjectTaggingOptions) (*tags.Tags, error) { 181 tagMap := map[string]string{ 182 "tag1": "value1", 183 } 184 otags, err := tags.MapToObjectTags(tagMap) 185 if err != nil { 186 return nil, err 187 } 188 return otags, nil 189 }, 190 }, 191 expectedResp: []*models.BucketObject{ 192 { 193 Name: "obj1", 194 LastModified: t1.Format(time.RFC3339), 195 Size: int64(1024), 196 ContentType: "content", 197 LegalHoldStatus: string(minio.LegalHoldEnabled), 198 RetentionMode: string(minio.Governance), 199 RetentionUntilDate: tretention.Format(time.RFC3339), 200 Tags: map[string]string{ 201 "tag1": "value1", 202 }, 203 }, { 204 Name: "obj2", 205 LastModified: t1.Format(time.RFC3339), 206 Size: int64(512), 207 ContentType: "content", 208 LegalHoldStatus: string(minio.LegalHoldEnabled), 209 RetentionMode: string(minio.Governance), 210 RetentionUntilDate: tretention.Format(time.RFC3339), 211 Tags: map[string]string{ 212 "tag1": "value1", 213 }, 214 }, 215 }, 216 wantError: nil, 217 }, 218 { 219 test: "Return zero objects", 220 args: args{ 221 bucketName: "bucket1", 222 prefix: "prefix", 223 recursive: true, 224 withVersions: false, 225 withMetadata: false, 226 listFunc: func(_ context.Context, _ string, _ minio.ListObjectsOptions) <-chan minio.ObjectInfo { 227 objectStatCh := make(chan minio.ObjectInfo, 1) 228 defer close(objectStatCh) 229 return objectStatCh 230 }, 231 objectLegalHoldFunc: func(_ context.Context, _, _ string, _ minio.GetObjectLegalHoldOptions) (status *minio.LegalHoldStatus, err error) { 232 s := minio.LegalHoldEnabled 233 return &s, nil 234 }, 235 objectRetentionFunc: func(_ context.Context, _, _, _ string) (mode *minio.RetentionMode, retainUntilDate *time.Time, err error) { 236 m := minio.Governance 237 return &m, &tretention, nil 238 }, 239 objectGetTaggingFunc: func(_ context.Context, _, _ string, _ minio.GetObjectTaggingOptions) (*tags.Tags, error) { 240 tagMap := map[string]string{ 241 "tag1": "value1", 242 } 243 otags, err := tags.MapToObjectTags(tagMap) 244 if err != nil { 245 return nil, err 246 } 247 return otags, nil 248 }, 249 }, 250 expectedResp: nil, 251 wantError: nil, 252 }, 253 { 254 test: "Handle error if present on object", 255 args: args{ 256 bucketName: "bucket1", 257 prefix: "prefix", 258 recursive: true, 259 withVersions: false, 260 withMetadata: false, 261 listFunc: func(_ context.Context, _ string, _ minio.ListObjectsOptions) <-chan minio.ObjectInfo { 262 objectStatCh := make(chan minio.ObjectInfo, 1) 263 go func(objectStatCh chan<- minio.ObjectInfo) { 264 defer close(objectStatCh) 265 for _, bucket := range []minio.ObjectInfo{ 266 { 267 Key: "obj2", 268 LastModified: t1, 269 Size: int64(512), 270 ContentType: "content", 271 }, 272 { 273 Err: errors.New("error here"), 274 }, 275 } { 276 objectStatCh <- bucket 277 } 278 }(objectStatCh) 279 return objectStatCh 280 }, 281 objectLegalHoldFunc: func(_ context.Context, _, _ string, _ minio.GetObjectLegalHoldOptions) (status *minio.LegalHoldStatus, err error) { 282 s := minio.LegalHoldEnabled 283 return &s, nil 284 }, 285 objectRetentionFunc: func(_ context.Context, _, _, _ string) (mode *minio.RetentionMode, retainUntilDate *time.Time, err error) { 286 m := minio.Governance 287 return &m, &tretention, nil 288 }, 289 objectGetTaggingFunc: func(_ context.Context, _, _ string, _ minio.GetObjectTaggingOptions) (*tags.Tags, error) { 290 tagMap := map[string]string{ 291 "tag1": "value1", 292 } 293 otags, err := tags.MapToObjectTags(tagMap) 294 if err != nil { 295 return nil, err 296 } 297 return otags, nil 298 }, 299 }, 300 expectedResp: nil, 301 wantError: errors.New("error here"), 302 }, 303 { 304 // Description: deleted objects with IsDeleteMarker 305 // should not call legsalhold, tag or retention funcs 306 test: "Return deleted objects", 307 args: args{ 308 bucketName: "bucket1", 309 prefix: "prefix", 310 recursive: true, 311 withVersions: false, 312 withMetadata: false, 313 listFunc: func(_ context.Context, _ string, _ minio.ListObjectsOptions) <-chan minio.ObjectInfo { 314 objectStatCh := make(chan minio.ObjectInfo, 1) 315 go func(objectStatCh chan<- minio.ObjectInfo) { 316 defer close(objectStatCh) 317 for _, bucket := range []minio.ObjectInfo{ 318 { 319 Key: "obj1", 320 LastModified: t1, 321 Size: int64(1024), 322 ContentType: "content", 323 IsDeleteMarker: true, 324 }, 325 { 326 Key: "obj2", 327 LastModified: t1, 328 Size: int64(512), 329 ContentType: "content", 330 }, 331 } { 332 objectStatCh <- bucket 333 } 334 }(objectStatCh) 335 return objectStatCh 336 }, 337 objectLegalHoldFunc: func(_ context.Context, _, _ string, _ minio.GetObjectLegalHoldOptions) (status *minio.LegalHoldStatus, err error) { 338 s := minio.LegalHoldEnabled 339 return &s, nil 340 }, 341 objectRetentionFunc: func(_ context.Context, _, _, _ string) (mode *minio.RetentionMode, retainUntilDate *time.Time, err error) { 342 m := minio.Governance 343 return &m, &tretention, nil 344 }, 345 objectGetTaggingFunc: func(_ context.Context, _, _ string, _ minio.GetObjectTaggingOptions) (*tags.Tags, error) { 346 tagMap := map[string]string{ 347 "tag1": "value1", 348 } 349 otags, err := tags.MapToObjectTags(tagMap) 350 if err != nil { 351 return nil, err 352 } 353 return otags, nil 354 }, 355 }, 356 expectedResp: []*models.BucketObject{ 357 { 358 Name: "obj1", 359 LastModified: t1.Format(time.RFC3339), 360 Size: int64(1024), 361 ContentType: "content", 362 IsDeleteMarker: true, 363 }, { 364 Name: "obj2", 365 LastModified: t1.Format(time.RFC3339), 366 Size: int64(512), 367 ContentType: "content", 368 LegalHoldStatus: string(minio.LegalHoldEnabled), 369 RetentionMode: string(minio.Governance), 370 RetentionUntilDate: tretention.Format(time.RFC3339), 371 Tags: map[string]string{ 372 "tag1": "value1", 373 }, 374 }, 375 }, 376 wantError: nil, 377 }, 378 { 379 // Description: deleted objects with 380 // error on legalhold, tags or retention funcs 381 // should only log errors 382 test: "Return deleted objects, error on legalhold and retention", 383 args: args{ 384 bucketName: "bucket1", 385 prefix: "prefix", 386 recursive: true, 387 withVersions: false, 388 withMetadata: false, 389 listFunc: func(_ context.Context, _ string, _ minio.ListObjectsOptions) <-chan minio.ObjectInfo { 390 objectStatCh := make(chan minio.ObjectInfo, 1) 391 go func(objectStatCh chan<- minio.ObjectInfo) { 392 defer close(objectStatCh) 393 for _, bucket := range []minio.ObjectInfo{ 394 { 395 Key: "obj1", 396 LastModified: t1, 397 Size: int64(1024), 398 ContentType: "content", 399 }, 400 } { 401 objectStatCh <- bucket 402 } 403 }(objectStatCh) 404 return objectStatCh 405 }, 406 objectLegalHoldFunc: func(_ context.Context, _, _ string, _ minio.GetObjectLegalHoldOptions) (status *minio.LegalHoldStatus, err error) { 407 return nil, errors.New("error legal") 408 }, 409 objectRetentionFunc: func(_ context.Context, _, _, _ string) (mode *minio.RetentionMode, retainUntilDate *time.Time, err error) { 410 return nil, nil, errors.New("error retention") 411 }, 412 objectGetTaggingFunc: func(_ context.Context, _, _ string, _ minio.GetObjectTaggingOptions) (*tags.Tags, error) { 413 return nil, errors.New("error get tags") 414 }, 415 }, 416 expectedResp: []*models.BucketObject{ 417 { 418 Name: "obj1", 419 LastModified: t1.Format(time.RFC3339), 420 Size: int64(1024), 421 ContentType: "content", 422 }, 423 }, 424 wantError: nil, 425 }, 426 { 427 // Description: if the prefix end with a `/` meaning it is a folder, 428 // it should not fetch retention, legalhold nor tags for each object 429 // it should only fetch it for single objects with or without versionID 430 test: "Don't get object retention/legalhold/tags for folders", 431 args: args{ 432 bucketName: "bucket1", 433 prefix: "prefix/folder/", 434 recursive: true, 435 withVersions: false, 436 withMetadata: false, 437 listFunc: func(_ context.Context, _ string, _ minio.ListObjectsOptions) <-chan minio.ObjectInfo { 438 objectStatCh := make(chan minio.ObjectInfo, 1) 439 go func(objectStatCh chan<- minio.ObjectInfo) { 440 defer close(objectStatCh) 441 for _, bucket := range []minio.ObjectInfo{ 442 { 443 Key: "obj1", 444 LastModified: t1, 445 Size: int64(1024), 446 ContentType: "content", 447 }, 448 { 449 Key: "obj2", 450 LastModified: t1, 451 Size: int64(512), 452 ContentType: "content", 453 }, 454 } { 455 objectStatCh <- bucket 456 } 457 }(objectStatCh) 458 return objectStatCh 459 }, 460 objectLegalHoldFunc: func(_ context.Context, _, _ string, _ minio.GetObjectLegalHoldOptions) (status *minio.LegalHoldStatus, err error) { 461 s := minio.LegalHoldEnabled 462 return &s, nil 463 }, 464 objectRetentionFunc: func(_ context.Context, _, _, _ string) (mode *minio.RetentionMode, retainUntilDate *time.Time, err error) { 465 m := minio.Governance 466 return &m, &tretention, nil 467 }, 468 objectGetTaggingFunc: func(_ context.Context, _, _ string, _ minio.GetObjectTaggingOptions) (*tags.Tags, error) { 469 tagMap := map[string]string{ 470 "tag1": "value1", 471 } 472 otags, err := tags.MapToObjectTags(tagMap) 473 if err != nil { 474 return nil, err 475 } 476 return otags, nil 477 }, 478 }, 479 expectedResp: []*models.BucketObject{ 480 { 481 Name: "obj1", 482 LastModified: t1.Format(time.RFC3339), 483 Size: int64(1024), 484 ContentType: "content", 485 }, { 486 Name: "obj2", 487 LastModified: t1.Format(time.RFC3339), 488 Size: int64(512), 489 ContentType: "content", 490 }, 491 }, 492 wantError: nil, 493 }, 494 { 495 // Description: if the prefix is "" meaning it is all contents within a bucket, 496 // it should not fetch retention, legalhold nor tags for each object 497 // it should only fetch it for single objects with or without versionID 498 test: "Don't get object retention/legalhold/tags for empty prefix", 499 args: args{ 500 bucketName: "bucket1", 501 prefix: "", 502 recursive: true, 503 withVersions: false, 504 withMetadata: false, 505 listFunc: func(_ context.Context, _ string, _ minio.ListObjectsOptions) <-chan minio.ObjectInfo { 506 objectStatCh := make(chan minio.ObjectInfo, 1) 507 go func(objectStatCh chan<- minio.ObjectInfo) { 508 defer close(objectStatCh) 509 for _, bucket := range []minio.ObjectInfo{ 510 { 511 Key: "obj1", 512 LastModified: t1, 513 Size: int64(1024), 514 ContentType: "content", 515 }, 516 { 517 Key: "obj2", 518 LastModified: t1, 519 Size: int64(512), 520 ContentType: "content", 521 }, 522 } { 523 objectStatCh <- bucket 524 } 525 }(objectStatCh) 526 return objectStatCh 527 }, 528 objectLegalHoldFunc: func(_ context.Context, _, _ string, _ minio.GetObjectLegalHoldOptions) (status *minio.LegalHoldStatus, err error) { 529 s := minio.LegalHoldEnabled 530 return &s, nil 531 }, 532 objectRetentionFunc: func(_ context.Context, _, _, _ string) (mode *minio.RetentionMode, retainUntilDate *time.Time, err error) { 533 m := minio.Governance 534 return &m, &tretention, nil 535 }, 536 objectGetTaggingFunc: func(_ context.Context, _, _ string, _ minio.GetObjectTaggingOptions) (*tags.Tags, error) { 537 tagMap := map[string]string{ 538 "tag1": "value1", 539 } 540 otags, err := tags.MapToObjectTags(tagMap) 541 if err != nil { 542 return nil, err 543 } 544 return otags, nil 545 }, 546 }, 547 expectedResp: []*models.BucketObject{ 548 { 549 Name: "obj1", 550 LastModified: t1.Format(time.RFC3339), 551 Size: int64(1024), 552 ContentType: "content", 553 }, { 554 Name: "obj2", 555 LastModified: t1.Format(time.RFC3339), 556 Size: int64(512), 557 ContentType: "content", 558 }, 559 }, 560 wantError: nil, 561 }, 562 { 563 test: "Return objects", 564 args: args{ 565 bucketName: "bucket1", 566 prefix: "prefix", 567 recursive: true, 568 withVersions: false, 569 withMetadata: false, 570 listFunc: func(_ context.Context, _ string, _ minio.ListObjectsOptions) <-chan minio.ObjectInfo { 571 objectStatCh := make(chan minio.ObjectInfo, 1) 572 go func(objectStatCh chan<- minio.ObjectInfo) { 573 defer close(objectStatCh) 574 for _, bucket := range []minio.ObjectInfo{ 575 { 576 Key: "obj1", 577 LastModified: t1, 578 Size: int64(1024), 579 ContentType: "content", 580 }, 581 { 582 Key: "obj2", 583 LastModified: t1, 584 Size: int64(512), 585 ContentType: "content", 586 }, 587 } { 588 objectStatCh <- bucket 589 } 590 }(objectStatCh) 591 return objectStatCh 592 }, 593 objectLegalHoldFunc: func(_ context.Context, _, _ string, _ minio.GetObjectLegalHoldOptions) (status *minio.LegalHoldStatus, err error) { 594 s := minio.LegalHoldEnabled 595 return &s, nil 596 }, 597 objectRetentionFunc: func(_ context.Context, _, _, _ string) (mode *minio.RetentionMode, retainUntilDate *time.Time, err error) { 598 m := minio.Governance 599 return &m, &tretention, nil 600 }, 601 objectGetTaggingFunc: func(_ context.Context, _, _ string, _ minio.GetObjectTaggingOptions) (*tags.Tags, error) { 602 tagMap := map[string]string{ 603 "tag1": "value1", 604 } 605 otags, err := tags.MapToObjectTags(tagMap) 606 if err != nil { 607 return nil, err 608 } 609 return otags, nil 610 }, 611 }, 612 expectedResp: []*models.BucketObject{ 613 { 614 Name: "obj1", 615 LastModified: t1.Format(time.RFC3339), 616 Size: int64(1024), 617 ContentType: "content", 618 LegalHoldStatus: string(minio.LegalHoldEnabled), 619 RetentionMode: string(minio.Governance), 620 RetentionUntilDate: tretention.Format(time.RFC3339), 621 Tags: map[string]string{ 622 "tag1": "value1", 623 }, 624 }, { 625 Name: "obj2", 626 LastModified: t1.Format(time.RFC3339), 627 Size: int64(512), 628 ContentType: "content", 629 LegalHoldStatus: string(minio.LegalHoldEnabled), 630 RetentionMode: string(minio.Governance), 631 RetentionUntilDate: tretention.Format(time.RFC3339), 632 Tags: map[string]string{ 633 "tag1": "value1", 634 }, 635 }, 636 }, 637 wantError: nil, 638 }, 639 { 640 test: "Limit 1", 641 args: args{ 642 bucketName: "bucket1", 643 prefix: "prefix", 644 recursive: true, 645 withVersions: false, 646 withMetadata: false, 647 limit: swag.Int32(1), 648 listFunc: func(_ context.Context, _ string, _ minio.ListObjectsOptions) <-chan minio.ObjectInfo { 649 objectStatCh := make(chan minio.ObjectInfo, 1) 650 go func(objectStatCh chan<- minio.ObjectInfo) { 651 defer close(objectStatCh) 652 for _, bucket := range []minio.ObjectInfo{ 653 { 654 Key: "obj1", 655 LastModified: t1, 656 Size: int64(1024), 657 ContentType: "content", 658 }, 659 { 660 Key: "obj2", 661 LastModified: t1, 662 Size: int64(512), 663 ContentType: "content", 664 }, 665 } { 666 objectStatCh <- bucket 667 } 668 }(objectStatCh) 669 return objectStatCh 670 }, 671 objectLegalHoldFunc: func(_ context.Context, _, _ string, _ minio.GetObjectLegalHoldOptions) (status *minio.LegalHoldStatus, err error) { 672 s := minio.LegalHoldEnabled 673 return &s, nil 674 }, 675 objectRetentionFunc: func(_ context.Context, _, _, _ string) (mode *minio.RetentionMode, retainUntilDate *time.Time, err error) { 676 m := minio.Governance 677 return &m, &tretention, nil 678 }, 679 objectGetTaggingFunc: func(_ context.Context, _, _ string, _ minio.GetObjectTaggingOptions) (*tags.Tags, error) { 680 tagMap := map[string]string{ 681 "tag1": "value1", 682 } 683 otags, err := tags.MapToObjectTags(tagMap) 684 if err != nil { 685 return nil, err 686 } 687 return otags, nil 688 }, 689 }, 690 expectedResp: []*models.BucketObject{ 691 { 692 Name: "obj1", 693 LastModified: t1.Format(time.RFC3339), 694 Size: int64(1024), 695 ContentType: "content", 696 LegalHoldStatus: string(minio.LegalHoldEnabled), 697 RetentionMode: string(minio.Governance), 698 RetentionUntilDate: tretention.Format(time.RFC3339), 699 Tags: map[string]string{ 700 "tag1": "value1", 701 }, 702 }, 703 }, 704 wantError: nil, 705 }, 706 } 707 708 t.Parallel() 709 for _, tt := range tests { 710 tt := tt 711 t.Run(tt.test, func(_ *testing.T) { 712 minioListObjectsMock = tt.args.listFunc 713 minioGetObjectLegalHoldMock = tt.args.objectLegalHoldFunc 714 minioGetObjectRetentionMock = tt.args.objectRetentionFunc 715 minioGetObjectTaggingMock = tt.args.objectGetTaggingFunc 716 resp, err := listBucketObjects(ListObjectsOpts{ 717 ctx: ctx, 718 client: minClient, 719 bucketName: tt.args.bucketName, 720 prefix: tt.args.prefix, 721 recursive: tt.args.recursive, 722 withVersions: tt.args.withVersions, 723 withMetadata: tt.args.withMetadata, 724 limit: tt.args.limit, 725 }) 726 switch { 727 case err == nil && tt.wantError != nil: 728 t.Errorf("listBucketObjects() error: %v, wantErr: %v", err, tt.wantError) 729 case err != nil && tt.wantError == nil: 730 t.Errorf("listBucketObjects() error: %v, wantErr: %v", err, tt.wantError) 731 case err != nil && tt.wantError != nil: 732 if err.Error() != tt.wantError.Error() { 733 t.Errorf("listBucketObjects() error: %v, wantErr: %v", err, tt.wantError) 734 } 735 } 736 if err == nil { 737 if !reflect.DeepEqual(resp, tt.expectedResp) { 738 ji, _ := json.Marshal(resp) 739 vi, _ := json.Marshal(tt.expectedResp) 740 t.Errorf("\ngot: %s \nwant: %s", ji, vi) 741 } 742 } 743 }) 744 } 745 } 746 747 func Test_deleteObjects(t *testing.T) { 748 ctx, cancel := context.WithCancel(context.Background()) 749 defer cancel() 750 s3Client1 := s3ClientMock{} 751 type args struct { 752 bucket string 753 path string 754 versionID string 755 recursive bool 756 nonCurrent bool 757 listFunc func(ctx context.Context, opts mc.ListOptions) <-chan *mc.ClientContent 758 removeFunc func(ctx context.Context, isIncomplete, isRemoveBucket, isBypass, forceDelete bool, contentCh <-chan *mc.ClientContent) <-chan mc.RemoveResult 759 } 760 tests := []struct { 761 test string 762 args args 763 wantError error 764 }{ 765 { 766 test: "Remove single object", 767 args: args{ 768 path: "obj.txt", 769 versionID: "", 770 recursive: false, 771 nonCurrent: false, 772 removeFunc: func(_ context.Context, _, _, _, _ bool, _ <-chan *mc.ClientContent) <-chan mc.RemoveResult { 773 resultCh := make(chan mc.RemoveResult, 1) 774 resultCh <- mc.RemoveResult{Err: nil} 775 close(resultCh) 776 return resultCh 777 }, 778 }, 779 wantError: nil, 780 }, 781 { 782 test: "Error on Remove single object", 783 args: args{ 784 path: "obj.txt", 785 versionID: "", 786 recursive: false, 787 nonCurrent: false, 788 removeFunc: func(_ context.Context, _, _, _, _ bool, _ <-chan *mc.ClientContent) <-chan mc.RemoveResult { 789 resultCh := make(chan mc.RemoveResult, 1) 790 resultCh <- mc.RemoveResult{Err: probe.NewError(errors.New("probe error"))} 791 close(resultCh) 792 return resultCh 793 }, 794 }, 795 wantError: errors.New("probe error"), 796 }, 797 { 798 test: "Remove multiple objects", 799 args: args{ 800 path: "path/", 801 versionID: "", 802 recursive: true, 803 nonCurrent: false, 804 removeFunc: func(_ context.Context, _, _, _, _ bool, _ <-chan *mc.ClientContent) <-chan mc.RemoveResult { 805 resultCh := make(chan mc.RemoveResult, 1) 806 resultCh <- mc.RemoveResult{Err: nil} 807 close(resultCh) 808 return resultCh 809 }, 810 listFunc: func(_ context.Context, _ mc.ListOptions) <-chan *mc.ClientContent { 811 ch := make(chan *mc.ClientContent, 1) 812 ch <- &mc.ClientContent{} 813 close(ch) 814 return ch 815 }, 816 }, 817 wantError: nil, 818 }, 819 { 820 // Description handle error when error happens on remove function 821 // while deleting multiple objects 822 test: "Error on Remove multiple objects", 823 args: args{ 824 path: "path/", 825 versionID: "", 826 recursive: true, 827 nonCurrent: false, 828 removeFunc: func(_ context.Context, _, _, _, _ bool, _ <-chan *mc.ClientContent) <-chan mc.RemoveResult { 829 resultCh := make(chan mc.RemoveResult, 1) 830 resultCh <- mc.RemoveResult{Err: probe.NewError(errors.New("probe error"))} 831 close(resultCh) 832 return resultCh 833 }, 834 listFunc: func(_ context.Context, _ mc.ListOptions) <-chan *mc.ClientContent { 835 ch := make(chan *mc.ClientContent, 1) 836 ch <- &mc.ClientContent{} 837 close(ch) 838 return ch 839 }, 840 }, 841 wantError: errors.New("probe error"), 842 }, 843 { 844 test: "Remove non current objects - no error", 845 args: args{ 846 path: "path/", 847 versionID: "", 848 recursive: true, 849 nonCurrent: true, 850 removeFunc: func(_ context.Context, _, _, _, _ bool, _ <-chan *mc.ClientContent) <-chan mc.RemoveResult { 851 resultCh := make(chan mc.RemoveResult, 1) 852 resultCh <- mc.RemoveResult{Err: nil} 853 close(resultCh) 854 return resultCh 855 }, 856 listFunc: func(_ context.Context, _ mc.ListOptions) <-chan *mc.ClientContent { 857 ch := make(chan *mc.ClientContent, 1) 858 ch <- &mc.ClientContent{} 859 close(ch) 860 return ch 861 }, 862 }, 863 wantError: nil, 864 }, 865 { 866 // Description handle error when error happens on remove function 867 // while deleting multiple objects 868 test: "Error deleting non current objects", 869 args: args{ 870 path: "path/", 871 versionID: "", 872 recursive: true, 873 nonCurrent: true, 874 removeFunc: func(_ context.Context, _, _, _, _ bool, _ <-chan *mc.ClientContent) <-chan mc.RemoveResult { 875 resultCh := make(chan mc.RemoveResult, 1) 876 resultCh <- mc.RemoveResult{Err: probe.NewError(errors.New("probe error"))} 877 close(resultCh) 878 return resultCh 879 }, 880 listFunc: func(_ context.Context, _ mc.ListOptions) <-chan *mc.ClientContent { 881 ch := make(chan *mc.ClientContent, 1) 882 ch <- &mc.ClientContent{} 883 close(ch) 884 return ch 885 }, 886 }, 887 wantError: errors.New("probe error"), 888 }, 889 } 890 891 t.Parallel() 892 for _, tt := range tests { 893 tt := tt 894 t.Run(tt.test, func(_ *testing.T) { 895 mcListMock = tt.args.listFunc 896 mcRemoveMock = tt.args.removeFunc 897 err := deleteObjects(ctx, s3Client1, tt.args.bucket, tt.args.path, tt.args.versionID, tt.args.recursive, false, tt.args.nonCurrent, false) 898 switch { 899 case err == nil && tt.wantError != nil: 900 t.Errorf("deleteObjects() error: %v, wantErr: %v", err, tt.wantError) 901 case err != nil && tt.wantError == nil: 902 t.Errorf("deleteObjects() error: %v, wantErr: %v", err, tt.wantError) 903 case err != nil && tt.wantError != nil: 904 if err.Error() != tt.wantError.Error() { 905 t.Errorf("deleteObjects() error: %v, wantErr: %v", err, tt.wantError) 906 } 907 } 908 }) 909 } 910 } 911 912 func Test_shareObject(t *testing.T) { 913 tAssert := assert.New(t) 914 ctx, cancel := context.WithCancel(context.Background()) 915 defer cancel() 916 client := s3ClientMock{} 917 type args struct { 918 r *http.Request 919 versionID string 920 expires string 921 shareFunc func(ctx context.Context, versionID string, expires time.Duration) (string, *probe.Error) 922 } 923 tests := []struct { 924 test string 925 args args 926 wantError error 927 expected string 928 }{ 929 { 930 test: "return sharefunc url base64 encoded with host name", 931 args: args{ 932 r: &http.Request{ 933 TLS: nil, 934 Host: "localhost:9090", 935 }, 936 versionID: "2121434", 937 expires: "30s", 938 shareFunc: func(_ context.Context, _ string, _ time.Duration) (string, *probe.Error) { 939 return "http://someurl", nil 940 }, 941 }, 942 943 wantError: nil, 944 expected: "http://localhost:9090/api/v1/download-shared-object/aHR0cDovL3NvbWV1cmw=", 945 }, 946 { 947 test: "return https scheme if url uses TLS", 948 args: args{ 949 r: &http.Request{ 950 TLS: &tls.ConnectionState{}, 951 Host: "localhost:9090", 952 }, 953 versionID: "2121434", 954 expires: "30s", 955 shareFunc: func(_ context.Context, _ string, _ time.Duration) (string, *probe.Error) { 956 return "http://someurl", nil 957 }, 958 }, 959 960 wantError: nil, 961 expected: "https://localhost:9090/api/v1/download-shared-object/aHR0cDovL3NvbWV1cmw=", 962 }, 963 { 964 test: "returns invalid expire duration if expiration is invalid", 965 args: args{ 966 r: &http.Request{ 967 TLS: nil, 968 Host: "localhost:9090", 969 }, 970 versionID: "2121434", 971 expires: "invalid", 972 shareFunc: func(_ context.Context, _ string, _ time.Duration) (string, *probe.Error) { 973 return "http://someurl", nil 974 }, 975 }, 976 wantError: errors.New("time: invalid duration \"invalid\""), 977 }, 978 { 979 test: "add default expiration if expiration is empty", 980 args: args{ 981 r: &http.Request{ 982 TLS: nil, 983 Host: "localhost:9090", 984 }, 985 versionID: "2121434", 986 expires: "", 987 shareFunc: func(_ context.Context, _ string, _ time.Duration) (string, *probe.Error) { 988 return "http://someurl", nil 989 }, 990 }, 991 wantError: nil, 992 expected: "http://localhost:9090/api/v1/download-shared-object/aHR0cDovL3NvbWV1cmw=", 993 }, 994 { 995 test: "return error if sharefunc returns error", 996 args: args{ 997 r: &http.Request{ 998 TLS: nil, 999 Host: "localhost:9090", 1000 }, 1001 versionID: "2121434", 1002 expires: "3h", 1003 shareFunc: func(_ context.Context, _ string, _ time.Duration) (string, *probe.Error) { 1004 return "", probe.NewError(errors.New("probe error")) 1005 }, 1006 }, 1007 wantError: errors.New("probe error"), 1008 }, 1009 { 1010 test: "return shareFunc url base64 encoded url-safe", 1011 args: args{ 1012 r: &http.Request{ 1013 TLS: nil, 1014 Host: "localhost:9090", 1015 }, 1016 versionID: "2121434", 1017 expires: "3h", 1018 shareFunc: func(_ context.Context, _ string, _ time.Duration) (string, *probe.Error) { 1019 // https://127.0.0.1:9000/cestest/Audio%20icon.svg?X-Amz-Algorithm=AWS4-HMAC-SHA256 using StdEncoding adds an extra `/` making it not url safe 1020 return "https://127.0.0.1:9000/cestest/Audio%20icon.svg?X-Amz-Algorithm=AWS4-HMAC-SHA256", nil 1021 }, 1022 }, 1023 wantError: nil, 1024 expected: "http://localhost:9090/api/v1/download-shared-object/aHR0cHM6Ly8xMjcuMC4wLjE6OTAwMC9jZXN0ZXN0L0F1ZGlvJTIwaWNvbi5zdmc_WC1BbXotQWxnb3JpdGhtPUFXUzQtSE1BQy1TSEEyNTY=", 1025 }, 1026 } 1027 1028 for _, tt := range tests { 1029 t.Run(tt.test, func(_ *testing.T) { 1030 mcShareDownloadMock = tt.args.shareFunc 1031 url, err := getShareObjectURL(ctx, client, tt.args.r, tt.args.versionID, tt.args.expires) 1032 if tt.wantError != nil { 1033 if !reflect.DeepEqual(err, tt.wantError) { 1034 t.Errorf("getShareObjectURL() error: `%s`, wantErr: `%s`", err, tt.wantError) 1035 return 1036 } 1037 } else { 1038 tAssert.Equal(*url, tt.expected) 1039 } 1040 }) 1041 } 1042 } 1043 1044 func Test_putObjectLegalHold(t *testing.T) { 1045 ctx, cancel := context.WithCancel(context.Background()) 1046 defer cancel() 1047 client := minioClientMock{} 1048 type args struct { 1049 bucket string 1050 prefix string 1051 versionID string 1052 status models.ObjectLegalHoldStatus 1053 legalHoldFunc func(ctx context.Context, bucketName, objectName string, opts minio.PutObjectLegalHoldOptions) error 1054 } 1055 tests := []struct { 1056 test string 1057 args args 1058 wantError error 1059 }{ 1060 { 1061 test: "Put Object Legal hold enabled status", 1062 args: args{ 1063 bucket: "buck1", 1064 versionID: "someversion", 1065 prefix: "folder/file.txt", 1066 status: models.ObjectLegalHoldStatusEnabled, 1067 legalHoldFunc: func(_ context.Context, _, _ string, _ minio.PutObjectLegalHoldOptions) error { 1068 return nil 1069 }, 1070 }, 1071 wantError: nil, 1072 }, 1073 { 1074 test: "Put Object Legal hold disabled status", 1075 args: args{ 1076 bucket: "buck1", 1077 versionID: "someversion", 1078 prefix: "folder/file.txt", 1079 status: models.ObjectLegalHoldStatusDisabled, 1080 legalHoldFunc: func(_ context.Context, _, _ string, _ minio.PutObjectLegalHoldOptions) error { 1081 return nil 1082 }, 1083 }, 1084 wantError: nil, 1085 }, 1086 { 1087 test: "Handle error on legalhold func", 1088 args: args{ 1089 bucket: "buck1", 1090 versionID: "someversion", 1091 prefix: "folder/file.txt", 1092 status: models.ObjectLegalHoldStatusDisabled, 1093 legalHoldFunc: func(_ context.Context, _, _ string, _ minio.PutObjectLegalHoldOptions) error { 1094 return errors.New("new error") 1095 }, 1096 }, 1097 wantError: errors.New("new error"), 1098 }, 1099 } 1100 1101 for _, tt := range tests { 1102 t.Run(tt.test, func(_ *testing.T) { 1103 minioPutObjectLegalHoldMock = tt.args.legalHoldFunc 1104 err := setObjectLegalHold(ctx, client, tt.args.bucket, tt.args.prefix, tt.args.versionID, tt.args.status) 1105 if !reflect.DeepEqual(err, tt.wantError) { 1106 t.Errorf("setObjectLegalHold() error: %v, wantErr: %v", err, tt.wantError) 1107 return 1108 } 1109 }) 1110 } 1111 } 1112 1113 func Test_putObjectRetention(t *testing.T) { 1114 tAssert := assert.New(t) 1115 ctx, cancel := context.WithCancel(context.Background()) 1116 defer cancel() 1117 client := minioClientMock{} 1118 type args struct { 1119 bucket string 1120 prefix string 1121 versionID string 1122 opts *models.PutObjectRetentionRequest 1123 retentionFunc func(ctx context.Context, bucketName, objectName string, opts minio.PutObjectRetentionOptions) error 1124 } 1125 tests := []struct { 1126 test string 1127 args args 1128 wantError error 1129 }{ 1130 { 1131 test: "Put Object retention governance", 1132 args: args{ 1133 bucket: "buck1", 1134 versionID: "someversion", 1135 prefix: "folder/file.txt", 1136 opts: &models.PutObjectRetentionRequest{ 1137 Expires: swag.String("2006-01-02T15:04:05Z"), 1138 GovernanceBypass: false, 1139 Mode: models.NewObjectRetentionMode(models.ObjectRetentionModeGovernance), 1140 }, 1141 retentionFunc: func(_ context.Context, _, _ string, _ minio.PutObjectRetentionOptions) error { 1142 return nil 1143 }, 1144 }, 1145 wantError: nil, 1146 }, 1147 { 1148 test: "Put Object retention compliance", 1149 args: args{ 1150 bucket: "buck1", 1151 versionID: "someversion", 1152 prefix: "folder/file.txt", 1153 opts: &models.PutObjectRetentionRequest{ 1154 Expires: swag.String("2006-01-02T15:04:05Z"), 1155 GovernanceBypass: false, 1156 Mode: models.NewObjectRetentionMode(models.ObjectRetentionModeCompliance), 1157 }, 1158 retentionFunc: func(_ context.Context, _, _ string, _ minio.PutObjectRetentionOptions) error { 1159 return nil 1160 }, 1161 }, 1162 wantError: nil, 1163 }, 1164 { 1165 test: "Empty opts should return error", 1166 args: args{ 1167 bucket: "buck1", 1168 versionID: "someversion", 1169 prefix: "folder/file.txt", 1170 opts: nil, 1171 retentionFunc: func(_ context.Context, _, _ string, _ minio.PutObjectRetentionOptions) error { 1172 return nil 1173 }, 1174 }, 1175 wantError: errors.New("object retention options can't be nil"), 1176 }, 1177 { 1178 test: "Empty expire on opts should return error", 1179 args: args{ 1180 bucket: "buck1", 1181 versionID: "someversion", 1182 prefix: "folder/file.txt", 1183 opts: &models.PutObjectRetentionRequest{ 1184 Expires: nil, 1185 GovernanceBypass: false, 1186 Mode: models.NewObjectRetentionMode(models.ObjectRetentionModeCompliance), 1187 }, 1188 retentionFunc: func(_ context.Context, _, _ string, _ minio.PutObjectRetentionOptions) error { 1189 return nil 1190 }, 1191 }, 1192 wantError: errors.New("object retention expires can't be nil"), 1193 }, 1194 { 1195 test: "Handle invalid expire time", 1196 args: args{ 1197 bucket: "buck1", 1198 versionID: "someversion", 1199 prefix: "folder/file.txt", 1200 opts: &models.PutObjectRetentionRequest{ 1201 Expires: swag.String("invalidtime"), 1202 GovernanceBypass: false, 1203 Mode: models.NewObjectRetentionMode(models.ObjectRetentionModeCompliance), 1204 }, 1205 retentionFunc: func(_ context.Context, _, _ string, _ minio.PutObjectRetentionOptions) error { 1206 return nil 1207 }, 1208 }, 1209 wantError: errors.New("parsing time \"invalidtime\" as \"2006-01-02T15:04:05Z07:00\": cannot parse \"invalidtime\" as \"2006\""), 1210 }, 1211 { 1212 test: "Handle error on retention func", 1213 args: args{ 1214 bucket: "buck1", 1215 versionID: "someversion", 1216 prefix: "folder/file.txt", 1217 opts: &models.PutObjectRetentionRequest{ 1218 Expires: swag.String("2006-01-02T15:04:05Z"), 1219 GovernanceBypass: false, 1220 Mode: models.NewObjectRetentionMode(models.ObjectRetentionModeCompliance), 1221 }, 1222 retentionFunc: func(_ context.Context, _, _ string, _ minio.PutObjectRetentionOptions) error { 1223 return errors.New("new Error") 1224 }, 1225 }, 1226 wantError: errors.New("new Error"), 1227 }, 1228 } 1229 1230 for _, tt := range tests { 1231 t.Run(tt.test, func(_ *testing.T) { 1232 minioPutObjectRetentionMock = tt.args.retentionFunc 1233 err := setObjectRetention(ctx, client, tt.args.bucket, tt.args.prefix, tt.args.versionID, tt.args.opts) 1234 if tt.wantError != nil { 1235 fmt.Println(t.Name()) 1236 tAssert.Equal(tt.wantError.Error(), err.Error(), fmt.Sprintf("setObjectRetention() error: `%s`, wantErr: `%s`", err, tt.wantError)) 1237 } else { 1238 tAssert.Nil(err, fmt.Sprintf("setObjectRetention() error: %v, wantErr: %v", err, tt.wantError)) 1239 } 1240 }) 1241 } 1242 } 1243 1244 func Test_deleteObjectRetention(t *testing.T) { 1245 tAssert := assert.New(t) 1246 ctx, cancel := context.WithCancel(context.Background()) 1247 defer cancel() 1248 client := minioClientMock{} 1249 type args struct { 1250 bucket string 1251 prefix string 1252 versionID string 1253 retentionFunc func(ctx context.Context, bucketName, objectName string, opts minio.PutObjectRetentionOptions) error 1254 } 1255 tests := []struct { 1256 test string 1257 args args 1258 wantError error 1259 }{ 1260 { 1261 test: "Delete Object retention governance", 1262 args: args{ 1263 bucket: "buck1", 1264 versionID: "someversion", 1265 prefix: "folder/file.txt", 1266 retentionFunc: func(_ context.Context, _, _ string, _ minio.PutObjectRetentionOptions) error { 1267 return nil 1268 }, 1269 }, 1270 wantError: nil, 1271 }, 1272 } 1273 for _, tt := range tests { 1274 t.Run(tt.test, func(_ *testing.T) { 1275 minioPutObjectRetentionMock = tt.args.retentionFunc 1276 err := deleteObjectRetention(ctx, client, tt.args.bucket, tt.args.prefix, tt.args.versionID) 1277 if tt.wantError != nil { 1278 fmt.Println(t.Name()) 1279 tAssert.Equal(tt.wantError.Error(), err.Error(), fmt.Sprintf("deleteObjectRetention() error: `%s`, wantErr: `%s`", err, tt.wantError)) 1280 } else { 1281 tAssert.Nil(err, fmt.Sprintf("deleteObjectRetention() error: %v, wantErr: %v", err, tt.wantError)) 1282 } 1283 }) 1284 } 1285 } 1286 1287 func Test_getObjectInfo(t *testing.T) { 1288 tAssert := assert.New(t) 1289 ctx, cancel := context.WithCancel(context.Background()) 1290 defer cancel() 1291 client := minioClientMock{} 1292 1293 type args struct { 1294 bucketName string 1295 prefix string 1296 statFunc func(ctx context.Context, bucketName string, prefix string, opts minio.GetObjectOptions) (objectInfo minio.ObjectInfo, err error) 1297 } 1298 tests := []struct { 1299 test string 1300 args args 1301 wantError error 1302 }{ 1303 { 1304 test: "Test function not returns an error", 1305 args: args{ 1306 bucketName: "bucket1", 1307 prefix: "someprefix", 1308 statFunc: func(_ context.Context, _ string, _ string, _ minio.GetObjectOptions) (minio.ObjectInfo, error) { 1309 return minio.ObjectInfo{}, nil 1310 }, 1311 }, 1312 wantError: nil, 1313 }, 1314 { 1315 test: "Test function returns an error", 1316 args: args{ 1317 bucketName: "bucket2", 1318 prefix: "someprefi2", 1319 statFunc: func(_ context.Context, _ string, _ string, _ minio.GetObjectOptions) (minio.ObjectInfo, error) { 1320 return minio.ObjectInfo{}, errors.New("new Error") 1321 }, 1322 }, 1323 wantError: errors.New("new Error"), 1324 }, 1325 } 1326 for _, tt := range tests { 1327 t.Run(tt.test, func(_ *testing.T) { 1328 minioStatObjectMock = tt.args.statFunc 1329 _, err := getObjectInfo(ctx, client, tt.args.bucketName, tt.args.prefix) 1330 if tt.wantError != nil { 1331 fmt.Println(t.Name()) 1332 tAssert.Equal(tt.wantError.Error(), err.Error(), fmt.Sprintf("getObjectInfo() error: `%s`, wantErr: `%s`", err, tt.wantError)) 1333 } else { 1334 tAssert.Nil(err, fmt.Sprintf("getObjectInfo() error: %v, wantErr: %v", err, tt.wantError)) 1335 } 1336 }) 1337 } 1338 } 1339 1340 func Test_getScheme(t *testing.T) { 1341 type args struct { 1342 rawurl string 1343 } 1344 tests := []struct { 1345 name string 1346 args args 1347 wantScheme string 1348 wantPath string 1349 }{ 1350 { 1351 name: "expected", 1352 args: args{ 1353 rawurl: "http://domain.com", 1354 }, 1355 wantScheme: "http", 1356 wantPath: "//domain.com", 1357 }, 1358 { 1359 name: "no scheme", 1360 args: args{ 1361 rawurl: "domain.com", 1362 }, 1363 wantScheme: "", 1364 wantPath: "domain.com", 1365 }, 1366 } 1367 for _, tt := range tests { 1368 t.Run(tt.name, func(_ *testing.T) { 1369 gotScheme, gotPath := getScheme(tt.args.rawurl) 1370 assert.Equalf(t, tt.wantScheme, gotScheme, "getScheme(%v)", tt.args.rawurl) 1371 assert.Equalf(t, tt.wantPath, gotPath, "getScheme(%v)", tt.args.rawurl) 1372 }) 1373 } 1374 } 1375 1376 func Test_splitSpecial(t *testing.T) { 1377 type args struct { 1378 s string 1379 delimiter string 1380 cutdelimiter bool 1381 } 1382 tests := []struct { 1383 name string 1384 args args 1385 want string 1386 want1 string 1387 }{ 1388 { 1389 name: "Expected", 1390 args: args{ 1391 s: "[s , s]", 1392 delimiter: ",", 1393 cutdelimiter: false, 1394 }, 1395 want: "[s ", 1396 want1: ", s]", 1397 }, 1398 { 1399 name: "no delimited", 1400 args: args{ 1401 s: "[s s]", 1402 delimiter: "", 1403 cutdelimiter: false, 1404 }, 1405 want: "", 1406 want1: "[s s]", 1407 }, 1408 { 1409 name: "Expected not delim", 1410 args: args{ 1411 s: "[s , s]", 1412 delimiter: ",", 1413 cutdelimiter: true, 1414 }, 1415 want: "[s ", 1416 want1: " s]", 1417 }, 1418 } 1419 for _, tt := range tests { 1420 t.Run(tt.name, func(_ *testing.T) { 1421 got, got1 := splitSpecial(tt.args.s, tt.args.delimiter, tt.args.cutdelimiter) 1422 assert.Equalf(t, tt.want, got, "splitSpecial(%v, %v, %v)", tt.args.s, tt.args.delimiter, tt.args.cutdelimiter) 1423 assert.Equalf(t, tt.want1, got1, "splitSpecial(%v, %v, %v)", tt.args.s, tt.args.delimiter, tt.args.cutdelimiter) 1424 }) 1425 } 1426 } 1427 1428 func Test_getHost(t *testing.T) { 1429 type args struct { 1430 authority string 1431 } 1432 tests := []struct { 1433 name string 1434 args args 1435 wantHost string 1436 }{ 1437 { 1438 name: "Expected", 1439 args: args{ 1440 authority: "username@domain.com", 1441 }, 1442 wantHost: "", 1443 }, 1444 { 1445 name: "Expected 2", 1446 args: args{ 1447 authority: "domain.com", 1448 }, 1449 wantHost: "domain.com", 1450 }, 1451 } 1452 for _, tt := range tests { 1453 t.Run(tt.name, func(_ *testing.T) { 1454 assert.Equalf(t, tt.wantHost, getHost(tt.args.authority), "getHost(%v)", tt.args.authority) 1455 }) 1456 } 1457 } 1458 1459 func Test_newClientURL(t *testing.T) { 1460 type args struct { 1461 urlStr string 1462 } 1463 tests := []struct { 1464 name string 1465 args args 1466 want mc.ClientURL 1467 }{ 1468 { 1469 name: "Expected", 1470 args: args{ 1471 urlStr: "http://domain.com", 1472 }, 1473 want: mc.ClientURL{ 1474 Type: 0, 1475 Scheme: "http", 1476 Host: "domain.com", 1477 Path: "/", 1478 SchemeSeparator: "://", 1479 Separator: 47, 1480 }, 1481 }, 1482 { 1483 name: "Expected file", 1484 args: args{ 1485 urlStr: "file.jpeg", 1486 }, 1487 want: mc.ClientURL{ 1488 Type: fileSystem, 1489 Path: "file.jpeg", 1490 Separator: filepath.Separator, 1491 }, 1492 }, 1493 } 1494 for _, tt := range tests { 1495 t.Run(tt.name, func(_ *testing.T) { 1496 assert.Equalf(t, tt.want, *newClientURL(tt.args.urlStr), "newClientURL(%v)", tt.args.urlStr) 1497 }) 1498 } 1499 } 1500 1501 func Test_getMultipleFilesDownloadResponse(t *testing.T) { 1502 type args struct { 1503 session *models.Principal 1504 params object.DownloadMultipleObjectsParams 1505 } 1506 1507 tests := []struct { 1508 name string 1509 args args 1510 want middleware.Responder 1511 want1 *CodedAPIError 1512 }{ 1513 { 1514 name: "test no objects sent for download", 1515 args: args{ 1516 session: nil, 1517 params: object.DownloadMultipleObjectsParams{ 1518 HTTPRequest: &http.Request{}, 1519 BucketName: "test-bucket", 1520 ObjectList: nil, 1521 }, 1522 }, 1523 want: nil, 1524 want1: nil, 1525 }, 1526 { 1527 name: "few objects sent for download", 1528 args: args{ 1529 session: nil, 1530 params: object.DownloadMultipleObjectsParams{ 1531 HTTPRequest: &http.Request{}, 1532 BucketName: "test-bucket", 1533 ObjectList: []string{"test.txt", ",y-obj.doc", "z-obj.png"}, 1534 }, 1535 }, 1536 want: nil, 1537 want1: nil, 1538 }, 1539 { 1540 name: "few prefixes and a file sent for download", 1541 args: args{ 1542 session: nil, 1543 params: object.DownloadMultipleObjectsParams{ 1544 HTTPRequest: &http.Request{}, 1545 BucketName: "test-bucket", 1546 ObjectList: []string{"my-folder/", "my-folder/test-nested", "z-obj.png"}, 1547 }, 1548 }, 1549 want: nil, 1550 want1: nil, 1551 }, 1552 } 1553 for _, tt := range tests { 1554 t.Run(tt.name, func(_ *testing.T) { 1555 got, got1 := getMultipleFilesDownloadResponse(tt.args.session, tt.args.params) 1556 assert.Equal(t, tt.want1, got1) 1557 assert.NotNil(t, got) 1558 }) 1559 } 1560 }