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

     1  package formationtemplate_test
     2  
     3  import (
     4  	"context"
     5  	"testing"
     6  
     7  	"github.com/kyma-incubator/compass/components/director/pkg/apperrors"
     8  	"github.com/kyma-incubator/compass/components/director/pkg/resource"
     9  
    10  	"github.com/kyma-incubator/compass/components/director/internal/domain/formationtemplate"
    11  	"github.com/kyma-incubator/compass/components/director/internal/domain/formationtemplate/automock"
    12  	tnt "github.com/kyma-incubator/compass/components/director/internal/domain/tenant"
    13  	"github.com/kyma-incubator/compass/components/director/internal/model"
    14  	"github.com/pkg/errors"
    15  	"github.com/stretchr/testify/assert"
    16  	"github.com/stretchr/testify/mock"
    17  	"github.com/stretchr/testify/require"
    18  )
    19  
    20  func TestService_Create(t *testing.T) {
    21  	// GIVEN
    22  	ctx := tnt.SaveToContext(context.TODO(), testTenantID, testTenantID)
    23  	ctxWithEmptyTenants := tnt.SaveToContext(context.TODO(), "", "")
    24  
    25  	uidSvcFn := func() *automock.UIDService {
    26  		uidSvc := &automock.UIDService{}
    27  		uidSvc.On("Generate").Return(testID)
    28  		return uidSvc
    29  	}
    30  
    31  	testCases := []struct {
    32  		Name                        string
    33  		Context                     context.Context
    34  		Input                       *model.FormationTemplateInput
    35  		FormationTemplateRepository func() *automock.FormationTemplateRepository
    36  		FormationTemplateConverter  func() *automock.FormationTemplateConverter
    37  		TenantSvc                   func() *automock.TenantService
    38  		WebhookRepo                 func() *automock.WebhookRepository
    39  		ExpectedOutput              string
    40  		ExpectedError               error
    41  	}{
    42  		{
    43  			Name:    "Success",
    44  			Context: ctx,
    45  			Input:   &formationTemplateModelInput,
    46  			FormationTemplateConverter: func() *automock.FormationTemplateConverter {
    47  				converter := &automock.FormationTemplateConverter{}
    48  				converter.On("FromModelInputToModel", &formationTemplateModelInput, testID, testTenantID).Return(&formationTemplateModel).Once()
    49  				return converter
    50  			},
    51  			FormationTemplateRepository: func() *automock.FormationTemplateRepository {
    52  				repo := &automock.FormationTemplateRepository{}
    53  				repo.On("Create", ctx, &formationTemplateModel).Return(nil).Once()
    54  				return repo
    55  			},
    56  			TenantSvc: func() *automock.TenantService {
    57  				svc := &automock.TenantService{}
    58  				svc.On("ExtractTenantIDForTenantScopedFormationTemplates", ctx).Return(testTenantID, nil).Once()
    59  				return svc
    60  			},
    61  			WebhookRepo: func() *automock.WebhookRepository {
    62  				repo := &automock.WebhookRepository{}
    63  				repo.On("CreateMany", ctx, testTenantID, formationTemplateModel.Webhooks).Return(nil)
    64  				return repo
    65  			},
    66  			ExpectedOutput: testID,
    67  			ExpectedError:  nil,
    68  		},
    69  		{
    70  			Name:    "Success when tenant in ctx is empty",
    71  			Context: ctxWithEmptyTenants,
    72  			Input:   &formationTemplateModelInput,
    73  			FormationTemplateConverter: func() *automock.FormationTemplateConverter {
    74  				converter := &automock.FormationTemplateConverter{}
    75  				converter.On("FromModelInputToModel", &formationTemplateModelInput, testID, "").Return(&formationTemplateModelNullTenant).Once()
    76  				return converter
    77  			},
    78  			FormationTemplateRepository: func() *automock.FormationTemplateRepository {
    79  				repo := &automock.FormationTemplateRepository{}
    80  				repo.On("Create", ctxWithEmptyTenants, &formationTemplateModelNullTenant).Return(nil).Once()
    81  				return repo
    82  			},
    83  			WebhookRepo: func() *automock.WebhookRepository {
    84  				repo := &automock.WebhookRepository{}
    85  				repo.On("CreateMany", ctxWithEmptyTenants, "", formationTemplateModelNullTenant.Webhooks).Return(nil)
    86  				return repo
    87  			},
    88  			TenantSvc: func() *automock.TenantService {
    89  				svc := &automock.TenantService{}
    90  				svc.On("ExtractTenantIDForTenantScopedFormationTemplates", ctxWithEmptyTenants).Return("", nil).Once()
    91  				return svc
    92  			},
    93  			ExpectedOutput: testID,
    94  			ExpectedError:  nil,
    95  		},
    96  		{
    97  			Name:    "Success for application only template",
    98  			Context: ctx,
    99  			Input:   &formationTemplateModelInputAppOnly,
   100  			FormationTemplateConverter: func() *automock.FormationTemplateConverter {
   101  				converter := &automock.FormationTemplateConverter{}
   102  				converter.On("FromModelInputToModel", &formationTemplateModelInputAppOnly, testID, testTenantID).Return(&formationTemplateModelAppOnly).Once()
   103  				return converter
   104  			},
   105  			FormationTemplateRepository: func() *automock.FormationTemplateRepository {
   106  				repo := &automock.FormationTemplateRepository{}
   107  				repo.On("Create", ctx, &formationTemplateModelAppOnly).Return(nil).Once()
   108  				return repo
   109  			},
   110  			TenantSvc: func() *automock.TenantService {
   111  				svc := &automock.TenantService{}
   112  				svc.On("ExtractTenantIDForTenantScopedFormationTemplates", ctx).Return(testTenantID, nil).Once()
   113  				return svc
   114  			},
   115  			WebhookRepo: func() *automock.WebhookRepository {
   116  				repo := &automock.WebhookRepository{}
   117  				repo.On("CreateMany", ctx, testTenantID, formationTemplateModelAppOnly.Webhooks).Return(nil)
   118  				return repo
   119  			},
   120  			ExpectedOutput: testID,
   121  			ExpectedError:  nil,
   122  		},
   123  		{
   124  			Name:    "Error when getting tenant object",
   125  			Context: ctx,
   126  			Input:   &formationTemplateModelInput,
   127  			FormationTemplateConverter: func() *automock.FormationTemplateConverter {
   128  				return &automock.FormationTemplateConverter{}
   129  			},
   130  			FormationTemplateRepository: func() *automock.FormationTemplateRepository {
   131  				return &automock.FormationTemplateRepository{}
   132  			},
   133  			TenantSvc: func() *automock.TenantService {
   134  				svc := &automock.TenantService{}
   135  				svc.On("ExtractTenantIDForTenantScopedFormationTemplates", ctx).Return("", testErr)
   136  				return svc
   137  			},
   138  			WebhookRepo: func() *automock.WebhookRepository {
   139  				return &automock.WebhookRepository{}
   140  			},
   141  			ExpectedOutput: "",
   142  			ExpectedError:  testErr,
   143  		},
   144  		{
   145  			Name:    "Error when creating formation template",
   146  			Context: ctx,
   147  			Input:   &formationTemplateModelInput,
   148  			FormationTemplateConverter: func() *automock.FormationTemplateConverter {
   149  				converter := &automock.FormationTemplateConverter{}
   150  				converter.On("FromModelInputToModel", &formationTemplateModelInput, testID, testTenantID).Return(&formationTemplateModel).Once()
   151  				return converter
   152  			},
   153  			FormationTemplateRepository: func() *automock.FormationTemplateRepository {
   154  				repo := &automock.FormationTemplateRepository{}
   155  				repo.On("Create", ctx, &formationTemplateModel).Return(testErr).Once()
   156  				return repo
   157  			},
   158  			TenantSvc: func() *automock.TenantService {
   159  				svc := &automock.TenantService{}
   160  				svc.On("ExtractTenantIDForTenantScopedFormationTemplates", ctx).Return(testTenantID, nil).Once()
   161  				return svc
   162  			},
   163  			WebhookRepo: func() *automock.WebhookRepository {
   164  				return &automock.WebhookRepository{}
   165  			},
   166  			ExpectedOutput: "",
   167  			ExpectedError:  testErr,
   168  		},
   169  		{
   170  			Name:    "Error when creating webhooks",
   171  			Context: ctx,
   172  			Input:   &formationTemplateModelInput,
   173  			FormationTemplateConverter: func() *automock.FormationTemplateConverter {
   174  				converter := &automock.FormationTemplateConverter{}
   175  				converter.On("FromModelInputToModel", &formationTemplateModelInput, testID, testTenantID).Return(&formationTemplateModel).Once()
   176  				return converter
   177  			},
   178  			FormationTemplateRepository: func() *automock.FormationTemplateRepository {
   179  				repo := &automock.FormationTemplateRepository{}
   180  				repo.On("Create", ctx, &formationTemplateModel).Return(nil).Once()
   181  				return repo
   182  			},
   183  			TenantSvc: func() *automock.TenantService {
   184  				svc := &automock.TenantService{}
   185  				svc.On("ExtractTenantIDForTenantScopedFormationTemplates", ctx).Return(testTenantID, nil).Once()
   186  				return svc
   187  			},
   188  			WebhookRepo: func() *automock.WebhookRepository {
   189  				repo := &automock.WebhookRepository{}
   190  				repo.On("CreateMany", ctx, testTenantID, formationTemplateModel.Webhooks).Return(testErr)
   191  				return repo
   192  			},
   193  			ExpectedOutput: "",
   194  			ExpectedError:  errors.New("while creating Webhooks for Formation Template"),
   195  		},
   196  	}
   197  
   198  	for _, testCase := range testCases {
   199  		t.Run(testCase.Name, func(t *testing.T) {
   200  			formationTemplateRepo := testCase.FormationTemplateRepository()
   201  			formationTemplateConv := testCase.FormationTemplateConverter()
   202  			tenantSvc := testCase.TenantSvc()
   203  			whRepo := testCase.WebhookRepo()
   204  			idSvc := uidSvcFn()
   205  
   206  			svc := formationtemplate.NewService(formationTemplateRepo, idSvc, formationTemplateConv, tenantSvc, whRepo, nil)
   207  
   208  			// WHEN
   209  			result, err := svc.Create(testCase.Context, testCase.Input)
   210  
   211  			// THEN
   212  			if testCase.ExpectedError != nil {
   213  				require.Error(t, err)
   214  				assert.Contains(t, err.Error(), testCase.ExpectedError.Error())
   215  			} else {
   216  				assert.NoError(t, err)
   217  			}
   218  			assert.Equal(t, testCase.ExpectedOutput, result)
   219  
   220  			mock.AssertExpectationsForObjects(t, formationTemplateRepo, idSvc, formationTemplateConv, tenantSvc, whRepo)
   221  		})
   222  	}
   223  }
   224  
   225  func TestService_Exist(t *testing.T) {
   226  	// GIVEN
   227  	ctx := context.TODO()
   228  
   229  	testErr := errors.New("test error")
   230  
   231  	testCases := []struct {
   232  		Name                        string
   233  		Input                       string
   234  		FormationTemplateRepository func() *automock.FormationTemplateRepository
   235  		ExpectedOutput              bool
   236  		ExpectedError               error
   237  	}{
   238  		{
   239  			Name:  "Success",
   240  			Input: testID,
   241  			FormationTemplateRepository: func() *automock.FormationTemplateRepository {
   242  				repo := &automock.FormationTemplateRepository{}
   243  				repo.On("Exists", ctx, testID).Return(true, nil).Once()
   244  				return repo
   245  			},
   246  			ExpectedOutput: true,
   247  			ExpectedError:  nil,
   248  		},
   249  		{
   250  			Name:  "Error when checking if formation template exists",
   251  			Input: testID,
   252  			FormationTemplateRepository: func() *automock.FormationTemplateRepository {
   253  				repo := &automock.FormationTemplateRepository{}
   254  				repo.On("Exists", ctx, testID).Return(false, testErr).Once()
   255  				return repo
   256  			},
   257  			ExpectedOutput: false,
   258  			ExpectedError:  testErr,
   259  		},
   260  	}
   261  
   262  	for _, testCase := range testCases {
   263  		t.Run(testCase.Name, func(t *testing.T) {
   264  			formationTemplateRepo := testCase.FormationTemplateRepository()
   265  
   266  			svc := formationtemplate.NewService(formationTemplateRepo, nil, nil, nil, nil, nil)
   267  
   268  			// WHEN
   269  			result, err := svc.Exist(ctx, testCase.Input)
   270  
   271  			// THEN
   272  			if testCase.ExpectedError != nil {
   273  				require.Error(t, err)
   274  				assert.Contains(t, err.Error(), testCase.ExpectedError.Error())
   275  			} else {
   276  				assert.NoError(t, err)
   277  			}
   278  			assert.Equal(t, testCase.ExpectedOutput, result)
   279  
   280  			mock.AssertExpectationsForObjects(t, formationTemplateRepo)
   281  		})
   282  	}
   283  }
   284  
   285  func TestService_Get(t *testing.T) {
   286  	// GIVEN
   287  	ctx := context.TODO()
   288  
   289  	testErr := errors.New("test error")
   290  
   291  	testCases := []struct {
   292  		Name                        string
   293  		Input                       string
   294  		FormationTemplateRepository func() *automock.FormationTemplateRepository
   295  		ExpectedOutput              *model.FormationTemplate
   296  		ExpectedError               error
   297  	}{
   298  		{
   299  			Name:  "Success",
   300  			Input: testID,
   301  			FormationTemplateRepository: func() *automock.FormationTemplateRepository {
   302  				repo := &automock.FormationTemplateRepository{}
   303  				repo.On("Get", ctx, testID).Return(&formationTemplateModel, nil).Once()
   304  				return repo
   305  			},
   306  			ExpectedOutput: &formationTemplateModel,
   307  			ExpectedError:  nil,
   308  		},
   309  		{
   310  			Name:  "Error when getting formation template",
   311  			Input: testID,
   312  			FormationTemplateRepository: func() *automock.FormationTemplateRepository {
   313  				repo := &automock.FormationTemplateRepository{}
   314  				repo.On("Get", ctx, testID).Return(nil, testErr).Once()
   315  				return repo
   316  			},
   317  			ExpectedOutput: nil,
   318  			ExpectedError:  testErr,
   319  		},
   320  	}
   321  
   322  	for _, testCase := range testCases {
   323  		t.Run(testCase.Name, func(t *testing.T) {
   324  			formationTemplateRepo := testCase.FormationTemplateRepository()
   325  
   326  			svc := formationtemplate.NewService(formationTemplateRepo, nil, nil, nil, nil, nil)
   327  
   328  			// WHEN
   329  			result, err := svc.Get(ctx, testCase.Input)
   330  
   331  			// THEN
   332  			if testCase.ExpectedError != nil {
   333  				require.Error(t, err)
   334  				assert.Contains(t, err.Error(), testCase.ExpectedError.Error())
   335  			} else {
   336  				assert.NoError(t, err)
   337  			}
   338  			assert.Equal(t, testCase.ExpectedOutput, result)
   339  
   340  			mock.AssertExpectationsForObjects(t, formationTemplateRepo)
   341  		})
   342  	}
   343  }
   344  
   345  func TestService_List(t *testing.T) {
   346  	// GIVEN
   347  	ctx := tnt.SaveToContext(context.TODO(), testTenantID, testTenantID)
   348  	ctxWithEmptyTenants := tnt.SaveToContext(context.TODO(), "", "")
   349  
   350  	testErr := errors.New("test error")
   351  	pageSize := 20
   352  	invalidPageSize := -100
   353  
   354  	testCases := []struct {
   355  		Name                        string
   356  		Context                     context.Context
   357  		PageSize                    int
   358  		FormationTemplateRepository func() *automock.FormationTemplateRepository
   359  		TenantSvc                   func() *automock.TenantService
   360  		ExpectedOutput              *model.FormationTemplatePage
   361  		ExpectedError               error
   362  	}{
   363  		{
   364  			Name:     "Success",
   365  			Context:  ctx,
   366  			PageSize: pageSize,
   367  			FormationTemplateRepository: func() *automock.FormationTemplateRepository {
   368  				repo := &automock.FormationTemplateRepository{}
   369  				repo.On("List", ctx, testTenantID, pageSize, mock.Anything).Return(&formationTemplateModelPage, nil).Once()
   370  				return repo
   371  			},
   372  			TenantSvc: func() *automock.TenantService {
   373  				svc := &automock.TenantService{}
   374  				svc.On("ExtractTenantIDForTenantScopedFormationTemplates", ctx).Return(testTenantID, nil).Once()
   375  				return svc
   376  			},
   377  			ExpectedOutput: &formationTemplateModelPage,
   378  			ExpectedError:  nil,
   379  		},
   380  		{
   381  			Name:     "Success when tenant in ctx is empty",
   382  			Context:  ctxWithEmptyTenants,
   383  			PageSize: pageSize,
   384  			FormationTemplateRepository: func() *automock.FormationTemplateRepository {
   385  				repo := &automock.FormationTemplateRepository{}
   386  				repo.On("List", ctxWithEmptyTenants, "", pageSize, mock.Anything).Return(&formationTemplateModelNullTenantPage, nil).Once()
   387  				return repo
   388  			},
   389  			TenantSvc: func() *automock.TenantService {
   390  				svc := &automock.TenantService{}
   391  				svc.On("ExtractTenantIDForTenantScopedFormationTemplates", ctxWithEmptyTenants).Return("", nil).Once()
   392  				return svc
   393  			},
   394  			ExpectedOutput: &formationTemplateModelNullTenantPage,
   395  			ExpectedError:  nil,
   396  		},
   397  		{
   398  			Name:     "Error when getting tenant object",
   399  			Context:  ctx,
   400  			PageSize: pageSize,
   401  			FormationTemplateRepository: func() *automock.FormationTemplateRepository {
   402  				return &automock.FormationTemplateRepository{}
   403  			},
   404  			TenantSvc: func() *automock.TenantService {
   405  				svc := &automock.TenantService{}
   406  				svc.On("ExtractTenantIDForTenantScopedFormationTemplates", ctx).Return("", testErr)
   407  				return svc
   408  			},
   409  			ExpectedOutput: nil,
   410  			ExpectedError:  testErr,
   411  		},
   412  		{
   413  			Name:     "Error when listing formation templates",
   414  			Context:  ctx,
   415  			PageSize: pageSize,
   416  			FormationTemplateRepository: func() *automock.FormationTemplateRepository {
   417  				repo := &automock.FormationTemplateRepository{}
   418  				repo.On("List", ctx, testTenantID, pageSize, mock.Anything).Return(nil, testErr).Once()
   419  				return repo
   420  			},
   421  			TenantSvc: func() *automock.TenantService {
   422  				svc := &automock.TenantService{}
   423  				svc.On("ExtractTenantIDForTenantScopedFormationTemplates", ctx).Return(testTenantID, nil).Once()
   424  				return svc
   425  			},
   426  			ExpectedOutput: nil,
   427  			ExpectedError:  testErr,
   428  		},
   429  		{
   430  			Name:                        "Error when invalid page size is given",
   431  			Context:                     ctx,
   432  			PageSize:                    invalidPageSize,
   433  			FormationTemplateRepository: UnusedFormationTemplateRepository,
   434  			TenantSvc:                   UnusedTenantService,
   435  			ExpectedOutput:              nil,
   436  			ExpectedError:               errors.New("page size must be between 1 and 200"),
   437  		},
   438  	}
   439  
   440  	for _, testCase := range testCases {
   441  		t.Run(testCase.Name, func(t *testing.T) {
   442  			formationTemplateRepo := testCase.FormationTemplateRepository()
   443  			tenantSvc := testCase.TenantSvc()
   444  
   445  			svc := formationtemplate.NewService(formationTemplateRepo, nil, nil, tenantSvc, nil, nil)
   446  
   447  			// WHEN
   448  			result, err := svc.List(testCase.Context, testCase.PageSize, "")
   449  
   450  			// THEN
   451  			if testCase.ExpectedError != nil {
   452  				require.Error(t, err)
   453  				assert.Contains(t, err.Error(), testCase.ExpectedError.Error())
   454  			} else {
   455  				assert.NoError(t, err)
   456  			}
   457  			assert.Equal(t, testCase.ExpectedOutput, result)
   458  
   459  			mock.AssertExpectationsForObjects(t, formationTemplateRepo, tenantSvc)
   460  		})
   461  	}
   462  }
   463  
   464  func TestService_Update(t *testing.T) {
   465  	// GIVEN
   466  	ctx := tnt.SaveToContext(context.TODO(), testTenantID, testTenantID)
   467  	ctxWithEmptyTenants := tnt.SaveToContext(context.TODO(), "", "")
   468  
   469  	testErr := errors.New("test error")
   470  
   471  	uidSvcFn := func() *automock.UIDService {
   472  		uidSvc := &automock.UIDService{}
   473  		uidSvc.On("Generate").Return(testID)
   474  		return uidSvc
   475  	}
   476  
   477  	testCases := []struct {
   478  		Name                        string
   479  		Context                     context.Context
   480  		Input                       string
   481  		InputFormationTemplate      *model.FormationTemplateInput
   482  		FormationTemplateRepository func() *automock.FormationTemplateRepository
   483  		FormationTemplateConverter  func() *automock.FormationTemplateConverter
   484  		TenantSvc                   func() *automock.TenantService
   485  		ExpectedError               error
   486  	}{
   487  		{
   488  			Name:                   "Success",
   489  			Context:                ctx,
   490  			Input:                  testID,
   491  			InputFormationTemplate: &formationTemplateModelInput,
   492  			FormationTemplateRepository: func() *automock.FormationTemplateRepository {
   493  				repo := &automock.FormationTemplateRepository{}
   494  				repo.On("Exists", ctx, testID).Return(true, nil).Once()
   495  				repo.On("Update", ctx, &formationTemplateModel).Return(nil).Once()
   496  				return repo
   497  			},
   498  			FormationTemplateConverter: func() *automock.FormationTemplateConverter {
   499  				converter := &automock.FormationTemplateConverter{}
   500  				converter.On("FromModelInputToModel", &formationTemplateModelInput, testID, testTenantID).Return(&formationTemplateModel).Once()
   501  
   502  				return converter
   503  			},
   504  			TenantSvc: func() *automock.TenantService {
   505  				svc := &automock.TenantService{}
   506  				svc.On("ExtractTenantIDForTenantScopedFormationTemplates", ctx).Return(testTenantID, nil).Once()
   507  				return svc
   508  			},
   509  			ExpectedError: nil,
   510  		},
   511  		{
   512  			Name:                   "Success when tenant in context is empty",
   513  			Context:                ctxWithEmptyTenants,
   514  			Input:                  testID,
   515  			InputFormationTemplate: &formationTemplateModelInput,
   516  			FormationTemplateRepository: func() *automock.FormationTemplateRepository {
   517  				repo := &automock.FormationTemplateRepository{}
   518  				repo.On("Exists", ctxWithEmptyTenants, testID).Return(true, nil).Once()
   519  				repo.On("Update", ctxWithEmptyTenants, &formationTemplateModelNullTenant).Return(nil).Once()
   520  				return repo
   521  			},
   522  			FormationTemplateConverter: func() *automock.FormationTemplateConverter {
   523  				converter := &automock.FormationTemplateConverter{}
   524  				converter.On("FromModelInputToModel", &formationTemplateModelInput, testID, "").Return(&formationTemplateModelNullTenant).Once()
   525  
   526  				return converter
   527  			},
   528  			TenantSvc: func() *automock.TenantService {
   529  				svc := &automock.TenantService{}
   530  				svc.On("ExtractTenantIDForTenantScopedFormationTemplates", ctxWithEmptyTenants).Return("", nil).Once()
   531  				return svc
   532  			},
   533  			ExpectedError: nil,
   534  		},
   535  		{
   536  			Name:                   "Error when formation template does not exist",
   537  			Context:                ctx,
   538  			Input:                  testID,
   539  			InputFormationTemplate: &formationTemplateModelInput,
   540  			FormationTemplateRepository: func() *automock.FormationTemplateRepository {
   541  				repo := &automock.FormationTemplateRepository{}
   542  				repo.On("Exists", ctx, testID).Return(false, nil).Once()
   543  				return repo
   544  			},
   545  			TenantSvc:                  UnusedTenantService,
   546  			FormationTemplateConverter: UnusedFormationTemplateConverter,
   547  			ExpectedError:              apperrors.NewNotFoundError(resource.FormationTemplate, testID),
   548  		},
   549  		{
   550  			Name:                   "Error when formation existence check failed",
   551  			Context:                ctx,
   552  			Input:                  testID,
   553  			InputFormationTemplate: &formationTemplateModelInput,
   554  			FormationTemplateRepository: func() *automock.FormationTemplateRepository {
   555  				repo := &automock.FormationTemplateRepository{}
   556  				repo.On("Exists", ctx, testID).Return(false, testErr).Once()
   557  				return repo
   558  			},
   559  			FormationTemplateConverter: UnusedFormationTemplateConverter,
   560  			TenantSvc:                  UnusedTenantService,
   561  			ExpectedError:              testErr,
   562  		},
   563  		{
   564  			Name:                   "Error when getting tenant object",
   565  			Context:                ctx,
   566  			Input:                  testID,
   567  			InputFormationTemplate: &formationTemplateModelInput,
   568  			FormationTemplateRepository: func() *automock.FormationTemplateRepository {
   569  				repo := &automock.FormationTemplateRepository{}
   570  				repo.On("Exists", ctx, testID).Return(true, nil).Once()
   571  				return repo
   572  			},
   573  			FormationTemplateConverter: UnusedFormationTemplateConverter,
   574  			TenantSvc: func() *automock.TenantService {
   575  				svc := &automock.TenantService{}
   576  				svc.On("ExtractTenantIDForTenantScopedFormationTemplates", ctx).Return("", testErr)
   577  				return svc
   578  			},
   579  			ExpectedError: testErr,
   580  		},
   581  		{
   582  			Name:                   "Error when updating formation template fails",
   583  			Context:                ctx,
   584  			Input:                  testID,
   585  			InputFormationTemplate: &formationTemplateModelInput,
   586  			FormationTemplateRepository: func() *automock.FormationTemplateRepository {
   587  				repo := &automock.FormationTemplateRepository{}
   588  				repo.On("Exists", ctx, testID).Return(true, nil).Once()
   589  				repo.On("Update", ctx, &formationTemplateModel).Return(testErr).Once()
   590  				return repo
   591  			},
   592  			FormationTemplateConverter: func() *automock.FormationTemplateConverter {
   593  				converter := &automock.FormationTemplateConverter{}
   594  				converter.On("FromModelInputToModel", &formationTemplateModelInput, testID, testTenantID).Return(&formationTemplateModel).Once()
   595  
   596  				return converter
   597  			},
   598  			TenantSvc: func() *automock.TenantService {
   599  				svc := &automock.TenantService{}
   600  				svc.On("ExtractTenantIDForTenantScopedFormationTemplates", ctx).Return(testTenantID, nil).Once()
   601  				return svc
   602  			},
   603  			ExpectedError: testErr,
   604  		},
   605  	}
   606  
   607  	for _, testCase := range testCases {
   608  		t.Run(testCase.Name, func(t *testing.T) {
   609  			formationTemplateRepo := testCase.FormationTemplateRepository()
   610  			formationTemplateConverter := testCase.FormationTemplateConverter()
   611  			tenantSvc := testCase.TenantSvc()
   612  
   613  			svc := formationtemplate.NewService(formationTemplateRepo, uidSvcFn(), formationTemplateConverter, tenantSvc, nil, nil)
   614  
   615  			// WHEN
   616  			err := svc.Update(testCase.Context, testCase.Input, testCase.InputFormationTemplate)
   617  
   618  			// THEN
   619  			if testCase.ExpectedError != nil {
   620  				require.Error(t, err)
   621  				assert.Contains(t, err.Error(), testCase.ExpectedError.Error())
   622  			} else {
   623  				assert.NoError(t, err)
   624  			}
   625  
   626  			mock.AssertExpectationsForObjects(t, formationTemplateRepo, tenantSvc)
   627  		})
   628  	}
   629  }
   630  
   631  func TestService_Delete(t *testing.T) {
   632  	// GIVEN
   633  	ctx := tnt.SaveToContext(context.TODO(), testTenantID, testTenantID)
   634  	ctxWithEmptyTenants := tnt.SaveToContext(context.TODO(), "", "")
   635  	testErr := errors.New("test error")
   636  
   637  	testCases := []struct {
   638  		Name                        string
   639  		Context                     context.Context
   640  		Input                       string
   641  		FormationTemplateRepository func() *automock.FormationTemplateRepository
   642  		TenantSvc                   func() *automock.TenantService
   643  		ExpectedError               error
   644  	}{
   645  		{
   646  			Name:    "Success",
   647  			Context: ctx,
   648  			Input:   testID,
   649  			FormationTemplateRepository: func() *automock.FormationTemplateRepository {
   650  				repo := &automock.FormationTemplateRepository{}
   651  				repo.On("Delete", ctx, testID, testTenantID).Return(nil).Once()
   652  				return repo
   653  			},
   654  			TenantSvc: func() *automock.TenantService {
   655  				svc := &automock.TenantService{}
   656  				svc.On("ExtractTenantIDForTenantScopedFormationTemplates", ctx).Return(testTenantID, nil).Once()
   657  				return svc
   658  			},
   659  			ExpectedError: nil,
   660  		},
   661  		{
   662  			Name:    "Success when tenant in ctx is empty",
   663  			Context: ctxWithEmptyTenants,
   664  			Input:   testID,
   665  			FormationTemplateRepository: func() *automock.FormationTemplateRepository {
   666  				repo := &automock.FormationTemplateRepository{}
   667  				repo.On("Delete", ctxWithEmptyTenants, testID, "").Return(nil).Once()
   668  				return repo
   669  			},
   670  			TenantSvc: func() *automock.TenantService {
   671  				svc := &automock.TenantService{}
   672  				svc.On("ExtractTenantIDForTenantScopedFormationTemplates", ctxWithEmptyTenants).Return("", nil).Once()
   673  				return svc
   674  			},
   675  			ExpectedError: nil,
   676  		},
   677  		{
   678  			Name:                        "Error when getting tenant object",
   679  			Context:                     ctx,
   680  			Input:                       testID,
   681  			FormationTemplateRepository: UnusedFormationTemplateRepository,
   682  			TenantSvc: func() *automock.TenantService {
   683  				svc := &automock.TenantService{}
   684  				svc.On("ExtractTenantIDForTenantScopedFormationTemplates", ctx).Return("", testErr)
   685  				return svc
   686  			},
   687  			ExpectedError: testErr,
   688  		},
   689  		{
   690  			Name:    "Error when deleting formation template",
   691  			Context: ctx,
   692  			Input:   testID,
   693  			FormationTemplateRepository: func() *automock.FormationTemplateRepository {
   694  				repo := &automock.FormationTemplateRepository{}
   695  				repo.On("Delete", ctx, testID, testTenantID).Return(testErr).Once()
   696  				return repo
   697  			},
   698  			TenantSvc: func() *automock.TenantService {
   699  				svc := &automock.TenantService{}
   700  				svc.On("ExtractTenantIDForTenantScopedFormationTemplates", ctx).Return(testTenantID, nil).Once()
   701  				return svc
   702  			},
   703  			ExpectedError: testErr,
   704  		},
   705  	}
   706  
   707  	for _, testCase := range testCases {
   708  		t.Run(testCase.Name, func(t *testing.T) {
   709  			formationTemplateRepo := testCase.FormationTemplateRepository()
   710  			tenantSvc := testCase.TenantSvc()
   711  
   712  			svc := formationtemplate.NewService(formationTemplateRepo, nil, nil, tenantSvc, nil, nil)
   713  
   714  			// WHEN
   715  			err := svc.Delete(testCase.Context, testCase.Input)
   716  
   717  			// THEN
   718  			if testCase.ExpectedError != nil {
   719  				require.Error(t, err)
   720  				assert.Contains(t, err.Error(), testCase.ExpectedError.Error())
   721  			} else {
   722  				assert.NoError(t, err)
   723  			}
   724  
   725  			mock.AssertExpectationsForObjects(t, formationTemplateRepo, tenantSvc)
   726  		})
   727  	}
   728  }
   729  
   730  func TestService_ListWebhooksForFormationTemplate(t *testing.T) {
   731  	// GIVEN
   732  	ctx := tnt.SaveToContext(context.TODO(), testTenantID, testTenantID)
   733  	ctxWithEmptyTenants := tnt.SaveToContext(context.TODO(), "", "")
   734  	testErr := errors.New("test error")
   735  	testWebhook := &model.Webhook{
   736  		ID:       testWebhookID,
   737  		ObjectID: testID,
   738  	}
   739  
   740  	testCases := []struct {
   741  		Name             string
   742  		Context          context.Context
   743  		Input            string
   744  		WebhookSvc       func() *automock.WebhookService
   745  		TenantSvc        func() *automock.TenantService
   746  		ExpectedWebhooks []*model.Webhook
   747  		ExpectedError    error
   748  	}{
   749  		{
   750  			Name:    "Success",
   751  			Context: ctx,
   752  			Input:   testID,
   753  			WebhookSvc: func() *automock.WebhookService {
   754  				webhookSvc := &automock.WebhookService{}
   755  				webhookSvc.On("ListForFormationTemplate", ctx, testTenantID, testID).Return([]*model.Webhook{testWebhook}, nil)
   756  				return webhookSvc
   757  			},
   758  			TenantSvc: func() *automock.TenantService {
   759  				svc := &automock.TenantService{}
   760  				svc.On("ExtractTenantIDForTenantScopedFormationTemplates", ctx).Return(testTenantID, nil).Once()
   761  				return svc
   762  			},
   763  			ExpectedError:    nil,
   764  			ExpectedWebhooks: []*model.Webhook{testWebhook},
   765  		},
   766  		{
   767  			Name: "Success when tenant in ctx is empty",
   768  			WebhookSvc: func() *automock.WebhookService {
   769  				webhookSvc := &automock.WebhookService{}
   770  				webhookSvc.On("ListForFormationTemplate", ctxWithEmptyTenants, "", testID).Return([]*model.Webhook{testWebhook}, nil)
   771  				return webhookSvc
   772  			},
   773  			Context: ctxWithEmptyTenants,
   774  			Input:   testID,
   775  			TenantSvc: func() *automock.TenantService {
   776  				svc := &automock.TenantService{}
   777  				svc.On("ExtractTenantIDForTenantScopedFormationTemplates", ctxWithEmptyTenants).Return("", nil).Once()
   778  				return svc
   779  			},
   780  			ExpectedError:    nil,
   781  			ExpectedWebhooks: []*model.Webhook{testWebhook},
   782  		},
   783  		{
   784  			Name:       "Error when getting tenant object",
   785  			Context:    ctx,
   786  			Input:      testID,
   787  			WebhookSvc: UnusedWebhookService,
   788  			TenantSvc: func() *automock.TenantService {
   789  				svc := &automock.TenantService{}
   790  				svc.On("ExtractTenantIDForTenantScopedFormationTemplates", ctx).Return("", testErr)
   791  				return svc
   792  			},
   793  			ExpectedError: testErr,
   794  		},
   795  		{
   796  			Name:    "Error when listing formation template webhooks",
   797  			Context: ctx,
   798  			Input:   testID,
   799  			WebhookSvc: func() *automock.WebhookService {
   800  				webhookSvc := &automock.WebhookService{}
   801  				webhookSvc.On("ListForFormationTemplate", ctx, testTenantID, testID).Return(nil, testErr)
   802  				return webhookSvc
   803  			},
   804  			TenantSvc: func() *automock.TenantService {
   805  				svc := &automock.TenantService{}
   806  				svc.On("ExtractTenantIDForTenantScopedFormationTemplates", ctx).Return(testTenantID, nil).Once()
   807  				return svc
   808  			},
   809  			ExpectedError: testErr,
   810  		},
   811  	}
   812  
   813  	for _, testCase := range testCases {
   814  		t.Run(testCase.Name, func(t *testing.T) {
   815  			tenantSvc := testCase.TenantSvc()
   816  			webhookSvc := testCase.WebhookSvc()
   817  
   818  			svc := formationtemplate.NewService(nil, nil, nil, tenantSvc, nil, webhookSvc)
   819  
   820  			// WHEN
   821  			webhooks, err := svc.ListWebhooksForFormationTemplate(testCase.Context, testID)
   822  
   823  			// THEN
   824  			if testCase.ExpectedError != nil {
   825  				require.Error(t, err)
   826  				assert.Contains(t, err.Error(), testCase.ExpectedError.Error())
   827  			} else {
   828  				assert.ElementsMatch(t, webhooks, testCase.ExpectedWebhooks)
   829  				assert.NoError(t, err)
   830  			}
   831  
   832  			mock.AssertExpectationsForObjects(t, webhookSvc, tenantSvc)
   833  		})
   834  	}
   835  }