github.com/kyma-incubator/compass/components/director@v0.0.0-20230623144113-d764f56ff805/internal/domain/webhook/service_test.go (about)

     1  package webhook_test
     2  
     3  import (
     4  	"context"
     5  	"encoding/json"
     6  	"testing"
     7  	"time"
     8  
     9  	"github.com/kyma-incubator/compass/components/director/pkg/graphql"
    10  
    11  	"github.com/kyma-incubator/compass/components/director/pkg/str"
    12  
    13  	"github.com/kyma-incubator/compass/components/director/pkg/apperrors"
    14  
    15  	"github.com/kyma-incubator/compass/components/director/internal/domain/tenant"
    16  	"github.com/kyma-incubator/compass/components/director/internal/domain/webhook"
    17  	"github.com/kyma-incubator/compass/components/director/internal/domain/webhook/automock"
    18  	"github.com/kyma-incubator/compass/components/director/internal/model"
    19  	"github.com/pkg/errors"
    20  	"github.com/stretchr/testify/assert"
    21  	"github.com/stretchr/testify/mock"
    22  	"github.com/stretchr/testify/require"
    23  )
    24  
    25  func TestService_Create(t *testing.T) {
    26  	// GIVEN
    27  	testErr := errors.New("Test error")
    28  	modelInput := fixModelWebhookInput("foo")
    29  
    30  	webhookModel := mock.MatchedBy(func(webhook *model.Webhook) bool {
    31  		return webhook.Type == modelInput.Type && webhook.URL == modelInput.URL
    32  	})
    33  
    34  	ctx := context.TODO()
    35  	ctx = tenant.SaveToContext(ctx, givenTenant(), givenExternalTenant())
    36  	ctxNoTenant := context.TODO()
    37  	ctxNoTenant = tenant.SaveToContext(ctx, "", "")
    38  
    39  	testCases := []struct {
    40  		Name         string
    41  		RepositoryFn func() *automock.WebhookRepository
    42  		UIDServiceFn func() *automock.UIDService
    43  		ExpectedErr  error
    44  		Context      context.Context
    45  	}{
    46  		{
    47  			Name: "Success",
    48  			RepositoryFn: func() *automock.WebhookRepository {
    49  				repo := &automock.WebhookRepository{}
    50  				repo.On("Create", ctx, givenTenant(), webhookModel).Return(nil).Once()
    51  				return repo
    52  			},
    53  			UIDServiceFn: func() *automock.UIDService {
    54  				svc := &automock.UIDService{}
    55  				svc.On("Generate").Return("foo").Once()
    56  				return svc
    57  			},
    58  			ExpectedErr: nil,
    59  			Context:     ctx,
    60  		},
    61  		{
    62  			Name: "Success when tenant is missing",
    63  			RepositoryFn: func() *automock.WebhookRepository {
    64  				repo := &automock.WebhookRepository{}
    65  				repo.On("Create", ctxNoTenant, "", webhookModel).Return(nil).Once()
    66  				return repo
    67  			},
    68  			UIDServiceFn: func() *automock.UIDService {
    69  				svc := &automock.UIDService{}
    70  				svc.On("Generate").Return("foo").Once()
    71  				return svc
    72  			},
    73  			ExpectedErr: nil,
    74  			Context:     ctxNoTenant,
    75  		},
    76  		{
    77  			Name: "Returns error when webhook creation failed",
    78  			RepositoryFn: func() *automock.WebhookRepository {
    79  				repo := &automock.WebhookRepository{}
    80  				repo.On("Create", ctx, givenTenant(), webhookModel).Return(testErr).Once()
    81  				return repo
    82  			},
    83  			UIDServiceFn: func() *automock.UIDService {
    84  				svc := &automock.UIDService{}
    85  				svc.On("Generate").Return("").Once()
    86  				return svc
    87  			},
    88  			ExpectedErr: testErr,
    89  			Context:     ctx,
    90  		},
    91  	}
    92  
    93  	for _, testCase := range testCases {
    94  		t.Run(testCase.Name, func(t *testing.T) {
    95  			repo := testCase.RepositoryFn()
    96  			uidSvc := testCase.UIDServiceFn()
    97  
    98  			svc := webhook.NewService(repo, nil, uidSvc, nil, nil, "")
    99  
   100  			// WHEN
   101  			result, err := svc.Create(testCase.Context, givenApplicationID(), *modelInput, model.ApplicationWebhookReference)
   102  
   103  			// THEN
   104  
   105  			if testCase.ExpectedErr == nil {
   106  				assert.NotEmpty(t, result)
   107  				require.NoError(t, err)
   108  			} else {
   109  				assert.Contains(t, err.Error(), testCase.ExpectedErr.Error())
   110  			}
   111  
   112  			mock.AssertExpectationsForObjects(t, repo, uidSvc)
   113  		})
   114  	}
   115  
   116  	t.Run("Returns error on loading tenant", func(t *testing.T) {
   117  		svc := webhook.NewService(nil, nil, nil, nil, nil, "")
   118  		// WHEN
   119  		_, err := svc.Create(context.TODO(), givenApplicationID(), *modelInput, model.ApplicationWebhookReference)
   120  		assert.True(t, apperrors.IsCannotReadTenant(err))
   121  	})
   122  }
   123  
   124  func TestService_CreateWithFormationTemplateWebhookType(t *testing.T) {
   125  	// GIVEN
   126  	testErr := errors.New("Test error")
   127  	modelInput := fixModelWebhookInput("foo")
   128  
   129  	webhookModel := mock.MatchedBy(func(webhook *model.Webhook) bool {
   130  		return webhook.Type == modelInput.Type && webhook.URL == modelInput.URL
   131  	})
   132  
   133  	ctx := context.TODO()
   134  	ctx = tenant.SaveToContext(ctx, givenTenant(), givenExternalTenant())
   135  	ctxNoTenant := context.TODO()
   136  	ctxNoTenant = tenant.SaveToContext(ctx, "", "")
   137  
   138  	testCases := []struct {
   139  		Name         string
   140  		RepositoryFn func() *automock.WebhookRepository
   141  		UIDServiceFn func() *automock.UIDService
   142  		TenantSvcFn  func() *automock.TenantService
   143  		ExpectedErr  error
   144  		Context      context.Context
   145  	}{
   146  		{
   147  			Name: "Success",
   148  			RepositoryFn: func() *automock.WebhookRepository {
   149  				repo := &automock.WebhookRepository{}
   150  				repo.On("Create", ctx, givenTenant(), webhookModel).Return(nil).Once()
   151  				return repo
   152  			},
   153  			TenantSvcFn: func() *automock.TenantService {
   154  				svc := &automock.TenantService{}
   155  				svc.On("ExtractTenantIDForTenantScopedFormationTemplates", ctx).Return(givenTenant(), nil).Once()
   156  				return svc
   157  			},
   158  			UIDServiceFn: func() *automock.UIDService {
   159  				svc := &automock.UIDService{}
   160  				svc.On("Generate").Return("foo").Once()
   161  				return svc
   162  			},
   163  			ExpectedErr: nil,
   164  			Context:     ctx,
   165  		},
   166  		{
   167  			Name: "Success when tenant is missing",
   168  			RepositoryFn: func() *automock.WebhookRepository {
   169  				repo := &automock.WebhookRepository{}
   170  				repo.On("Create", ctxNoTenant, "", webhookModel).Return(nil).Once()
   171  				return repo
   172  			},
   173  			TenantSvcFn: func() *automock.TenantService {
   174  				svc := &automock.TenantService{}
   175  				svc.On("ExtractTenantIDForTenantScopedFormationTemplates", ctxNoTenant).Return("", nil).Once()
   176  				return svc
   177  			},
   178  			UIDServiceFn: func() *automock.UIDService {
   179  				svc := &automock.UIDService{}
   180  				svc.On("Generate").Return("foo").Once()
   181  				return svc
   182  			},
   183  			ExpectedErr: nil,
   184  			Context:     ctxNoTenant,
   185  		},
   186  		{
   187  			Name: "Returns error when webhook creation failed",
   188  			RepositoryFn: func() *automock.WebhookRepository {
   189  				repo := &automock.WebhookRepository{}
   190  				repo.On("Create", ctx, givenTenant(), webhookModel).Return(testErr).Once()
   191  				return repo
   192  			},
   193  			TenantSvcFn: func() *automock.TenantService {
   194  				svc := &automock.TenantService{}
   195  				svc.On("ExtractTenantIDForTenantScopedFormationTemplates", ctx).Return(givenTenant(), nil).Once()
   196  				return svc
   197  			},
   198  			UIDServiceFn: func() *automock.UIDService {
   199  				svc := &automock.UIDService{}
   200  				svc.On("Generate").Return("").Once()
   201  				return svc
   202  			},
   203  			ExpectedErr: testErr,
   204  			Context:     ctx,
   205  		},
   206  		{
   207  			Name: "Error when getting tenant fails",
   208  			RepositoryFn: func() *automock.WebhookRepository {
   209  				return &automock.WebhookRepository{}
   210  			},
   211  			TenantSvcFn: func() *automock.TenantService {
   212  				svc := &automock.TenantService{}
   213  				svc.On("ExtractTenantIDForTenantScopedFormationTemplates", ctx).Return("", testErr).Once()
   214  				return svc
   215  			},
   216  			UIDServiceFn: func() *automock.UIDService {
   217  				return &automock.UIDService{}
   218  			},
   219  			ExpectedErr: testErr,
   220  			Context:     ctx,
   221  		},
   222  	}
   223  
   224  	for _, testCase := range testCases {
   225  		t.Run(testCase.Name, func(t *testing.T) {
   226  			repo := testCase.RepositoryFn()
   227  			uidSvc := testCase.UIDServiceFn()
   228  			tenantSvc := testCase.TenantSvcFn()
   229  			svc := webhook.NewService(repo, nil, uidSvc, tenantSvc, nil, "")
   230  
   231  			// WHEN
   232  			result, err := svc.Create(testCase.Context, givenApplicationID(), *modelInput, model.FormationTemplateWebhookReference)
   233  
   234  			// THEN
   235  
   236  			if testCase.ExpectedErr == nil {
   237  				assert.NotEmpty(t, result)
   238  				require.NoError(t, err)
   239  			} else {
   240  				assert.Contains(t, err.Error(), testCase.ExpectedErr.Error())
   241  			}
   242  
   243  			mock.AssertExpectationsForObjects(t, repo, uidSvc, tenantSvc)
   244  		})
   245  	}
   246  
   247  	t.Run("Returns error on loading tenant", func(t *testing.T) {
   248  		svc := webhook.NewService(nil, nil, nil, nil, nil, "")
   249  		// WHEN
   250  		_, err := svc.Create(context.TODO(), givenApplicationID(), *modelInput, model.ApplicationWebhookReference)
   251  		assert.True(t, apperrors.IsCannotReadTenant(err))
   252  	})
   253  }
   254  
   255  func TestService_Get(t *testing.T) {
   256  	// GIVEN
   257  	testErr := errors.New("Test error")
   258  
   259  	id := "foo"
   260  	url := "bar"
   261  
   262  	webhookModel := fixApplicationModelWebhook("1", id, givenTenant(), url, time.Time{})
   263  
   264  	ctx := context.TODO()
   265  	ctx = tenant.SaveToContext(ctx, givenTenant(), givenExternalTenant())
   266  
   267  	testCases := []struct {
   268  		Name               string
   269  		RepositoryFn       func() *automock.WebhookRepository
   270  		ExpectedWebhook    *model.Webhook
   271  		ExpectedErrMessage string
   272  	}{
   273  		{
   274  			Name: "Success",
   275  			RepositoryFn: func() *automock.WebhookRepository {
   276  				repo := &automock.WebhookRepository{}
   277  				repo.On("GetByID", ctx, givenTenant(), id, model.ApplicationWebhookReference).Return(webhookModel, nil).Once()
   278  				return repo
   279  			},
   280  			ExpectedWebhook:    webhookModel,
   281  			ExpectedErrMessage: "",
   282  		},
   283  		{
   284  			Name: "Returns error when webhook retrieval failed",
   285  			RepositoryFn: func() *automock.WebhookRepository {
   286  				repo := &automock.WebhookRepository{}
   287  				repo.On("GetByID", ctx, givenTenant(), id, model.ApplicationWebhookReference).Return(nil, testErr).Once()
   288  				return repo
   289  			},
   290  			ExpectedWebhook:    webhookModel,
   291  			ExpectedErrMessage: testErr.Error(),
   292  		},
   293  	}
   294  
   295  	for _, testCase := range testCases {
   296  		t.Run(testCase.Name, func(t *testing.T) {
   297  			repo := testCase.RepositoryFn()
   298  			svc := webhook.NewService(repo, nil, nil, nil, nil, "")
   299  
   300  			// WHEN
   301  			actual, err := svc.Get(ctx, id, model.ApplicationWebhookReference)
   302  
   303  			// THEN
   304  			if testCase.ExpectedErrMessage == "" {
   305  				require.NoError(t, err)
   306  				assert.Equal(t, testCase.ExpectedWebhook, actual)
   307  			} else {
   308  				assert.Contains(t, err.Error(), testCase.ExpectedErrMessage)
   309  			}
   310  
   311  			repo.AssertExpectations(t)
   312  		})
   313  	}
   314  }
   315  
   316  func TestService_ListForApplication(t *testing.T) {
   317  	// GIVEN
   318  	testErr := errors.New("Test error")
   319  
   320  	modelWebhooks := []*model.Webhook{
   321  		fixApplicationModelWebhook("1", "foo", givenTenant(), "Foo", time.Time{}),
   322  		fixApplicationModelWebhook("2", "bar", givenTenant(), "Bar", time.Time{}),
   323  	}
   324  	applicationID := "foo"
   325  
   326  	ctx := context.TODO()
   327  	ctx = tenant.SaveToContext(ctx, givenTenant(), givenExternalTenant())
   328  
   329  	testCases := []struct {
   330  		Name               string
   331  		RepositoryFn       func() *automock.WebhookRepository
   332  		ExpectedResult     []*model.Webhook
   333  		ExpectedErrMessage string
   334  	}{
   335  		{
   336  			Name: "Success",
   337  			RepositoryFn: func() *automock.WebhookRepository {
   338  				repo := &automock.WebhookRepository{}
   339  				repo.On("ListByReferenceObjectID", ctx, givenTenant(), applicationID, model.ApplicationWebhookReference).Return(modelWebhooks, nil).Once()
   340  				return repo
   341  			},
   342  			ExpectedResult:     modelWebhooks,
   343  			ExpectedErrMessage: "",
   344  		},
   345  		{
   346  			Name: "Returns error when webhook listing failed",
   347  			RepositoryFn: func() *automock.WebhookRepository {
   348  				repo := &automock.WebhookRepository{}
   349  				repo.On("ListByReferenceObjectID", ctx, givenTenant(), applicationID, model.ApplicationWebhookReference).Return(nil, testErr).Once()
   350  				return repo
   351  			},
   352  			ExpectedResult:     nil,
   353  			ExpectedErrMessage: testErr.Error(),
   354  		},
   355  	}
   356  
   357  	for _, testCase := range testCases {
   358  		t.Run(testCase.Name, func(t *testing.T) {
   359  			repo := testCase.RepositoryFn()
   360  			svc := webhook.NewService(repo, nil, nil, nil, nil, "")
   361  
   362  			// WHEN
   363  			webhooks, err := svc.ListForApplication(ctx, applicationID)
   364  
   365  			// THEN
   366  			if testCase.ExpectedErrMessage == "" {
   367  				require.NoError(t, err)
   368  				assert.Equal(t, testCase.ExpectedResult, webhooks)
   369  			} else {
   370  				assert.Contains(t, err.Error(), testCase.ExpectedErrMessage)
   371  			}
   372  
   373  			repo.AssertExpectations(t)
   374  		})
   375  	}
   376  
   377  	t.Run("Returns error on loading tenant", func(t *testing.T) {
   378  		svc := webhook.NewService(nil, nil, nil, nil, nil, "")
   379  		// WHEN
   380  		_, err := svc.ListForApplication(context.TODO(), givenApplicationID())
   381  		assert.True(t, apperrors.IsCannotReadTenant(err))
   382  	})
   383  }
   384  
   385  func TestService_ListForRuntime(t *testing.T) {
   386  	// GIVEN
   387  	testErr := errors.New("Test error")
   388  
   389  	modelWebhooks := []*model.Webhook{
   390  		fixRuntimeModelWebhook("1", "foo", "Foo"),
   391  		fixRuntimeModelWebhook("2", "bar", "Bar"),
   392  	}
   393  	runtimeID := "foo"
   394  
   395  	ctx := context.TODO()
   396  	ctx = tenant.SaveToContext(ctx, givenTenant(), givenExternalTenant())
   397  
   398  	testCases := []struct {
   399  		Name               string
   400  		RepositoryFn       func() *automock.WebhookRepository
   401  		ExpectedResult     []*model.Webhook
   402  		ExpectedErrMessage string
   403  	}{
   404  		{
   405  			Name: "Success",
   406  			RepositoryFn: func() *automock.WebhookRepository {
   407  				repo := &automock.WebhookRepository{}
   408  				repo.On("ListByReferenceObjectID", ctx, givenTenant(), runtimeID, model.RuntimeWebhookReference).Return(modelWebhooks, nil).Once()
   409  				return repo
   410  			},
   411  			ExpectedResult:     modelWebhooks,
   412  			ExpectedErrMessage: "",
   413  		},
   414  		{
   415  			Name: "Returns error when webhook listing failed",
   416  			RepositoryFn: func() *automock.WebhookRepository {
   417  				repo := &automock.WebhookRepository{}
   418  				repo.On("ListByReferenceObjectID", ctx, givenTenant(), runtimeID, model.RuntimeWebhookReference).Return(nil, testErr).Once()
   419  				return repo
   420  			},
   421  			ExpectedResult:     nil,
   422  			ExpectedErrMessage: testErr.Error(),
   423  		},
   424  	}
   425  
   426  	for _, testCase := range testCases {
   427  		t.Run(testCase.Name, func(t *testing.T) {
   428  			repo := testCase.RepositoryFn()
   429  			svc := webhook.NewService(repo, nil, nil, nil, nil, "")
   430  
   431  			// WHEN
   432  			webhooks, err := svc.ListForRuntime(ctx, runtimeID)
   433  
   434  			// THEN
   435  			if testCase.ExpectedErrMessage == "" {
   436  				require.NoError(t, err)
   437  				assert.Equal(t, testCase.ExpectedResult, webhooks)
   438  			} else {
   439  				assert.Contains(t, err.Error(), testCase.ExpectedErrMessage)
   440  			}
   441  
   442  			repo.AssertExpectations(t)
   443  		})
   444  	}
   445  
   446  	t.Run("Returns error on loading tenant", func(t *testing.T) {
   447  		svc := webhook.NewService(nil, nil, nil, nil, nil, "")
   448  		// WHEN
   449  		_, err := svc.ListForRuntime(context.TODO(), givenRuntimeID())
   450  		assert.True(t, apperrors.IsCannotReadTenant(err))
   451  	})
   452  }
   453  
   454  func TestService_ListForApplicationTemplate(t *testing.T) {
   455  	// GIVEN
   456  	testErr := errors.New("Test error")
   457  
   458  	modelWebhooks := []*model.Webhook{
   459  		fixApplicationTemplateModelWebhook("1", "foo", "Foo"),
   460  		fixApplicationTemplateModelWebhook("2", "bar", "Bar"),
   461  	}
   462  	applicationTemplateID := "foo"
   463  
   464  	ctx := context.TODO()
   465  
   466  	testCases := []struct {
   467  		Name               string
   468  		RepositoryFn       func() *automock.WebhookRepository
   469  		ExpectedResult     []*model.Webhook
   470  		ExpectedErrMessage string
   471  	}{
   472  		{
   473  			Name: "Success",
   474  			RepositoryFn: func() *automock.WebhookRepository {
   475  				repo := &automock.WebhookRepository{}
   476  				repo.On("ListByApplicationTemplateID", ctx, applicationTemplateID).Return(modelWebhooks, nil).Once()
   477  				return repo
   478  			},
   479  			ExpectedResult:     modelWebhooks,
   480  			ExpectedErrMessage: "",
   481  		},
   482  		{
   483  			Name: "Returns error when webhook listing failed",
   484  			RepositoryFn: func() *automock.WebhookRepository {
   485  				repo := &automock.WebhookRepository{}
   486  				repo.On("ListByApplicationTemplateID", ctx, applicationTemplateID).Return(nil, testErr).Once()
   487  				return repo
   488  			},
   489  			ExpectedResult:     nil,
   490  			ExpectedErrMessage: testErr.Error(),
   491  		},
   492  	}
   493  
   494  	for _, testCase := range testCases {
   495  		t.Run(testCase.Name, func(t *testing.T) {
   496  			repo := testCase.RepositoryFn()
   497  			svc := webhook.NewService(repo, nil, nil, nil, nil, "")
   498  
   499  			// WHEN
   500  			webhooks, err := svc.ListForApplicationTemplate(ctx, applicationTemplateID)
   501  
   502  			// THEN
   503  			if testCase.ExpectedErrMessage == "" {
   504  				require.NoError(t, err)
   505  				assert.Equal(t, testCase.ExpectedResult, webhooks)
   506  			} else {
   507  				assert.Contains(t, err.Error(), testCase.ExpectedErrMessage)
   508  			}
   509  
   510  			repo.AssertExpectations(t)
   511  		})
   512  	}
   513  }
   514  
   515  func TestService_ListForFormationTemplate(t *testing.T) {
   516  	// GIVEN
   517  	testErr := errors.New("Test error")
   518  
   519  	modelWebhooks := []*model.Webhook{
   520  		fixFormationTemplateModelWebhook("1", "foo", "Foo"),
   521  		fixFormationTemplateModelWebhook("2", "bar", "Bar"),
   522  	}
   523  	formationTemplateID := "foo"
   524  
   525  	ctx := context.TODO()
   526  	ctx = tenant.SaveToContext(ctx, givenTenant(), givenExternalTenant())
   527  
   528  	testCases := []struct {
   529  		Name               string
   530  		RepositoryFn       func() *automock.WebhookRepository
   531  		ExpectedResult     []*model.Webhook
   532  		Tenant             string
   533  		ExpectedErrMessage string
   534  	}{
   535  		{
   536  			Name: "Success",
   537  			RepositoryFn: func() *automock.WebhookRepository {
   538  				repo := &automock.WebhookRepository{}
   539  				repo.On("ListByReferenceObjectID", ctx, givenTenant(), formationTemplateID, model.FormationTemplateWebhookReference).Return(modelWebhooks, nil).Once()
   540  				return repo
   541  			},
   542  			Tenant:             givenTenant(),
   543  			ExpectedResult:     modelWebhooks,
   544  			ExpectedErrMessage: "",
   545  		},
   546  		{
   547  			Name:   "Returns error when webhook listing failed",
   548  			Tenant: givenTenant(),
   549  			RepositoryFn: func() *automock.WebhookRepository {
   550  				repo := &automock.WebhookRepository{}
   551  				repo.On("ListByReferenceObjectID", ctx, givenTenant(), formationTemplateID, model.FormationTemplateWebhookReference).Return(nil, testErr).Once()
   552  				return repo
   553  			},
   554  			ExpectedResult:     nil,
   555  			ExpectedErrMessage: testErr.Error(),
   556  		},
   557  		{
   558  			Name: "Success global",
   559  			RepositoryFn: func() *automock.WebhookRepository {
   560  				repo := &automock.WebhookRepository{}
   561  				repo.On("ListByReferenceObjectIDGlobal", ctx, formationTemplateID, model.FormationTemplateWebhookReference).Return(modelWebhooks, nil).Once()
   562  				return repo
   563  			},
   564  			ExpectedResult:     modelWebhooks,
   565  			ExpectedErrMessage: "",
   566  		},
   567  		{
   568  			Name: "Returns error when webhook listing failed",
   569  			RepositoryFn: func() *automock.WebhookRepository {
   570  				repo := &automock.WebhookRepository{}
   571  				repo.On("ListByReferenceObjectIDGlobal", ctx, formationTemplateID, model.FormationTemplateWebhookReference).Return(nil, testErr).Once()
   572  				return repo
   573  			},
   574  			ExpectedResult:     nil,
   575  			ExpectedErrMessage: testErr.Error(),
   576  		},
   577  	}
   578  
   579  	for _, testCase := range testCases {
   580  		t.Run(testCase.Name, func(t *testing.T) {
   581  			repo := testCase.RepositoryFn()
   582  			svc := webhook.NewService(repo, nil, nil, nil, nil, "")
   583  
   584  			// WHEN
   585  			webhooks, err := svc.ListForFormationTemplate(ctx, testCase.Tenant, formationTemplateID)
   586  
   587  			// THEN
   588  			if testCase.ExpectedErrMessage == "" {
   589  				require.NoError(t, err)
   590  				assert.Equal(t, testCase.ExpectedResult, webhooks)
   591  			} else {
   592  				assert.Contains(t, err.Error(), testCase.ExpectedErrMessage)
   593  			}
   594  
   595  			repo.AssertExpectations(t)
   596  		})
   597  	}
   598  }
   599  
   600  func TestService_ListAllApplicationWebhooks(t *testing.T) {
   601  	// GIVEN
   602  	testErr := errors.New("Test error")
   603  
   604  	application := &model.Application{
   605  		Name: "app-1",
   606  		BaseEntity: &model.BaseEntity{
   607  			ID: "app-1-id",
   608  		},
   609  	}
   610  	applicationFromTemplate := &model.Application{
   611  		Name:                  "app-1",
   612  		ApplicationTemplateID: str.Ptr("app-template-id-1"),
   613  		BaseEntity: &model.BaseEntity{
   614  			ID: "app-1-id",
   615  		},
   616  	}
   617  
   618  	appModelWebhooks := []*model.Webhook{
   619  		fixApplicationModelWebhookWithType("app-webhook-1", "app-1", givenTenant(), "test-url-1.com", model.WebhookTypeRegisterApplication, time.Time{}),
   620  		fixApplicationModelWebhookWithType("app-webhook-2", "app-1", givenTenant(), "test-url-2.com", model.WebhookTypeDeleteApplication, time.Time{}),
   621  	}
   622  	appTemplateModelWebhooks := []*model.Webhook{
   623  		fixApplicationTemplateModelWebhookWithType("app-template-webhook-1", "app-template-1", "test-url-1.com", model.WebhookTypeRegisterApplication),
   624  		fixApplicationTemplateModelWebhookWithType("app-template-webhook-2", "app-template-2", "test-url-2.com", model.WebhookTypeDeleteApplication),
   625  	}
   626  	mergedWebhooks := []*model.Webhook{
   627  		appTemplateModelWebhooks[1],
   628  		appModelWebhooks[0],
   629  	}
   630  
   631  	ctx := context.TODO()
   632  	ctx = tenant.SaveToContext(ctx, givenTenant(), givenExternalTenant())
   633  
   634  	testCases := []struct {
   635  		Name                    string
   636  		WebhookRepositoryFn     func() *automock.WebhookRepository
   637  		ApplicationRepositoryFn func() *automock.ApplicationRepository
   638  		ExpectedResult          []*model.Webhook
   639  		ExpectedErrMessage      string
   640  	}{
   641  		{
   642  			Name: "Success when only application has webhooks",
   643  			ApplicationRepositoryFn: func() *automock.ApplicationRepository {
   644  				appRepo := &automock.ApplicationRepository{}
   645  				appRepo.On("GetGlobalByID", ctx, application.ID).Return(application, nil).Once()
   646  				return appRepo
   647  			},
   648  			WebhookRepositoryFn: func() *automock.WebhookRepository {
   649  				webhookRepo := &automock.WebhookRepository{}
   650  				webhookRepo.On("ListByReferenceObjectID", ctx, givenTenant(), application.ID, model.ApplicationWebhookReference).Return(appModelWebhooks, nil).Once()
   651  				return webhookRepo
   652  			},
   653  			ExpectedResult:     appModelWebhooks,
   654  			ExpectedErrMessage: "",
   655  		},
   656  		{
   657  			Name: "Success when only application template has webhooks",
   658  			ApplicationRepositoryFn: func() *automock.ApplicationRepository {
   659  				appRepo := &automock.ApplicationRepository{}
   660  				appRepo.On("GetGlobalByID", ctx, applicationFromTemplate.ID).Return(applicationFromTemplate, nil).Once()
   661  				return appRepo
   662  			},
   663  			WebhookRepositoryFn: func() *automock.WebhookRepository {
   664  				webhookRepo := &automock.WebhookRepository{}
   665  				webhookRepo.On("ListByReferenceObjectID", ctx, givenTenant(), applicationFromTemplate.ID, model.ApplicationWebhookReference).Return(nil, nil).Once()
   666  				webhookRepo.On("ListByApplicationTemplateID", ctx, *applicationFromTemplate.ApplicationTemplateID).Return(appTemplateModelWebhooks, nil).Once()
   667  				return webhookRepo
   668  			},
   669  			ExpectedResult:     appTemplateModelWebhooks,
   670  			ExpectedErrMessage: "",
   671  		},
   672  		{
   673  			Name: "Success when application template webhooks have to be overwritten",
   674  			ApplicationRepositoryFn: func() *automock.ApplicationRepository {
   675  				appRepo := &automock.ApplicationRepository{}
   676  				appRepo.On("GetGlobalByID", ctx, applicationFromTemplate.ID).Return(applicationFromTemplate, nil).Once()
   677  				return appRepo
   678  			},
   679  			WebhookRepositoryFn: func() *automock.WebhookRepository {
   680  				webhookRepo := &automock.WebhookRepository{}
   681  				webhookRepo.On("ListByReferenceObjectID", ctx, givenTenant(), applicationFromTemplate.ID, model.ApplicationWebhookReference).Return(appModelWebhooks, nil).Once()
   682  				webhookRepo.On("ListByApplicationTemplateID", ctx, *applicationFromTemplate.ApplicationTemplateID).Return(appTemplateModelWebhooks, nil).Once()
   683  				return webhookRepo
   684  			},
   685  			ExpectedResult:     appModelWebhooks,
   686  			ExpectedErrMessage: "",
   687  		},
   688  		{
   689  			Name: "Success when webhooks have to be merged from both app and template",
   690  			ApplicationRepositoryFn: func() *automock.ApplicationRepository {
   691  				appRepo := &automock.ApplicationRepository{}
   692  				appRepo.On("GetGlobalByID", ctx, applicationFromTemplate.ID).Return(applicationFromTemplate, nil).Once()
   693  				return appRepo
   694  			},
   695  			WebhookRepositoryFn: func() *automock.WebhookRepository {
   696  				webhookRepo := &automock.WebhookRepository{}
   697  				webhookRepo.On("ListByReferenceObjectID", ctx, givenTenant(), applicationFromTemplate.ID, model.ApplicationWebhookReference).Return([]*model.Webhook{appModelWebhooks[0]}, nil).Once()
   698  				webhookRepo.On("ListByApplicationTemplateID", ctx, *applicationFromTemplate.ApplicationTemplateID).Return([]*model.Webhook{appTemplateModelWebhooks[1]}, nil).Once()
   699  				return webhookRepo
   700  			},
   701  			ExpectedResult:     mergedWebhooks,
   702  			ExpectedErrMessage: "",
   703  		},
   704  		{
   705  			Name: "Returns error when webhook listing for application failed",
   706  			ApplicationRepositoryFn: func() *automock.ApplicationRepository {
   707  				appRepo := &automock.ApplicationRepository{}
   708  				appRepo.On("GetGlobalByID", ctx, application.ID).Return(application, nil).Once()
   709  				return appRepo
   710  			},
   711  			WebhookRepositoryFn: func() *automock.WebhookRepository {
   712  				repo := &automock.WebhookRepository{}
   713  				repo.On("ListByReferenceObjectID", ctx, givenTenant(), application.ID, model.ApplicationWebhookReference).Return(nil, testErr).Once()
   714  				return repo
   715  			},
   716  			ExpectedResult:     nil,
   717  			ExpectedErrMessage: testErr.Error(),
   718  		},
   719  		{
   720  			Name: "Returns error when webhook listing for application template failed",
   721  			ApplicationRepositoryFn: func() *automock.ApplicationRepository {
   722  				appRepo := &automock.ApplicationRepository{}
   723  				appRepo.On("GetGlobalByID", ctx, applicationFromTemplate.ID).Return(applicationFromTemplate, nil).Once()
   724  				return appRepo
   725  			},
   726  			WebhookRepositoryFn: func() *automock.WebhookRepository {
   727  				webhookRepo := &automock.WebhookRepository{}
   728  				webhookRepo.On("ListByReferenceObjectID", ctx, givenTenant(), applicationFromTemplate.ID, model.ApplicationWebhookReference).Return(appModelWebhooks, nil).Once()
   729  				webhookRepo.On("ListByApplicationTemplateID", ctx, *applicationFromTemplate.ApplicationTemplateID).Return(nil, testErr).Once()
   730  
   731  				return webhookRepo
   732  			},
   733  			ExpectedResult:     nil,
   734  			ExpectedErrMessage: testErr.Error(),
   735  		},
   736  		{
   737  			Name: "Returns error when application repository returns an error",
   738  			ApplicationRepositoryFn: func() *automock.ApplicationRepository {
   739  				appRepo := &automock.ApplicationRepository{}
   740  				appRepo.On("GetGlobalByID", ctx, applicationFromTemplate.ID).Return(nil, testErr).Once()
   741  				return appRepo
   742  			},
   743  			WebhookRepositoryFn: func() *automock.WebhookRepository {
   744  				return &automock.WebhookRepository{}
   745  			},
   746  			ExpectedResult:     nil,
   747  			ExpectedErrMessage: testErr.Error(),
   748  		},
   749  	}
   750  
   751  	for _, testCase := range testCases {
   752  		t.Run(testCase.Name, func(t *testing.T) {
   753  			webhookRepo := testCase.WebhookRepositoryFn()
   754  			applicationRepo := testCase.ApplicationRepositoryFn()
   755  			svc := webhook.NewService(webhookRepo, applicationRepo, nil, nil, nil, "")
   756  
   757  			// WHEN
   758  			webhooks, err := svc.ListAllApplicationWebhooks(ctx, application.ID)
   759  
   760  			// THEN
   761  			if testCase.ExpectedErrMessage == "" {
   762  				require.NoError(t, err)
   763  				for _, expected := range testCase.ExpectedResult {
   764  					assert.Contains(t, webhooks, expected)
   765  				}
   766  			} else {
   767  				assert.Contains(t, err.Error(), testCase.ExpectedErrMessage)
   768  			}
   769  
   770  			webhookRepo.AssertExpectations(t)
   771  		})
   772  	}
   773  
   774  	t.Run("Returns error on loading tenant", func(t *testing.T) {
   775  		svc := webhook.NewService(nil, nil, nil, nil, nil, "")
   776  		// WHEN
   777  		_, err := svc.ListForApplication(context.TODO(), givenApplicationID())
   778  		assert.True(t, apperrors.IsCannotReadTenant(err))
   779  	})
   780  }
   781  
   782  func TestService_Update(t *testing.T) {
   783  	// GIVEN
   784  	testErr := errors.New("Test error")
   785  
   786  	url := "foo"
   787  	id := "bar"
   788  	modelInput := fixModelWebhookInput(url)
   789  
   790  	inputWebhookModel := mock.MatchedBy(func(webhook *model.Webhook) bool {
   791  		return webhook.URL == modelInput.URL
   792  	})
   793  
   794  	applicationWebhookModel := fixApplicationModelWebhook("1", id, givenTenant(), url, time.Time{})
   795  	applicationTemplateWebhookModel := fixApplicationTemplateModelWebhook("1", id, url)
   796  	noIDWebhookModel := &model.Webhook{}
   797  	*noIDWebhookModel = *applicationWebhookModel
   798  	noIDWebhookModel.ObjectID = ""
   799  
   800  	tenantCtx := context.TODO()
   801  	tenantCtx = tenant.SaveToContext(tenantCtx, givenTenant(), givenExternalTenant())
   802  
   803  	testCases := []struct {
   804  		Name                string
   805  		WebhookRepositoryFn func() *automock.WebhookRepository
   806  		ExpectedErrMessage  string
   807  		WebhookType         model.WebhookReferenceObjectType
   808  		Context             context.Context
   809  	}{
   810  		{
   811  			Name: "Success when applicationID is present",
   812  			WebhookRepositoryFn: func() *automock.WebhookRepository {
   813  				repo := &automock.WebhookRepository{}
   814  				repo.On("GetByID", tenantCtx, givenTenant(), id, model.ApplicationWebhookReference).Return(applicationWebhookModel, nil).Once()
   815  				repo.On("Update", tenantCtx, givenTenant(), inputWebhookModel).Return(nil).Once()
   816  				return repo
   817  			},
   818  			ExpectedErrMessage: "",
   819  			WebhookType:        model.ApplicationWebhookReference,
   820  			Context:            tenantCtx,
   821  		},
   822  		{
   823  			Name: "Success when applicationTemplateID is present",
   824  			WebhookRepositoryFn: func() *automock.WebhookRepository {
   825  				repo := &automock.WebhookRepository{}
   826  				ctx := context.TODO()
   827  				repo.On("GetByIDGlobal", ctx, id).Return(applicationTemplateWebhookModel, nil).Once()
   828  				repo.On("Update", ctx, "", inputWebhookModel).Return(nil).Once()
   829  				return repo
   830  			},
   831  			ExpectedErrMessage: "",
   832  			WebhookType:        model.ApplicationTemplateWebhookReference,
   833  			Context:            context.TODO(),
   834  		},
   835  		{
   836  			Name: "Returns error when webhook update failed",
   837  			WebhookRepositoryFn: func() *automock.WebhookRepository {
   838  				repo := &automock.WebhookRepository{}
   839  				repo.On("GetByID", tenantCtx, givenTenant(), id, model.ApplicationWebhookReference).Return(applicationWebhookModel, nil).Once()
   840  				repo.On("Update", tenantCtx, givenTenant(), inputWebhookModel).Return(testErr).Once()
   841  				return repo
   842  			},
   843  			ExpectedErrMessage: testErr.Error(),
   844  			WebhookType:        model.ApplicationWebhookReference,
   845  			Context:            tenantCtx,
   846  		},
   847  		{
   848  			Name: "Returns error when webhook retrieval failed",
   849  			WebhookRepositoryFn: func() *automock.WebhookRepository {
   850  				repo := &automock.WebhookRepository{}
   851  				repo.On("GetByID", tenantCtx, givenTenant(), id, model.ApplicationWebhookReference).Return(nil, testErr).Once()
   852  				return repo
   853  			},
   854  			ExpectedErrMessage: testErr.Error(),
   855  			WebhookType:        model.ApplicationWebhookReference,
   856  			Context:            tenantCtx,
   857  		},
   858  		{
   859  			Name: "Returns error application doesn't have any ids",
   860  			WebhookRepositoryFn: func() *automock.WebhookRepository {
   861  				repo := &automock.WebhookRepository{}
   862  				repo.On("GetByID", tenantCtx, givenTenant(), id, model.ApplicationWebhookReference).Return(noIDWebhookModel, nil).Once()
   863  				return repo
   864  			},
   865  			ExpectedErrMessage: "webhook doesn't have neither of application_id, application_template_id, runtime_id and formation_template_id",
   866  			WebhookType:        model.ApplicationWebhookReference,
   867  			Context:            tenantCtx,
   868  		},
   869  	}
   870  
   871  	for _, testCase := range testCases {
   872  		t.Run(testCase.Name, func(t *testing.T) {
   873  			repo := testCase.WebhookRepositoryFn()
   874  			svc := webhook.NewService(repo, nil, nil, nil, nil, "")
   875  
   876  			// WHEN
   877  			err := svc.Update(testCase.Context, id, *modelInput, testCase.WebhookType)
   878  
   879  			// THEN
   880  			if testCase.ExpectedErrMessage == "" {
   881  				require.NoError(t, err)
   882  			} else {
   883  				assert.Contains(t, err.Error(), testCase.ExpectedErrMessage)
   884  			}
   885  
   886  			repo.AssertExpectations(t)
   887  		})
   888  	}
   889  	t.Run("Returns error on loading tenant", func(t *testing.T) {
   890  		svc := webhook.NewService(nil, nil, nil, nil, nil, "")
   891  		// WHEN
   892  		err := svc.Update(context.TODO(), givenApplicationID(), model.WebhookInput{}, model.ApplicationWebhookReference)
   893  		assert.True(t, apperrors.IsCannotReadTenant(err))
   894  	})
   895  }
   896  
   897  func TestService_Delete(t *testing.T) {
   898  	// GIVEN
   899  	testErr := errors.New("Test error")
   900  
   901  	id := "foo"
   902  	url := "bar"
   903  
   904  	webhookModel := fixApplicationModelWebhook("1", id, givenTenant(), url, time.Time{})
   905  
   906  	ctx := context.TODO()
   907  	ctx = tenant.SaveToContext(ctx, givenTenant(), givenExternalTenant())
   908  
   909  	testCases := []struct {
   910  		Name               string
   911  		RepositoryFn       func() *automock.WebhookRepository
   912  		ExpectedErrMessage string
   913  	}{
   914  		{
   915  			Name: "Success",
   916  			RepositoryFn: func() *automock.WebhookRepository {
   917  				repo := &automock.WebhookRepository{}
   918  				repo.On("GetByID", ctx, givenTenant(), id, model.ApplicationWebhookReference).Return(webhookModel, nil).Once()
   919  				repo.On("Delete", ctx, webhookModel.ID).Return(nil).Once()
   920  				return repo
   921  			},
   922  			ExpectedErrMessage: "",
   923  		},
   924  		{
   925  			Name: "Returns error when webhook deletion failed",
   926  			RepositoryFn: func() *automock.WebhookRepository {
   927  				repo := &automock.WebhookRepository{}
   928  				repo.On("GetByID", ctx, givenTenant(), id, model.ApplicationWebhookReference).Return(webhookModel, nil).Once()
   929  				repo.On("Delete", ctx, webhookModel.ID).Return(testErr).Once()
   930  				return repo
   931  			},
   932  			ExpectedErrMessage: testErr.Error(),
   933  		},
   934  		{
   935  			Name: "Returns error when webhook retrieval failed",
   936  			RepositoryFn: func() *automock.WebhookRepository {
   937  				repo := &automock.WebhookRepository{}
   938  				repo.On("GetByID", ctx, givenTenant(), id, model.ApplicationWebhookReference).Return(nil, testErr).Once()
   939  				return repo
   940  			},
   941  			ExpectedErrMessage: testErr.Error(),
   942  		},
   943  	}
   944  
   945  	for _, testCase := range testCases {
   946  		t.Run(testCase.Name, func(t *testing.T) {
   947  			repo := testCase.RepositoryFn()
   948  			svc := webhook.NewService(repo, nil, nil, nil, nil, "")
   949  
   950  			// WHEN
   951  			err := svc.Delete(ctx, id, model.ApplicationWebhookReference)
   952  
   953  			// THEN
   954  			if testCase.ExpectedErrMessage == "" {
   955  				require.NoError(t, err)
   956  			} else {
   957  				assert.Contains(t, err.Error(), testCase.ExpectedErrMessage)
   958  			}
   959  
   960  			repo.AssertExpectations(t)
   961  		})
   962  	}
   963  }
   964  
   965  func TestService_EnrichWebhooksWithTenantMappingWebhooks(t *testing.T) {
   966  	// GIVEN
   967  	callbackURL := "http://callback.url"
   968  
   969  	webhookWithoutURL := fixTenantMappedWebhooks()
   970  	webhookWithoutURL[0].URL = nil
   971  
   972  	webhookWithoutMode := fixTenantMappedWebhooks()
   973  	webhookWithoutMode[0].Mode = nil
   974  
   975  	testCases := []struct {
   976  		Name                  string
   977  		TenantMappingConfigFn func() map[string]interface{}
   978  		Input                 []*graphql.WebhookInput
   979  		Output                []*graphql.WebhookInput
   980  		ExpectedErrMessage    string
   981  	}{
   982  		{
   983  			Name:                  "Success",
   984  			TenantMappingConfigFn: fixTenantMappingConfig,
   985  			Input:                 fixTenantMappedWebhooks(),
   986  			Output:                fixEnrichedTenantMappedWebhooks(),
   987  		},
   988  		{
   989  			Name:                  "Success for ASYNC_CALLBACK mode",
   990  			TenantMappingConfigFn: fixTenantMappingConfigForAsyncCallback,
   991  			Input:                 fixTenantMappedWebhooksForAsyncCallbackMode(),
   992  			Output:                fixEnrichedTenantMappedWebhooksForAsyncCallbackMode(callbackURL),
   993  		},
   994  		{
   995  			Name:                  "Should not enrich webhooks if they do not have version",
   996  			TenantMappingConfigFn: fixTenantMappingConfig,
   997  			Input:                 []*graphql.WebhookInput{fixGQLWebhookInput(testURL)},
   998  			Output:                []*graphql.WebhookInput{fixGQLWebhookInput(testURL)},
   999  		},
  1000  		{
  1001  			Name:                  "Should throw error when tenant mapping webhook does not have a URL",
  1002  			TenantMappingConfigFn: fixTenantMappingConfig,
  1003  			Input:                 webhookWithoutURL,
  1004  			ExpectedErrMessage:    "url and mode are required fields when version is provided",
  1005  		},
  1006  		{
  1007  			Name:                  "Should throw error when tenant mapping webhook does not have a Mode",
  1008  			TenantMappingConfigFn: fixTenantMappingConfig,
  1009  			Input:                 webhookWithoutMode,
  1010  			ExpectedErrMessage:    "url and mode are required fields when version is provided",
  1011  		},
  1012  		{
  1013  			Name:                  "Should throw error when tenant mapping webhook version and mode does not match the tenant mapping config",
  1014  			TenantMappingConfigFn: fixEmptyTenantMappingConfig,
  1015  			Input:                 fixTenantMappedWebhooks(),
  1016  			ExpectedErrMessage:    "missing tenant mapping configuration for mode SYNC",
  1017  		},
  1018  		{
  1019  			Name:                  "Should throw error when tenant mapping configuration is not a map",
  1020  			TenantMappingConfigFn: fixInvalidTenantMappingConfig,
  1021  			Input:                 fixTenantMappedWebhooks(),
  1022  			ExpectedErrMessage:    "unexpected mode type, should be a map, but was string",
  1023  		},
  1024  		{
  1025  			Name:                  "Should throw error when tenant mapping configuration is not a map",
  1026  			TenantMappingConfigFn: fixInvalidTenantMappingConfig,
  1027  			Input:                 fixTenantMappedWebhooks(),
  1028  			ExpectedErrMessage:    "unexpected mode type, should be a map, but was string",
  1029  		},
  1030  		{
  1031  			Name:                  "Should throw error when tenant mapping webhook has a version that is not supported",
  1032  			TenantMappingConfigFn: fixTenantMappingConfig,
  1033  			Input:                 fixTenantMappedWebhooksWithInvalidVersion(),
  1034  			ExpectedErrMessage:    "missing tenant mapping configuration for mode SYNC and version notfound",
  1035  		},
  1036  	}
  1037  
  1038  	for _, testCase := range testCases {
  1039  		t.Run(testCase.Name, func(t *testing.T) {
  1040  			tenantMappingConfig := testCase.TenantMappingConfigFn()
  1041  			svc := webhook.NewService(nil, nil, nil, nil, tenantMappingConfig, callbackURL)
  1042  
  1043  			// WHEN
  1044  			result, err := svc.EnrichWebhooksWithTenantMappingWebhooks(testCase.Input)
  1045  
  1046  			// THEN
  1047  			if testCase.ExpectedErrMessage == "" {
  1048  				require.NoError(t, err)
  1049  				require.Equal(t, testCase.Output, result)
  1050  			} else {
  1051  				assert.Contains(t, err.Error(), testCase.ExpectedErrMessage)
  1052  			}
  1053  		})
  1054  	}
  1055  }
  1056  
  1057  func GetTenantMappingConfig(config string) map[string]interface{} {
  1058  	var tenantMappingConfig map[string]interface{}
  1059  	if err := json.Unmarshal([]byte(config), &tenantMappingConfig); err != nil {
  1060  		return nil
  1061  	}
  1062  	return tenantMappingConfig
  1063  }