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

     1  package product_test
     2  
     3  import (
     4  	"context"
     5  	"testing"
     6  
     7  	"github.com/kyma-incubator/compass/components/director/pkg/resource"
     8  
     9  	"github.com/kyma-incubator/compass/components/director/internal/domain/product"
    10  	"github.com/kyma-incubator/compass/components/director/internal/domain/product/automock"
    11  	"github.com/kyma-incubator/compass/components/director/internal/domain/tenant"
    12  	"github.com/kyma-incubator/compass/components/director/internal/model"
    13  	"github.com/pkg/errors"
    14  	"github.com/stretchr/testify/assert"
    15  	"github.com/stretchr/testify/mock"
    16  	"github.com/stretchr/testify/require"
    17  )
    18  
    19  func TestService_Create(t *testing.T) {
    20  	// GIVEN
    21  	testErr := errors.New("Test error")
    22  
    23  	ctx := context.TODO()
    24  	ctx = tenant.SaveToContext(ctx, tenantID, externalTenantID)
    25  
    26  	modelProductForApp := fixProductModelForApp()
    27  	modelProductForAppTemplateVersion := fixProductModelForAppTemplateVersion()
    28  	modelInput := *fixProductModelInput()
    29  
    30  	testCases := []struct {
    31  		Name         string
    32  		RepositoryFn func() *automock.ProductRepository
    33  		UIDServiceFn func() *automock.UIDService
    34  		Input        model.ProductInput
    35  		ResourceType resource.Type
    36  		ResourceID   string
    37  		ExpectedErr  error
    38  	}{
    39  		{
    40  			Name: "Success for Application",
    41  			RepositoryFn: func() *automock.ProductRepository {
    42  				repo := &automock.ProductRepository{}
    43  				repo.On("Create", ctx, tenantID, modelProductForApp).Return(nil).Once()
    44  				return repo
    45  			},
    46  			UIDServiceFn: func() *automock.UIDService {
    47  				svc := &automock.UIDService{}
    48  				svc.On("Generate").Return(productID)
    49  				return svc
    50  			},
    51  			Input:        modelInput,
    52  			ResourceType: resource.Application,
    53  			ResourceID:   appID,
    54  			ExpectedErr:  nil,
    55  		},
    56  		{
    57  			Name: "Success for Application Template Version",
    58  			RepositoryFn: func() *automock.ProductRepository {
    59  				repo := &automock.ProductRepository{}
    60  				repo.On("CreateGlobal", ctx, modelProductForAppTemplateVersion).Return(nil).Once()
    61  				return repo
    62  			},
    63  			UIDServiceFn: func() *automock.UIDService {
    64  				svc := &automock.UIDService{}
    65  				svc.On("Generate").Return(productID)
    66  				return svc
    67  			},
    68  			Input:        modelInput,
    69  			ResourceType: resource.ApplicationTemplateVersion,
    70  			ResourceID:   appTemplateVersionID,
    71  			ExpectedErr:  nil,
    72  		},
    73  		{
    74  			Name: "Error - Product creation for Application",
    75  			RepositoryFn: func() *automock.ProductRepository {
    76  				repo := &automock.ProductRepository{}
    77  				repo.On("Create", ctx, tenantID, modelProductForApp).Return(testErr).Once()
    78  				return repo
    79  			},
    80  			UIDServiceFn: func() *automock.UIDService {
    81  				svc := &automock.UIDService{}
    82  				svc.On("Generate").Return(productID)
    83  				return svc
    84  			},
    85  			Input:        modelInput,
    86  			ResourceType: resource.Application,
    87  			ResourceID:   appID,
    88  			ExpectedErr:  testErr,
    89  		},
    90  		{
    91  			Name: "Error - Product creation for Application Template Version",
    92  			RepositoryFn: func() *automock.ProductRepository {
    93  				repo := &automock.ProductRepository{}
    94  				repo.On("CreateGlobal", ctx, modelProductForAppTemplateVersion).Return(testErr).Once()
    95  				return repo
    96  			},
    97  			UIDServiceFn: func() *automock.UIDService {
    98  				svc := &automock.UIDService{}
    99  				svc.On("Generate").Return(productID)
   100  				return svc
   101  			},
   102  			Input:        modelInput,
   103  			ResourceType: resource.ApplicationTemplateVersion,
   104  			ResourceID:   appTemplateVersionID,
   105  			ExpectedErr:  testErr,
   106  		},
   107  	}
   108  
   109  	for _, testCase := range testCases {
   110  		t.Run(testCase.Name, func(t *testing.T) {
   111  			// GIVEN
   112  			repo := testCase.RepositoryFn()
   113  			uidSvc := testCase.UIDServiceFn()
   114  
   115  			svc := product.NewService(repo, uidSvc)
   116  
   117  			// WHEN
   118  			result, err := svc.Create(ctx, testCase.ResourceType, testCase.ResourceID, testCase.Input)
   119  
   120  			// then
   121  			if testCase.ExpectedErr != nil {
   122  				require.Error(t, err)
   123  				assert.Contains(t, err.Error(), testCase.ExpectedErr.Error())
   124  			} else {
   125  				assert.IsType(t, "string", result)
   126  			}
   127  
   128  			mock.AssertExpectationsForObjects(t, repo)
   129  		})
   130  	}
   131  	t.Run("Error when tenant not in context", func(t *testing.T) {
   132  		svc := product.NewService(nil, fixUIDService())
   133  		// WHEN
   134  		_, err := svc.Create(context.TODO(), resource.Application, "", model.ProductInput{})
   135  		// THEN
   136  		require.Error(t, err)
   137  		assert.Contains(t, err.Error(), "cannot read tenant from context")
   138  	})
   139  }
   140  
   141  func TestService_CreateGlobal(t *testing.T) {
   142  	// GIVEN
   143  	testErr := errors.New("Test error")
   144  
   145  	ctx := context.TODO()
   146  
   147  	modelProduct := fixGlobalProductModel()
   148  	modelInput := *fixProductModelInput()
   149  
   150  	testCases := []struct {
   151  		Name         string
   152  		RepositoryFn func() *automock.ProductRepository
   153  		UIDServiceFn func() *automock.UIDService
   154  		Input        model.ProductInput
   155  		ExpectedErr  error
   156  	}{
   157  		{
   158  			Name: "Success",
   159  			RepositoryFn: func() *automock.ProductRepository {
   160  				repo := &automock.ProductRepository{}
   161  				repo.On("CreateGlobal", ctx, modelProduct).Return(nil).Once()
   162  				return repo
   163  			},
   164  			UIDServiceFn: func() *automock.UIDService {
   165  				svc := &automock.UIDService{}
   166  				svc.On("Generate").Return(productID)
   167  				return svc
   168  			},
   169  			Input:       modelInput,
   170  			ExpectedErr: nil,
   171  		},
   172  		{
   173  			Name: "Error - Product creation",
   174  			RepositoryFn: func() *automock.ProductRepository {
   175  				repo := &automock.ProductRepository{}
   176  				repo.On("CreateGlobal", ctx, modelProduct).Return(testErr).Once()
   177  				return repo
   178  			},
   179  			UIDServiceFn: func() *automock.UIDService {
   180  				svc := &automock.UIDService{}
   181  				svc.On("Generate").Return(productID)
   182  				return svc
   183  			},
   184  			Input:       modelInput,
   185  			ExpectedErr: testErr,
   186  		},
   187  	}
   188  
   189  	for _, testCase := range testCases {
   190  		t.Run(testCase.Name, func(t *testing.T) {
   191  			// GIVEN
   192  			repo := testCase.RepositoryFn()
   193  			uidSvc := testCase.UIDServiceFn()
   194  
   195  			svc := product.NewService(repo, uidSvc)
   196  
   197  			// WHEN
   198  			result, err := svc.CreateGlobal(ctx, testCase.Input)
   199  
   200  			// then
   201  			if testCase.ExpectedErr != nil {
   202  				require.Error(t, err)
   203  				assert.Contains(t, err.Error(), testCase.ExpectedErr.Error())
   204  			} else {
   205  				assert.IsType(t, "string", result)
   206  			}
   207  
   208  			mock.AssertExpectationsForObjects(t, repo)
   209  		})
   210  	}
   211  }
   212  
   213  func TestService_Update(t *testing.T) {
   214  	// GIVEN
   215  	testErr := errors.New("Test error")
   216  
   217  	modelProductForApp := fixProductModelForApp()
   218  	modelProductForAppTemplateVersion := fixProductModelForApp()
   219  	modelInput := *fixProductModelInput()
   220  
   221  	inputProductModel := mock.MatchedBy(func(prod *model.Product) bool {
   222  		return prod.Title == modelInput.Title
   223  	})
   224  
   225  	ctx := context.TODO()
   226  	ctx = tenant.SaveToContext(ctx, tenantID, externalTenantID)
   227  
   228  	testCases := []struct {
   229  		Name         string
   230  		RepositoryFn func() *automock.ProductRepository
   231  		Input        model.ProductInput
   232  		InputID      string
   233  		ResourceType resource.Type
   234  		ExpectedErr  error
   235  	}{
   236  		{
   237  			Name: "Success for Application",
   238  			RepositoryFn: func() *automock.ProductRepository {
   239  				repo := &automock.ProductRepository{}
   240  				repo.On("GetByID", ctx, tenantID, productID).Return(modelProductForApp, nil).Once()
   241  				repo.On("Update", ctx, tenantID, inputProductModel).Return(nil).Once()
   242  				return repo
   243  			},
   244  			InputID:      productID,
   245  			Input:        modelInput,
   246  			ResourceType: resource.Application,
   247  			ExpectedErr:  nil,
   248  		},
   249  		{
   250  			Name: "Success for Application Template Version",
   251  			RepositoryFn: func() *automock.ProductRepository {
   252  				repo := &automock.ProductRepository{}
   253  				repo.On("GetByIDGlobal", ctx, productID).Return(modelProductForAppTemplateVersion, nil).Once()
   254  				repo.On("UpdateGlobal", ctx, inputProductModel).Return(nil).Once()
   255  				return repo
   256  			},
   257  			InputID:      productID,
   258  			Input:        modelInput,
   259  			ResourceType: resource.ApplicationTemplateVersion,
   260  			ExpectedErr:  nil,
   261  		},
   262  		{
   263  			Name: "Update Error for Application",
   264  			RepositoryFn: func() *automock.ProductRepository {
   265  				repo := &automock.ProductRepository{}
   266  				repo.On("GetByID", ctx, tenantID, productID).Return(modelProductForApp, nil).Once()
   267  				repo.On("Update", ctx, tenantID, inputProductModel).Return(testErr).Once()
   268  				return repo
   269  			},
   270  			InputID:      productID,
   271  			Input:        modelInput,
   272  			ResourceType: resource.Application,
   273  			ExpectedErr:  testErr,
   274  		},
   275  		{
   276  			Name: "Update Error for Application Template Version",
   277  			RepositoryFn: func() *automock.ProductRepository {
   278  				repo := &automock.ProductRepository{}
   279  				repo.On("GetByIDGlobal", ctx, productID).Return(modelProductForAppTemplateVersion, nil).Once()
   280  				repo.On("UpdateGlobal", ctx, inputProductModel).Return(testErr).Once()
   281  				return repo
   282  			},
   283  			InputID:      productID,
   284  			Input:        modelInput,
   285  			ResourceType: resource.ApplicationTemplateVersion,
   286  			ExpectedErr:  testErr,
   287  		},
   288  		{
   289  			Name: "Get Error for Application",
   290  			RepositoryFn: func() *automock.ProductRepository {
   291  				repo := &automock.ProductRepository{}
   292  				repo.On("GetByID", ctx, tenantID, productID).Return(nil, testErr).Once()
   293  				return repo
   294  			},
   295  			InputID:      productID,
   296  			Input:        modelInput,
   297  			ResourceType: resource.Application,
   298  			ExpectedErr:  testErr,
   299  		},
   300  		{
   301  			Name: "Get Error for Application Template Version",
   302  			RepositoryFn: func() *automock.ProductRepository {
   303  				repo := &automock.ProductRepository{}
   304  				repo.On("GetByIDGlobal", ctx, productID).Return(nil, testErr).Once()
   305  				return repo
   306  			},
   307  			InputID:      productID,
   308  			Input:        modelInput,
   309  			ResourceType: resource.ApplicationTemplateVersion,
   310  			ExpectedErr:  testErr,
   311  		},
   312  	}
   313  
   314  	for _, testCase := range testCases {
   315  		t.Run(testCase.Name, func(t *testing.T) {
   316  			// GIVEN
   317  			repo := testCase.RepositoryFn()
   318  
   319  			svc := product.NewService(repo, nil)
   320  
   321  			// WHEN
   322  			err := svc.Update(ctx, testCase.ResourceType, testCase.InputID, testCase.Input)
   323  
   324  			// then
   325  			if testCase.ExpectedErr == nil {
   326  				require.NoError(t, err)
   327  			} else {
   328  				require.Error(t, err)
   329  				assert.Contains(t, err.Error(), testCase.ExpectedErr.Error())
   330  			}
   331  
   332  			repo.AssertExpectations(t)
   333  		})
   334  	}
   335  	t.Run("Error when tenant not in context", func(t *testing.T) {
   336  		svc := product.NewService(nil, nil)
   337  		// WHEN
   338  		err := svc.Update(context.TODO(), resource.Application, "", model.ProductInput{})
   339  		// THEN
   340  		require.Error(t, err)
   341  		assert.Contains(t, err.Error(), "cannot read tenant from context")
   342  	})
   343  }
   344  
   345  func TestService_UpdateGlobal(t *testing.T) {
   346  	// GIVEN
   347  	testErr := errors.New("Test error")
   348  
   349  	modelProduct := fixGlobalProductModel()
   350  	modelInput := *fixProductModelInput()
   351  
   352  	inputProductModel := mock.MatchedBy(func(prod *model.Product) bool {
   353  		return prod.Title == modelInput.Title
   354  	})
   355  
   356  	ctx := context.TODO()
   357  
   358  	testCases := []struct {
   359  		Name         string
   360  		RepositoryFn func() *automock.ProductRepository
   361  		Input        model.ProductInput
   362  		InputID      string
   363  		ExpectedErr  error
   364  	}{
   365  		{
   366  			Name: "Success",
   367  			RepositoryFn: func() *automock.ProductRepository {
   368  				repo := &automock.ProductRepository{}
   369  				repo.On("GetByIDGlobal", ctx, productID).Return(modelProduct, nil).Once()
   370  				repo.On("UpdateGlobal", ctx, inputProductModel).Return(nil).Once()
   371  				return repo
   372  			},
   373  			InputID:     productID,
   374  			Input:       modelInput,
   375  			ExpectedErr: nil,
   376  		},
   377  		{
   378  			Name: "UpdateGlobal Error",
   379  			RepositoryFn: func() *automock.ProductRepository {
   380  				repo := &automock.ProductRepository{}
   381  				repo.On("GetByIDGlobal", ctx, productID).Return(modelProduct, nil).Once()
   382  				repo.On("UpdateGlobal", ctx, inputProductModel).Return(testErr).Once()
   383  				return repo
   384  			},
   385  			InputID:     productID,
   386  			Input:       modelInput,
   387  			ExpectedErr: testErr,
   388  		},
   389  		{
   390  			Name: "Get Error",
   391  			RepositoryFn: func() *automock.ProductRepository {
   392  				repo := &automock.ProductRepository{}
   393  				repo.On("GetByIDGlobal", ctx, productID).Return(nil, testErr).Once()
   394  				return repo
   395  			},
   396  			InputID:     productID,
   397  			Input:       modelInput,
   398  			ExpectedErr: testErr,
   399  		},
   400  	}
   401  
   402  	for _, testCase := range testCases {
   403  		t.Run(testCase.Name, func(t *testing.T) {
   404  			// GIVEN
   405  			repo := testCase.RepositoryFn()
   406  
   407  			svc := product.NewService(repo, nil)
   408  
   409  			// WHEN
   410  			err := svc.UpdateGlobal(ctx, testCase.InputID, testCase.Input)
   411  
   412  			// then
   413  			if testCase.ExpectedErr == nil {
   414  				require.NoError(t, err)
   415  			} else {
   416  				require.Error(t, err)
   417  				assert.Contains(t, err.Error(), testCase.ExpectedErr.Error())
   418  			}
   419  
   420  			repo.AssertExpectations(t)
   421  		})
   422  	}
   423  }
   424  
   425  func TestService_Delete(t *testing.T) {
   426  	// GIVEN
   427  	testErr := errors.New("Test error")
   428  
   429  	ctx := context.TODO()
   430  	ctx = tenant.SaveToContext(ctx, tenantID, externalTenantID)
   431  
   432  	testCases := []struct {
   433  		Name         string
   434  		RepositoryFn func() *automock.ProductRepository
   435  		Input        model.ProductInput
   436  		InputID      string
   437  		ResourceType resource.Type
   438  		ExpectedErr  error
   439  	}{
   440  		{
   441  			Name: "Success for Application",
   442  			RepositoryFn: func() *automock.ProductRepository {
   443  				repo := &automock.ProductRepository{}
   444  				repo.On("Delete", ctx, tenantID, productID).Return(nil).Once()
   445  				return repo
   446  			},
   447  			InputID:      productID,
   448  			ResourceType: resource.Application,
   449  			ExpectedErr:  nil,
   450  		},
   451  		{
   452  			Name: "Success for Application Template Version",
   453  			RepositoryFn: func() *automock.ProductRepository {
   454  				repo := &automock.ProductRepository{}
   455  				repo.On("DeleteGlobal", ctx, productID).Return(nil).Once()
   456  				return repo
   457  			},
   458  			InputID:      productID,
   459  			ResourceType: resource.ApplicationTemplateVersion,
   460  			ExpectedErr:  nil,
   461  		},
   462  		{
   463  			Name: "Delete Error for Application",
   464  			RepositoryFn: func() *automock.ProductRepository {
   465  				repo := &automock.ProductRepository{}
   466  				repo.On("Delete", ctx, tenantID, productID).Return(testErr).Once()
   467  				return repo
   468  			},
   469  			InputID:      productID,
   470  			ResourceType: resource.Application,
   471  			ExpectedErr:  testErr,
   472  		},
   473  		{
   474  			Name: "Delete Error for Application Template Version",
   475  			RepositoryFn: func() *automock.ProductRepository {
   476  				repo := &automock.ProductRepository{}
   477  				repo.On("DeleteGlobal", ctx, productID).Return(testErr).Once()
   478  				return repo
   479  			},
   480  			InputID:      productID,
   481  			ResourceType: resource.ApplicationTemplateVersion,
   482  			ExpectedErr:  testErr,
   483  		},
   484  	}
   485  
   486  	for _, testCase := range testCases {
   487  		t.Run(testCase.Name, func(t *testing.T) {
   488  			// GIVEN
   489  			repo := testCase.RepositoryFn()
   490  
   491  			svc := product.NewService(repo, nil)
   492  
   493  			// WHEN
   494  			err := svc.Delete(ctx, testCase.ResourceType, testCase.InputID)
   495  
   496  			// then
   497  			if testCase.ExpectedErr == nil {
   498  				require.NoError(t, err)
   499  			} else {
   500  				require.Error(t, err)
   501  				assert.Contains(t, err.Error(), testCase.ExpectedErr.Error())
   502  			}
   503  
   504  			repo.AssertExpectations(t)
   505  		})
   506  	}
   507  	t.Run("Error when tenant not in context", func(t *testing.T) {
   508  		svc := product.NewService(nil, nil)
   509  		// WHEN
   510  		err := svc.Delete(context.TODO(), resource.Application, "")
   511  		// THEN
   512  		require.Error(t, err)
   513  		assert.Contains(t, err.Error(), "cannot read tenant from context")
   514  	})
   515  }
   516  
   517  func TestService_DeleteGlobal(t *testing.T) {
   518  	// GIVEN
   519  	testErr := errors.New("Test error")
   520  
   521  	ctx := context.TODO()
   522  
   523  	testCases := []struct {
   524  		Name         string
   525  		RepositoryFn func() *automock.ProductRepository
   526  		Input        model.ProductInput
   527  		InputID      string
   528  		ExpectedErr  error
   529  	}{
   530  		{
   531  			Name: "Success",
   532  			RepositoryFn: func() *automock.ProductRepository {
   533  				repo := &automock.ProductRepository{}
   534  				repo.On("DeleteGlobal", ctx, productID).Return(nil).Once()
   535  				return repo
   536  			},
   537  			InputID:     productID,
   538  			ExpectedErr: nil,
   539  		},
   540  		{
   541  			Name: "DeleteGlobal Error",
   542  			RepositoryFn: func() *automock.ProductRepository {
   543  				repo := &automock.ProductRepository{}
   544  				repo.On("DeleteGlobal", ctx, productID).Return(testErr).Once()
   545  				return repo
   546  			},
   547  			InputID:     productID,
   548  			ExpectedErr: testErr,
   549  		},
   550  	}
   551  
   552  	for _, testCase := range testCases {
   553  		t.Run(testCase.Name, func(t *testing.T) {
   554  			// GIVEN
   555  			repo := testCase.RepositoryFn()
   556  
   557  			svc := product.NewService(repo, nil)
   558  
   559  			// WHEN
   560  			err := svc.DeleteGlobal(ctx, testCase.InputID)
   561  
   562  			// then
   563  			if testCase.ExpectedErr == nil {
   564  				require.NoError(t, err)
   565  			} else {
   566  				require.Error(t, err)
   567  				assert.Contains(t, err.Error(), testCase.ExpectedErr.Error())
   568  			}
   569  
   570  			repo.AssertExpectations(t)
   571  		})
   572  	}
   573  }
   574  
   575  func TestService_Exist(t *testing.T) {
   576  	// GIVEN
   577  	testErr := errors.New("Test error")
   578  	ctx := tenant.SaveToContext(context.TODO(), tenantID, externalTenantID)
   579  
   580  	testCases := []struct {
   581  		Name           string
   582  		RepoFn         func() *automock.ProductRepository
   583  		ExpectedError  error
   584  		ExpectedOutput bool
   585  	}{
   586  		{
   587  			Name: "Success",
   588  			RepoFn: func() *automock.ProductRepository {
   589  				productRepo := &automock.ProductRepository{}
   590  				productRepo.On("Exists", ctx, tenantID, productID).Return(true, nil).Once()
   591  				return productRepo
   592  			},
   593  			ExpectedOutput: true,
   594  		},
   595  		{
   596  			Name: "Error when getting Product",
   597  			RepoFn: func() *automock.ProductRepository {
   598  				productRepo := &automock.ProductRepository{}
   599  				productRepo.On("Exists", ctx, tenantID, productID).Return(false, testErr).Once()
   600  				return productRepo
   601  			},
   602  			ExpectedError:  testErr,
   603  			ExpectedOutput: false,
   604  		},
   605  	}
   606  
   607  	for _, testCase := range testCases {
   608  		t.Run(testCase.Name, func(t *testing.T) {
   609  			productRepo := testCase.RepoFn()
   610  			svc := product.NewService(productRepo, nil)
   611  
   612  			// WHEN
   613  			result, err := svc.Exist(ctx, productID)
   614  
   615  			// THEN
   616  			if testCase.ExpectedError != nil {
   617  				require.Error(t, err)
   618  				assert.Contains(t, err.Error(), testCase.ExpectedError.Error())
   619  			} else {
   620  				assert.NoError(t, err)
   621  			}
   622  			assert.Equal(t, testCase.ExpectedOutput, result)
   623  
   624  			productRepo.AssertExpectations(t)
   625  		})
   626  	}
   627  
   628  	t.Run("Error when tenant not in context", func(t *testing.T) {
   629  		svc := product.NewService(nil, nil)
   630  		// WHEN
   631  		_, err := svc.Exist(context.TODO(), "")
   632  		// THEN
   633  		require.Error(t, err)
   634  		assert.Contains(t, err.Error(), "cannot read tenant from context")
   635  	})
   636  }
   637  
   638  func TestService_Get(t *testing.T) {
   639  	// GIVEN
   640  	testErr := errors.New("Test error")
   641  
   642  	productModel := fixProductModelForApp()
   643  
   644  	ctx := context.TODO()
   645  	ctx = tenant.SaveToContext(ctx, tenantID, externalTenantID)
   646  
   647  	testCases := []struct {
   648  		Name               string
   649  		RepositoryFn       func() *automock.ProductRepository
   650  		Input              model.ProductInput
   651  		InputID            string
   652  		ExpectedProduct    *model.Product
   653  		ExpectedErrMessage string
   654  	}{
   655  		{
   656  			Name: "Success",
   657  			RepositoryFn: func() *automock.ProductRepository {
   658  				repo := &automock.ProductRepository{}
   659  				repo.On("GetByID", ctx, tenantID, productID).Return(productModel, nil).Once()
   660  				return repo
   661  			},
   662  			InputID:            productID,
   663  			ExpectedProduct:    productModel,
   664  			ExpectedErrMessage: "",
   665  		},
   666  		{
   667  			Name: "Returns error when Product retrieval failed",
   668  			RepositoryFn: func() *automock.ProductRepository {
   669  				repo := &automock.ProductRepository{}
   670  				repo.On("GetByID", ctx, tenantID, productID).Return(nil, testErr).Once()
   671  				return repo
   672  			},
   673  			InputID:            productID,
   674  			ExpectedProduct:    productModel,
   675  			ExpectedErrMessage: testErr.Error(),
   676  		},
   677  	}
   678  
   679  	for _, testCase := range testCases {
   680  		t.Run(testCase.Name, func(t *testing.T) {
   681  			repo := testCase.RepositoryFn()
   682  			svc := product.NewService(repo, nil)
   683  
   684  			// WHEN
   685  			prod, err := svc.Get(ctx, testCase.InputID)
   686  
   687  			// then
   688  			if testCase.ExpectedErrMessage == "" {
   689  				require.NoError(t, err)
   690  				assert.Equal(t, testCase.ExpectedProduct, prod)
   691  			} else {
   692  				require.Error(t, err)
   693  				assert.Contains(t, err.Error(), testCase.ExpectedErrMessage)
   694  			}
   695  
   696  			repo.AssertExpectations(t)
   697  		})
   698  	}
   699  	t.Run("Error when tenant not in context", func(t *testing.T) {
   700  		svc := product.NewService(nil, nil)
   701  		// WHEN
   702  		_, err := svc.Get(context.TODO(), "")
   703  		// THEN
   704  		require.Error(t, err)
   705  		assert.Contains(t, err.Error(), "cannot read tenant from context")
   706  	})
   707  }
   708  
   709  func TestService_ListByApplicationID(t *testing.T) {
   710  	// GIVEN
   711  	testErr := errors.New("Test error")
   712  
   713  	products := []*model.Product{
   714  		fixProductModelForApp(),
   715  		fixProductModelForApp(),
   716  		fixProductModelForApp(),
   717  	}
   718  
   719  	ctx := context.TODO()
   720  	ctx = tenant.SaveToContext(ctx, tenantID, externalTenantID)
   721  
   722  	testCases := []struct {
   723  		Name               string
   724  		PageSize           int
   725  		RepositoryFn       func() *automock.ProductRepository
   726  		ExpectedResult     []*model.Product
   727  		ExpectedErrMessage string
   728  	}{
   729  		{
   730  			Name: "Success",
   731  			RepositoryFn: func() *automock.ProductRepository {
   732  				repo := &automock.ProductRepository{}
   733  				repo.On("ListByResourceID", ctx, tenantID, appID, resource.Application).Return(products, nil).Once()
   734  				return repo
   735  			},
   736  			PageSize:           2,
   737  			ExpectedResult:     products,
   738  			ExpectedErrMessage: "",
   739  		},
   740  		{
   741  			Name: "Returns error when APIDefinition listing failed",
   742  			RepositoryFn: func() *automock.ProductRepository {
   743  				repo := &automock.ProductRepository{}
   744  				repo.On("ListByResourceID", ctx, tenantID, appID, resource.Application).Return(nil, testErr).Once()
   745  				return repo
   746  			},
   747  			PageSize:           2,
   748  			ExpectedResult:     nil,
   749  			ExpectedErrMessage: testErr.Error(),
   750  		},
   751  	}
   752  
   753  	for _, testCase := range testCases {
   754  		t.Run(testCase.Name, func(t *testing.T) {
   755  			repo := testCase.RepositoryFn()
   756  
   757  			svc := product.NewService(repo, nil)
   758  
   759  			// WHEN
   760  			docs, err := svc.ListByApplicationID(ctx, appID)
   761  
   762  			// then
   763  			if testCase.ExpectedErrMessage == "" {
   764  				require.NoError(t, err)
   765  				assert.Equal(t, testCase.ExpectedResult, docs)
   766  			} else {
   767  				require.Error(t, err)
   768  				assert.Contains(t, err.Error(), testCase.ExpectedErrMessage)
   769  			}
   770  
   771  			repo.AssertExpectations(t)
   772  		})
   773  	}
   774  	t.Run("Error when tenant not in context", func(t *testing.T) {
   775  		svc := product.NewService(nil, nil)
   776  		// WHEN
   777  		_, err := svc.ListByApplicationID(context.TODO(), "")
   778  		// THEN
   779  		require.Error(t, err)
   780  		assert.Contains(t, err.Error(), "cannot read tenant from context")
   781  	})
   782  }
   783  
   784  func TestService_ListByApplicationTemplateVersionID(t *testing.T) {
   785  	// GIVEN
   786  	testErr := errors.New("Test error")
   787  
   788  	products := []*model.Product{
   789  		fixProductModelForAppTemplateVersion(),
   790  		fixProductModelForAppTemplateVersion(),
   791  	}
   792  
   793  	ctx := context.TODO()
   794  
   795  	testCases := []struct {
   796  		Name               string
   797  		PageSize           int
   798  		RepositoryFn       func() *automock.ProductRepository
   799  		ExpectedResult     []*model.Product
   800  		ExpectedErrMessage string
   801  	}{
   802  		{
   803  			Name: "Success",
   804  			RepositoryFn: func() *automock.ProductRepository {
   805  				repo := &automock.ProductRepository{}
   806  				repo.On("ListByResourceID", ctx, "", appTemplateVersionID, resource.ApplicationTemplateVersion).Return(products, nil).Once()
   807  				return repo
   808  			},
   809  			PageSize:           2,
   810  			ExpectedResult:     products,
   811  			ExpectedErrMessage: "",
   812  		},
   813  		{
   814  			Name: "Returns error when APIDefinition listing failed",
   815  			RepositoryFn: func() *automock.ProductRepository {
   816  				repo := &automock.ProductRepository{}
   817  				repo.On("ListByResourceID", ctx, "", appTemplateVersionID, resource.ApplicationTemplateVersion).Return(nil, testErr).Once()
   818  				return repo
   819  			},
   820  			PageSize:           2,
   821  			ExpectedResult:     nil,
   822  			ExpectedErrMessage: testErr.Error(),
   823  		},
   824  	}
   825  
   826  	for _, testCase := range testCases {
   827  		t.Run(testCase.Name, func(t *testing.T) {
   828  			repo := testCase.RepositoryFn()
   829  
   830  			svc := product.NewService(repo, nil)
   831  
   832  			// WHEN
   833  			docs, err := svc.ListByApplicationTemplateVersionID(ctx, appTemplateVersionID)
   834  
   835  			// then
   836  			if testCase.ExpectedErrMessage == "" {
   837  				require.NoError(t, err)
   838  				assert.Equal(t, testCase.ExpectedResult, docs)
   839  			} else {
   840  				require.Error(t, err)
   841  				assert.Contains(t, err.Error(), testCase.ExpectedErrMessage)
   842  			}
   843  
   844  			repo.AssertExpectations(t)
   845  		})
   846  	}
   847  	t.Run("Error when tenant not in context", func(t *testing.T) {
   848  		svc := product.NewService(nil, nil)
   849  		// WHEN
   850  		_, err := svc.ListByApplicationID(context.TODO(), "")
   851  		// THEN
   852  		require.Error(t, err)
   853  		assert.Contains(t, err.Error(), "cannot read tenant from context")
   854  	})
   855  }
   856  
   857  func TestService_ListGlobal(t *testing.T) {
   858  	// GIVEN
   859  	testErr := errors.New("Test error")
   860  
   861  	products := []*model.Product{
   862  		fixGlobalProductModel(),
   863  		fixGlobalProductModel(),
   864  		fixGlobalProductModel(),
   865  	}
   866  
   867  	ctx := context.TODO()
   868  
   869  	testCases := []struct {
   870  		Name               string
   871  		PageSize           int
   872  		RepositoryFn       func() *automock.ProductRepository
   873  		ExpectedResult     []*model.Product
   874  		ExpectedErrMessage string
   875  	}{
   876  		{
   877  			Name: "Success",
   878  			RepositoryFn: func() *automock.ProductRepository {
   879  				repo := &automock.ProductRepository{}
   880  				repo.On("ListGlobal", ctx).Return(products, nil).Once()
   881  				return repo
   882  			},
   883  			PageSize:           2,
   884  			ExpectedResult:     products,
   885  			ExpectedErrMessage: "",
   886  		},
   887  		{
   888  			Name: "Returns error when APIDefinition listing failed",
   889  			RepositoryFn: func() *automock.ProductRepository {
   890  				repo := &automock.ProductRepository{}
   891  				repo.On("ListGlobal", ctx).Return(nil, testErr).Once()
   892  				return repo
   893  			},
   894  			PageSize:           2,
   895  			ExpectedResult:     nil,
   896  			ExpectedErrMessage: testErr.Error(),
   897  		},
   898  	}
   899  
   900  	for _, testCase := range testCases {
   901  		t.Run(testCase.Name, func(t *testing.T) {
   902  			repo := testCase.RepositoryFn()
   903  
   904  			svc := product.NewService(repo, nil)
   905  
   906  			// WHEN
   907  			docs, err := svc.ListGlobal(ctx)
   908  
   909  			// then
   910  			if testCase.ExpectedErrMessage == "" {
   911  				require.NoError(t, err)
   912  				assert.Equal(t, testCase.ExpectedResult, docs)
   913  			} else {
   914  				require.Error(t, err)
   915  				assert.Contains(t, err.Error(), testCase.ExpectedErrMessage)
   916  			}
   917  
   918  			repo.AssertExpectations(t)
   919  		})
   920  	}
   921  }
   922  
   923  func fixUIDService() *automock.UIDService {
   924  	svc := &automock.UIDService{}
   925  	svc.On("Generate").Return(productID).Once()
   926  	return svc
   927  }