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: &notification.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: &notification.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: &notification.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: &notification.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  }