github.com/minio/console@v1.4.1/api/user_buckets_events_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 "errors" 22 "fmt" 23 "testing" 24 25 "github.com/go-openapi/swag" 26 "github.com/minio/console/models" 27 "github.com/minio/mc/pkg/probe" 28 "github.com/minio/minio-go/v7/pkg/notification" 29 "github.com/stretchr/testify/assert" 30 ) 31 32 // assigning mock at runtime instead of compile time 33 var minioGetBucketNotificationMock func(ctx context.Context, bucketName string) (bucketNotification notification.Configuration, err error) 34 35 // mock function of getBucketNotification() 36 func (mc minioClientMock) getBucketNotification(ctx context.Context, bucketName string) (bucketNotification notification.Configuration, err error) { 37 return minioGetBucketNotificationMock(ctx, bucketName) 38 } 39 40 // // Mock mc S3Client functions //// 41 var ( 42 mcAddNotificationConfigMock func(ctx context.Context, arn string, events []string, prefix, suffix string, ignoreExisting bool) *probe.Error 43 mcRemoveNotificationConfigMock func(ctx context.Context, arn string, event string, prefix string, suffix string) *probe.Error 44 ) 45 46 // Define a mock struct of mc S3Client interface implementation 47 type s3ClientMock struct{} 48 49 // implements mc.S3Client.AddNotificationConfigMock() 50 func (c s3ClientMock) addNotificationConfig(ctx context.Context, arn string, events []string, prefix, suffix string, ignoreExisting bool) *probe.Error { 51 return mcAddNotificationConfigMock(ctx, arn, events, prefix, suffix, ignoreExisting) 52 } 53 54 // implements mc.S3Client.DeleteBucketEventNotification() 55 func (c s3ClientMock) removeNotificationConfig(ctx context.Context, arn string, event string, prefix string, suffix string) *probe.Error { 56 return mcRemoveNotificationConfigMock(ctx, arn, event, prefix, suffix) 57 } 58 59 func TestAddBucketNotification(t *testing.T) { 60 assert := assert.New(t) 61 // mock minIO client 62 ctx, cancel := context.WithCancel(context.Background()) 63 defer cancel() 64 client := s3ClientMock{} 65 function := "createBucketEvent()" 66 // Test-1: createBucketEvent() set an event with empty parameters and events, should set default values with no error 67 testArn := "arn:minio:sqs::test:postgresql" 68 testNotificationEvents := []models.NotificationEventType{} 69 testPrefix := "" 70 testSuffix := "" 71 testIgnoreExisting := false 72 mcAddNotificationConfigMock = func(_ context.Context, _ string, _ []string, _, _ string, _ bool) *probe.Error { 73 return nil 74 } 75 if err := createBucketEvent(ctx, client, testArn, testNotificationEvents, testPrefix, testSuffix, testIgnoreExisting); err != nil { 76 t.Errorf("Failed on %s:, error occurred: %s", function, err.Error()) 77 } 78 79 // Test-2: createBucketEvent() with different even types in list shouls create event with no errors 80 testArn = "arn:minio:sqs::test:postgresql" 81 testNotificationEvents = []models.NotificationEventType{ 82 models.NotificationEventTypePut, 83 models.NotificationEventTypeGet, 84 } 85 testPrefix = "photos/" 86 testSuffix = ".jpg" 87 testIgnoreExisting = true 88 mcAddNotificationConfigMock = func(_ context.Context, _ string, _ []string, _, _ string, _ bool) *probe.Error { 89 return nil 90 } 91 if err := createBucketEvent(ctx, client, testArn, testNotificationEvents, testPrefix, testSuffix, testIgnoreExisting); err != nil { 92 t.Errorf("Failed on %s:, error occurred: %s", function, err.Error()) 93 } 94 95 // Test-3 createBucketEvent() S3Client.AddNotificationConfig returns an error and is handled correctly 96 mcAddNotificationConfigMock = func(_ context.Context, _ string, _ []string, _, _ string, _ bool) *probe.Error { 97 return probe.NewError(errors.New("error")) 98 } 99 if err := createBucketEvent(ctx, client, testArn, testNotificationEvents, testPrefix, testSuffix, testIgnoreExisting); assert.Error(err) { 100 assert.Equal("error", err.Error()) 101 } 102 } 103 104 func TestDeleteBucketNotification(t *testing.T) { 105 ctx, cancel := context.WithCancel(context.Background()) 106 defer cancel() 107 assert := assert.New(t) 108 // mock minIO client 109 client := s3ClientMock{} 110 function := "deleteBucketEventNotification()" 111 // Test-1: deleteBucketEventNotification() delete a bucket event notification 112 testArn := "arn:minio:sqs::test:postgresql" 113 // arn string, events []models.NotificationEventType, prefix, suffix *string 114 events := []models.NotificationEventType{ 115 models.NotificationEventTypeGet, 116 models.NotificationEventTypeDelete, 117 models.NotificationEventTypePut, 118 } 119 prefix := "/photos" 120 suffix := ".jpg" 121 mcRemoveNotificationConfigMock = func(_ context.Context, _ string, _ string, _ string, _ string) *probe.Error { 122 return nil 123 } 124 if err := deleteBucketEventNotification(ctx, client, testArn, events, swag.String(prefix), swag.String(suffix)); err != nil { 125 t.Errorf("Failed on %s:, error occurred: %s", function, err.Error()) 126 } 127 128 // Test-2 deleteBucketEventNotification() S3Client.DeleteBucketEventNotification returns an error and is handled correctly 129 mcRemoveNotificationConfigMock = func(_ context.Context, _ string, _ string, _ string, _ string) *probe.Error { 130 return probe.NewError(errors.New("error")) 131 } 132 if err := deleteBucketEventNotification(ctx, client, testArn, events, swag.String(prefix), swag.String(suffix)); assert.Error(err) { 133 assert.Equal("error", err.Error()) 134 } 135 136 // Test-3 joinNotificationEvents() verify that it returns the events as a single string separated by commas 137 function = "joinNotificationEvents()" 138 eventString := joinNotificationEvents(events) 139 assert.Equal("get,delete,put", eventString, fmt.Sprintf("Failed on %s:", function)) 140 } 141 142 func TestListBucketEvents(t *testing.T) { 143 assert := assert.New(t) 144 // mock minIO client 145 minClient := minioClientMock{} 146 function := "listBucketEvents()" 147 148 ////// Test-1 : listBucketEvents() get list of events for a particular bucket only one config 149 // mock bucketNotification response from MinIO 150 mockBucketN := notification.Configuration{ 151 LambdaConfigs: []notification.LambdaConfig{}, 152 TopicConfigs: []notification.TopicConfig{}, 153 QueueConfigs: []notification.QueueConfig{ 154 { 155 Queue: "arn:minio:sqs::test:postgresql", 156 Config: notification.Config{ 157 ID: "", 158 Events: []notification.EventType{ 159 notification.ObjectAccessedAll, 160 notification.ObjectCreatedAll, 161 notification.ObjectRemovedAll, 162 }, 163 Filter: ¬ification.Filter{ 164 S3Key: notification.S3Key{ 165 FilterRules: []notification.FilterRule{ 166 { 167 Name: "suffix", 168 Value: ".jpg", 169 }, 170 { 171 Name: "prefix", 172 Value: "file/", 173 }, 174 }, 175 }, 176 }, 177 }, 178 }, 179 }, 180 } 181 expectedOutput := []*models.NotificationConfig{ 182 { 183 Arn: swag.String("arn:minio:sqs::test:postgresql"), 184 ID: "", 185 Prefix: "file/", 186 Suffix: ".jpg", 187 Events: []models.NotificationEventType{ 188 models.NotificationEventTypeGet, 189 models.NotificationEventTypePut, 190 models.NotificationEventTypeDelete, 191 }, 192 }, 193 } 194 minioGetBucketNotificationMock = func(_ context.Context, _ string) (bucketNotification notification.Configuration, err error) { 195 return mockBucketN, nil 196 } 197 eventConfigs, err := listBucketEvents(minClient, "bucket") 198 if err != nil { 199 t.Errorf("Failed on %s:, error occurred: %s", function, err.Error()) 200 } 201 // verify length of buckets is correct 202 assert.Equal(len(expectedOutput), len(eventConfigs), fmt.Sprintf("Failed on %s: length of lists is not the same", function)) 203 for i, conf := range eventConfigs { 204 assert.Equal(expectedOutput[i].Arn, conf.Arn) 205 assert.Equal(expectedOutput[i].ID, conf.ID) 206 assert.Equal(expectedOutput[i].Suffix, conf.Suffix) 207 assert.Equal(expectedOutput[i].Prefix, conf.Prefix) 208 assert.Equal(len(expectedOutput[i].Events), len(conf.Events), fmt.Sprintf("Failed on %s: length of lists is not the same", function)) 209 for j, event := range conf.Events { 210 assert.Equal(expectedOutput[i].Events[j], event) 211 } 212 } 213 214 ////// Test-2 : listBucketEvents() get list of events no filters 215 mockBucketN = notification.Configuration{ 216 LambdaConfigs: []notification.LambdaConfig{}, 217 TopicConfigs: []notification.TopicConfig{}, 218 QueueConfigs: []notification.QueueConfig{ 219 { 220 Queue: "arn:minio:sqs::test:postgresql", 221 Config: notification.Config{ 222 ID: "", 223 Events: []notification.EventType{ 224 notification.ObjectRemovedAll, 225 }, 226 }, 227 }, 228 }, 229 } 230 expectedOutput = []*models.NotificationConfig{ 231 { 232 Arn: swag.String("arn:minio:sqs::test:postgresql"), 233 ID: "", 234 Prefix: "", 235 Suffix: "", 236 Events: []models.NotificationEventType{ 237 models.NotificationEventTypeDelete, 238 }, 239 }, 240 } 241 minioGetBucketNotificationMock = func(_ context.Context, _ string) (bucketNotification notification.Configuration, err error) { 242 return mockBucketN, nil 243 } 244 eventConfigs, err = listBucketEvents(minClient, "bucket") 245 if err != nil { 246 t.Errorf("Failed on %s:, error occurred: %s", function, err.Error()) 247 } 248 // verify length of buckets is correct 249 assert.Equal(len(expectedOutput), len(eventConfigs), fmt.Sprintf("Failed on %s: length of lists is not the same", function)) 250 for i, conf := range eventConfigs { 251 assert.Equal(expectedOutput[i].Arn, conf.Arn) 252 assert.Equal(expectedOutput[i].ID, conf.ID) 253 assert.Equal(expectedOutput[i].Suffix, conf.Suffix) 254 assert.Equal(expectedOutput[i].Prefix, conf.Prefix) 255 assert.Equal(len(expectedOutput[i].Events), len(conf.Events), fmt.Sprintf("Failed on %s: length of lists is not the same", function)) 256 for j, event := range conf.Events { 257 assert.Equal(expectedOutput[i].Events[j], event) 258 } 259 } 260 261 ////// Test-3 : listBucketEvents() get list of events 262 mockBucketN = notification.Configuration{ 263 LambdaConfigs: []notification.LambdaConfig{ 264 { 265 Lambda: "lambda", 266 Config: notification.Config{ 267 ID: "", 268 Events: []notification.EventType{ 269 notification.ObjectRemovedAll, 270 }, 271 Filter: ¬ification.Filter{ 272 S3Key: notification.S3Key{ 273 FilterRules: []notification.FilterRule{ 274 { 275 Name: "suffix", 276 Value: ".png", 277 }, 278 { 279 Name: "prefix", 280 Value: "lambda/", 281 }, 282 }, 283 }, 284 }, 285 }, 286 }, 287 }, 288 TopicConfigs: []notification.TopicConfig{ 289 { 290 Topic: "topic", 291 Config: notification.Config{ 292 ID: "", 293 Events: []notification.EventType{ 294 notification.ObjectRemovedAll, 295 }, 296 Filter: ¬ification.Filter{ 297 S3Key: notification.S3Key{ 298 FilterRules: []notification.FilterRule{ 299 { 300 Name: "suffix", 301 Value: ".gif", 302 }, 303 { 304 Name: "prefix", 305 Value: "topic/", 306 }, 307 }, 308 }, 309 }, 310 }, 311 }, 312 }, 313 QueueConfigs: []notification.QueueConfig{ 314 { 315 Queue: "arn:minio:sqs::test:postgresql", 316 Config: notification.Config{ 317 ID: "", 318 Events: []notification.EventType{ 319 notification.ObjectRemovedAll, 320 }, 321 Filter: ¬ification.Filter{ 322 S3Key: notification.S3Key{ 323 FilterRules: []notification.FilterRule{}, 324 }, 325 }, 326 }, 327 }, 328 }, 329 } 330 // order matters in output: topic,queue then lambda are given respectively 331 expectedOutput = []*models.NotificationConfig{ 332 { 333 Arn: swag.String("topic"), 334 ID: "", 335 Prefix: "topic/", 336 Suffix: ".gif", 337 Events: []models.NotificationEventType{ 338 models.NotificationEventTypeDelete, 339 }, 340 }, 341 { 342 Arn: swag.String("arn:minio:sqs::test:postgresql"), 343 ID: "", 344 Prefix: "", 345 Suffix: "", 346 Events: []models.NotificationEventType{ 347 models.NotificationEventTypeDelete, 348 }, 349 }, 350 { 351 Arn: swag.String("lambda"), 352 ID: "", 353 Prefix: "lambda/", 354 Suffix: ".png", 355 Events: []models.NotificationEventType{ 356 models.NotificationEventTypeDelete, 357 }, 358 }, 359 } 360 minioGetBucketNotificationMock = func(_ context.Context, _ string) (bucketNotification notification.Configuration, err error) { 361 return mockBucketN, nil 362 } 363 eventConfigs, err = listBucketEvents(minClient, "bucket") 364 if err != nil { 365 t.Errorf("Failed on %s:, error occurred: %s", function, err.Error()) 366 } 367 // verify length of buckets is correct 368 assert.Equal(len(expectedOutput), len(eventConfigs), fmt.Sprintf("Failed on %s: length of lists is not the same", function)) 369 for i, conf := range eventConfigs { 370 assert.Equal(expectedOutput[i].Arn, conf.Arn) 371 assert.Equal(expectedOutput[i].ID, conf.ID) 372 assert.Equal(expectedOutput[i].Suffix, conf.Suffix) 373 assert.Equal(expectedOutput[i].Prefix, conf.Prefix) 374 assert.Equal(len(expectedOutput[i].Events), len(conf.Events), fmt.Sprintf("Failed on %s: length of lists is not the same", function)) 375 for j, event := range conf.Events { 376 assert.Equal(expectedOutput[i].Events[j], event) 377 } 378 } 379 380 ////// Test-2 : listBucketEvents() Returns error and see that the error is handled correctly and returned 381 minioGetBucketNotificationMock = func(_ context.Context, _ string) (bucketNotification notification.Configuration, err error) { 382 return notification.Configuration{}, errors.New("error") 383 } 384 _, err = listBucketEvents(minClient, "bucket") 385 if assert.Error(err) { 386 assert.Equal("error", err.Error()) 387 } 388 }