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

     1  package api_test
     2  
     3  import (
     4  	"context"
     5  	"github.com/kyma-incubator/compass/components/director/internal/uid"
     6  	"testing"
     7  	"time"
     8  
     9  	"github.com/kyma-incubator/compass/components/director/pkg/str"
    10  
    11  	"github.com/kyma-incubator/compass/components/director/internal/domain/api"
    12  	"github.com/kyma-incubator/compass/components/director/internal/domain/api/automock"
    13  	"github.com/kyma-incubator/compass/components/director/internal/domain/tenant"
    14  	"github.com/kyma-incubator/compass/components/director/internal/model"
    15  	"github.com/kyma-incubator/compass/components/director/pkg/apperrors"
    16  	"github.com/kyma-incubator/compass/components/director/pkg/pagination"
    17  	"github.com/kyma-incubator/compass/components/director/pkg/resource"
    18  	"github.com/pkg/errors"
    19  	"github.com/stretchr/testify/assert"
    20  	"github.com/stretchr/testify/mock"
    21  	"github.com/stretchr/testify/require"
    22  )
    23  
    24  func TestService_Get(t *testing.T) {
    25  	// GIVEN
    26  	testErr := errors.New("Test error")
    27  
    28  	id := "foo"
    29  	name := "foo"
    30  	desc := "bar"
    31  
    32  	apiDefinition := fixAPIDefinitionModel(id, name, desc)
    33  
    34  	ctx := context.TODO()
    35  	ctx = tenant.SaveToContext(ctx, tenantID, externalTenantID)
    36  
    37  	testCases := []struct {
    38  		Name               string
    39  		RepositoryFn       func() *automock.APIRepository
    40  		Input              model.APIDefinitionInput
    41  		InputID            string
    42  		ExpectedDocument   *model.APIDefinition
    43  		ExpectedErrMessage string
    44  	}{
    45  		{
    46  			Name: "Success",
    47  			RepositoryFn: func() *automock.APIRepository {
    48  				repo := &automock.APIRepository{}
    49  				repo.On("GetByID", ctx, tenantID, id).Return(apiDefinition, nil).Once()
    50  				return repo
    51  			},
    52  			InputID:            id,
    53  			ExpectedDocument:   apiDefinition,
    54  			ExpectedErrMessage: "",
    55  		},
    56  		{
    57  			Name: "Returns error when APIDefinition retrieval failed",
    58  			RepositoryFn: func() *automock.APIRepository {
    59  				repo := &automock.APIRepository{}
    60  				repo.On("GetByID", ctx, tenantID, id).Return(nil, testErr).Once()
    61  				return repo
    62  			},
    63  			InputID:            id,
    64  			ExpectedDocument:   apiDefinition,
    65  			ExpectedErrMessage: testErr.Error(),
    66  		},
    67  	}
    68  
    69  	for _, testCase := range testCases {
    70  		t.Run(testCase.Name, func(t *testing.T) {
    71  			repo := testCase.RepositoryFn()
    72  			svc := api.NewService(repo, nil, nil, nil)
    73  
    74  			// WHEN
    75  			document, err := svc.Get(ctx, testCase.InputID)
    76  
    77  			// THEN
    78  			if testCase.ExpectedErrMessage == "" {
    79  				require.NoError(t, err)
    80  				assert.Equal(t, testCase.ExpectedDocument, document)
    81  			} else {
    82  				require.Error(t, err)
    83  				assert.Contains(t, err.Error(), testCase.ExpectedErrMessage)
    84  			}
    85  
    86  			repo.AssertExpectations(t)
    87  		})
    88  	}
    89  	t.Run("Error when tenant not in context", func(t *testing.T) {
    90  		svc := api.NewService(nil, nil, nil, nil)
    91  		// WHEN
    92  		_, err := svc.Get(context.TODO(), "")
    93  		// THEN
    94  		require.Error(t, err)
    95  		assert.Contains(t, err.Error(), "cannot read tenant from context")
    96  	})
    97  }
    98  
    99  func TestService_GetForBundle(t *testing.T) {
   100  	// GIVEN
   101  	testErr := errors.New("Test error")
   102  
   103  	id := "foo"
   104  	bndlID := "foobar"
   105  	name := "foo"
   106  	desc := "bar"
   107  
   108  	apiDefinition := fixAPIDefinitionModel(id, name, desc)
   109  
   110  	ctx := context.TODO()
   111  	ctx = tenant.SaveToContext(ctx, tenantID, externalTenantID)
   112  
   113  	testCases := []struct {
   114  		Name               string
   115  		RepositoryFn       func() *automock.APIRepository
   116  		Input              model.APIDefinitionInput
   117  		InputID            string
   118  		BundleID           string
   119  		ExpectedAPI        *model.APIDefinition
   120  		ExpectedErrMessage string
   121  	}{
   122  		{
   123  			Name: "Success",
   124  			RepositoryFn: func() *automock.APIRepository {
   125  				repo := &automock.APIRepository{}
   126  				repo.On("GetForBundle", ctx, tenantID, id, bndlID).Return(apiDefinition, nil).Once()
   127  				return repo
   128  			},
   129  			InputID:            id,
   130  			BundleID:           bndlID,
   131  			ExpectedAPI:        apiDefinition,
   132  			ExpectedErrMessage: "",
   133  		},
   134  		{
   135  			Name: "Returns error when APIDefinition retrieval failed",
   136  			RepositoryFn: func() *automock.APIRepository {
   137  				repo := &automock.APIRepository{}
   138  				repo.On("GetForBundle", ctx, tenantID, id, bndlID).Return(nil, testErr).Once()
   139  				return repo
   140  			},
   141  			InputID:            id,
   142  			BundleID:           bndlID,
   143  			ExpectedAPI:        nil,
   144  			ExpectedErrMessage: testErr.Error(),
   145  		},
   146  	}
   147  
   148  	for _, testCase := range testCases {
   149  		t.Run(testCase.Name, func(t *testing.T) {
   150  			repo := testCase.RepositoryFn()
   151  			svc := api.NewService(repo, nil, nil, nil)
   152  
   153  			// WHEN
   154  			api, err := svc.GetForBundle(ctx, testCase.InputID, testCase.BundleID)
   155  
   156  			// THEN
   157  			if testCase.ExpectedErrMessage == "" {
   158  				require.NoError(t, err)
   159  				assert.Equal(t, testCase.ExpectedAPI, api)
   160  			} else {
   161  				require.Error(t, err)
   162  				assert.Contains(t, err.Error(), testCase.ExpectedErrMessage)
   163  			}
   164  
   165  			repo.AssertExpectations(t)
   166  		})
   167  	}
   168  	t.Run("Error when tenant not in context", func(t *testing.T) {
   169  		svc := api.NewService(nil, nil, nil, nil)
   170  		// WHEN
   171  		_, err := svc.GetForBundle(context.TODO(), "", "")
   172  		// THEN
   173  		require.Error(t, err)
   174  		assert.Contains(t, err.Error(), "cannot read tenant from context")
   175  	})
   176  }
   177  
   178  func TestService_ListByBundleIDs(t *testing.T) {
   179  	// GIVEN
   180  	testErr := errors.New("Test error")
   181  
   182  	firstAPIDefID := "foo"
   183  	secondAPIDefID := "foo2"
   184  	firstBundleID := "bar"
   185  	secondBundleID := "bar2"
   186  	name := "foo"
   187  	targetURL := "https://test.com"
   188  	numberOfAPIsInFirstBundle := 1
   189  	numberOfAPIsInSecondBundle := 1
   190  	bundleIDs := []string{firstBundleID, secondBundleID}
   191  
   192  	apiDefFirstBundle := fixAPIDefinitionModel(firstAPIDefID, name, targetURL)
   193  	apiDefSecondBundle := fixAPIDefinitionModel(secondAPIDefID, name, targetURL)
   194  
   195  	apiDefFirstBundleReference := fixModelBundleReference(firstBundleID, firstAPIDefID)
   196  	apiDefSecondBundleReference := fixModelBundleReference(secondBundleID, secondAPIDefID)
   197  	bundleRefs := []*model.BundleReference{apiDefFirstBundleReference, apiDefSecondBundleReference}
   198  	totalCounts := map[string]int{firstBundleID: numberOfAPIsInFirstBundle, secondBundleID: numberOfAPIsInSecondBundle}
   199  
   200  	apiDefsFirstBundle := []*model.APIDefinition{apiDefFirstBundle}
   201  	apiDefsSecondBundle := []*model.APIDefinition{apiDefSecondBundle}
   202  
   203  	apiDefPageFirstBundle := &model.APIDefinitionPage{
   204  		Data:       apiDefsFirstBundle,
   205  		TotalCount: len(apiDefsFirstBundle),
   206  		PageInfo: &pagination.Page{
   207  			HasNextPage: false,
   208  			EndCursor:   "end",
   209  			StartCursor: "start",
   210  		},
   211  	}
   212  	apiDefPageSecondBundle := &model.APIDefinitionPage{
   213  		Data:       apiDefsSecondBundle,
   214  		TotalCount: len(apiDefsSecondBundle),
   215  		PageInfo: &pagination.Page{
   216  			HasNextPage: false,
   217  			EndCursor:   "end",
   218  			StartCursor: "start",
   219  		},
   220  	}
   221  
   222  	apiDefPages := []*model.APIDefinitionPage{apiDefPageFirstBundle, apiDefPageSecondBundle}
   223  
   224  	after := "test"
   225  
   226  	ctx := context.TODO()
   227  	ctx = tenant.SaveToContext(ctx, tenantID, externalTenantID)
   228  
   229  	testCases := []struct {
   230  		Name               string
   231  		PageSize           int
   232  		RepositoryFn       func() *automock.APIRepository
   233  		BundleRefSvcFn     func() *automock.BundleReferenceService
   234  		ExpectedResult     []*model.APIDefinitionPage
   235  		ExpectedErrMessage string
   236  	}{
   237  		{
   238  			Name: "Success",
   239  			BundleRefSvcFn: func() *automock.BundleReferenceService {
   240  				svc := &automock.BundleReferenceService{}
   241  				svc.On("ListByBundleIDs", ctx, model.BundleAPIReference, bundleIDs, 2, after).Return(bundleRefs, totalCounts, nil).Once()
   242  				return svc
   243  			},
   244  			RepositoryFn: func() *automock.APIRepository {
   245  				repo := &automock.APIRepository{}
   246  				repo.On("ListByBundleIDs", ctx, tenantID, bundleIDs, bundleRefs, totalCounts, 2, after).Return(apiDefPages, nil).Once()
   247  				return repo
   248  			},
   249  			PageSize:           2,
   250  			ExpectedResult:     apiDefPages,
   251  			ExpectedErrMessage: "",
   252  		},
   253  		{
   254  			Name: "Return error when page size is less than 1",
   255  			BundleRefSvcFn: func() *automock.BundleReferenceService {
   256  				svc := &automock.BundleReferenceService{}
   257  				return svc
   258  			},
   259  			RepositoryFn: func() *automock.APIRepository {
   260  				repo := &automock.APIRepository{}
   261  				return repo
   262  			},
   263  			PageSize:           0,
   264  			ExpectedResult:     apiDefPages,
   265  			ExpectedErrMessage: "page size must be between 1 and 200",
   266  		},
   267  		{
   268  			Name: "Return error when page size is bigger than 200",
   269  			BundleRefSvcFn: func() *automock.BundleReferenceService {
   270  				svc := &automock.BundleReferenceService{}
   271  				return svc
   272  			},
   273  			RepositoryFn: func() *automock.APIRepository {
   274  				repo := &automock.APIRepository{}
   275  				return repo
   276  			},
   277  			PageSize:           201,
   278  			ExpectedResult:     apiDefPages,
   279  			ExpectedErrMessage: "page size must be between 1 and 200",
   280  		},
   281  		{
   282  			Name: "Returns error when APIDefinition BundleReferences listing failed",
   283  			BundleRefSvcFn: func() *automock.BundleReferenceService {
   284  				svc := &automock.BundleReferenceService{}
   285  				svc.On("ListByBundleIDs", ctx, model.BundleAPIReference, bundleIDs, 2, after).Return(nil, nil, testErr).Once()
   286  				return svc
   287  			},
   288  			RepositoryFn: func() *automock.APIRepository {
   289  				repo := &automock.APIRepository{}
   290  				return repo
   291  			},
   292  			PageSize:           2,
   293  			ExpectedResult:     nil,
   294  			ExpectedErrMessage: testErr.Error(),
   295  		},
   296  		{
   297  			Name: "Returns error when APIDefinition listing failed",
   298  			BundleRefSvcFn: func() *automock.BundleReferenceService {
   299  				svc := &automock.BundleReferenceService{}
   300  				svc.On("ListByBundleIDs", ctx, model.BundleAPIReference, bundleIDs, 2, after).Return(bundleRefs, totalCounts, nil).Once()
   301  				return svc
   302  			},
   303  			RepositoryFn: func() *automock.APIRepository {
   304  				repo := &automock.APIRepository{}
   305  				repo.On("ListByBundleIDs", ctx, tenantID, bundleIDs, bundleRefs, totalCounts, 2, after).Return(nil, testErr).Once()
   306  				return repo
   307  			},
   308  			PageSize:           2,
   309  			ExpectedResult:     nil,
   310  			ExpectedErrMessage: testErr.Error(),
   311  		},
   312  	}
   313  
   314  	for _, testCase := range testCases {
   315  		t.Run(testCase.Name, func(t *testing.T) {
   316  			repo := testCase.RepositoryFn()
   317  			bndlRefSvc := testCase.BundleRefSvcFn()
   318  
   319  			svc := api.NewService(repo, nil, nil, bndlRefSvc)
   320  
   321  			// WHEN
   322  			apiDefs, err := svc.ListByBundleIDs(ctx, bundleIDs, testCase.PageSize, after)
   323  
   324  			// THEN
   325  			if testCase.ExpectedErrMessage == "" {
   326  				require.NoError(t, err)
   327  				assert.Equal(t, testCase.ExpectedResult, apiDefs)
   328  			} else {
   329  				require.Error(t, err)
   330  				assert.Contains(t, err.Error(), testCase.ExpectedErrMessage)
   331  			}
   332  
   333  			repo.AssertExpectations(t)
   334  		})
   335  	}
   336  	t.Run("Error when tenant not in context", func(t *testing.T) {
   337  		svc := api.NewService(nil, nil, nil, nil)
   338  		// WHEN
   339  		_, err := svc.ListByBundleIDs(context.TODO(), nil, 5, "")
   340  		// THEN
   341  		require.Error(t, err)
   342  		assert.Contains(t, err.Error(), "cannot read tenant from context")
   343  	})
   344  }
   345  
   346  func TestService_ListByApplicationID(t *testing.T) {
   347  	// GIVEN
   348  	testErr := errors.New("Test error")
   349  
   350  	id := "foo"
   351  	name := "foo"
   352  	desc := "bar"
   353  
   354  	apiDefinitions := []*model.APIDefinition{
   355  		fixAPIDefinitionModel(id, name, desc),
   356  		fixAPIDefinitionModel(id, name, desc),
   357  		fixAPIDefinitionModel(id, name, desc),
   358  	}
   359  
   360  	ctx := context.TODO()
   361  	ctx = tenant.SaveToContext(ctx, tenantID, externalTenantID)
   362  
   363  	testCases := []struct {
   364  		Name               string
   365  		RepositoryFn       func() *automock.APIRepository
   366  		ExpectedResult     []*model.APIDefinition
   367  		ExpectedErrMessage string
   368  	}{
   369  		{
   370  			Name: "Success",
   371  			RepositoryFn: func() *automock.APIRepository {
   372  				repo := &automock.APIRepository{}
   373  				repo.On("ListByResourceID", ctx, tenantID, resource.Application, appID).Return(apiDefinitions, nil).Once()
   374  				return repo
   375  			},
   376  			ExpectedResult:     apiDefinitions,
   377  			ExpectedErrMessage: "",
   378  		},
   379  		{
   380  			Name: "Returns error when APIDefinition listing failed",
   381  			RepositoryFn: func() *automock.APIRepository {
   382  				repo := &automock.APIRepository{}
   383  				repo.On("ListByResourceID", ctx, tenantID, resource.Application, appID).Return(nil, testErr).Once()
   384  				return repo
   385  			},
   386  			ExpectedResult:     nil,
   387  			ExpectedErrMessage: testErr.Error(),
   388  		},
   389  	}
   390  
   391  	for _, testCase := range testCases {
   392  		t.Run(testCase.Name, func(t *testing.T) {
   393  			repo := testCase.RepositoryFn()
   394  
   395  			svc := api.NewService(repo, nil, nil, nil)
   396  
   397  			// WHEN
   398  			docs, err := svc.ListByApplicationID(ctx, appID)
   399  
   400  			// THEN
   401  			if testCase.ExpectedErrMessage == "" {
   402  				require.NoError(t, err)
   403  				assert.Equal(t, testCase.ExpectedResult, docs)
   404  			} else {
   405  				require.Error(t, err)
   406  				assert.Contains(t, err.Error(), testCase.ExpectedErrMessage)
   407  			}
   408  
   409  			repo.AssertExpectations(t)
   410  		})
   411  	}
   412  	t.Run("Error when tenant not in context", func(t *testing.T) {
   413  		svc := api.NewService(nil, nil, nil, nil)
   414  		// WHEN
   415  		_, err := svc.ListByApplicationID(context.TODO(), "")
   416  		// THEN
   417  		require.Error(t, err)
   418  		assert.Contains(t, err.Error(), "cannot read tenant from context")
   419  	})
   420  }
   421  
   422  func TestService_ListByApplicationTemplateVersionID(t *testing.T) {
   423  	// GIVEN
   424  	testErr := errors.New("Test error")
   425  
   426  	id := "foo"
   427  	name := "foo"
   428  	desc := "bar"
   429  
   430  	apiDefinitions := []*model.APIDefinition{
   431  		fixAPIDefinitionModel(id, name, desc),
   432  		fixAPIDefinitionModel(id, name, desc),
   433  		fixAPIDefinitionModel(id, name, desc),
   434  	}
   435  
   436  	ctx := context.TODO()
   437  	ctx = tenant.SaveToContext(ctx, tenantID, externalTenantID)
   438  
   439  	testCases := []struct {
   440  		Name               string
   441  		RepositoryFn       func() *automock.APIRepository
   442  		ExpectedResult     []*model.APIDefinition
   443  		ExpectedErrMessage string
   444  	}{
   445  		{
   446  			Name: "Success",
   447  			RepositoryFn: func() *automock.APIRepository {
   448  				repo := &automock.APIRepository{}
   449  				repo.On("ListByResourceID", ctx, "", resource.ApplicationTemplateVersion, appTemplateVersionID).Return(apiDefinitions, nil).Once()
   450  				return repo
   451  			},
   452  			ExpectedResult:     apiDefinitions,
   453  			ExpectedErrMessage: "",
   454  		},
   455  		{
   456  			Name: "Returns error when APIDefinition listing failed",
   457  			RepositoryFn: func() *automock.APIRepository {
   458  				repo := &automock.APIRepository{}
   459  				repo.On("ListByResourceID", ctx, "", resource.ApplicationTemplateVersion, appTemplateVersionID).Return(nil, testErr).Once()
   460  				return repo
   461  			},
   462  			ExpectedResult:     nil,
   463  			ExpectedErrMessage: testErr.Error(),
   464  		},
   465  	}
   466  
   467  	for _, testCase := range testCases {
   468  		t.Run(testCase.Name, func(t *testing.T) {
   469  			repo := testCase.RepositoryFn()
   470  
   471  			svc := api.NewService(repo, nil, nil, nil)
   472  
   473  			// WHEN
   474  			docs, err := svc.ListByApplicationTemplateVersionID(ctx, appTemplateVersionID)
   475  
   476  			// THEN
   477  			if testCase.ExpectedErrMessage == "" {
   478  				require.NoError(t, err)
   479  				assert.Equal(t, testCase.ExpectedResult, docs)
   480  			} else {
   481  				require.Error(t, err)
   482  				assert.Contains(t, err.Error(), testCase.ExpectedErrMessage)
   483  			}
   484  
   485  			repo.AssertExpectations(t)
   486  		})
   487  	}
   488  	t.Run("Error when tenant not in context", func(t *testing.T) {
   489  		svc := api.NewService(nil, nil, nil, nil)
   490  		// WHEN
   491  		_, err := svc.ListByApplicationID(context.TODO(), "")
   492  		// THEN
   493  		require.Error(t, err)
   494  		assert.Contains(t, err.Error(), "cannot read tenant from context")
   495  	})
   496  }
   497  
   498  func TestService_ListByApplicationIDPage(t *testing.T) {
   499  	// GIVEN
   500  	testErr := errors.New("Test error")
   501  
   502  	id := "foo"
   503  	name := "foo"
   504  	desc := "bar"
   505  	pageSize := 1
   506  	invalidPageSize := 0
   507  	cursor := ""
   508  
   509  	apiDefinitions := []*model.APIDefinition{
   510  		fixAPIDefinitionModel(id, name, desc),
   511  		fixAPIDefinitionModel(id, name, desc),
   512  		fixAPIDefinitionModel(id, name, desc),
   513  	}
   514  
   515  	apiDefinitionsPage := &model.APIDefinitionPage{
   516  		Data: apiDefinitions,
   517  		PageInfo: &pagination.Page{
   518  			StartCursor: "",
   519  			EndCursor:   "",
   520  			HasNextPage: false,
   521  		},
   522  		TotalCount: len(apiDefinitions),
   523  	}
   524  
   525  	ctx := context.TODO()
   526  	ctx = tenant.SaveToContext(ctx, tenantID, externalTenantID)
   527  
   528  	testCases := []struct {
   529  		Name               string
   530  		PageSize           int
   531  		RepositoryFn       func() *automock.APIRepository
   532  		ExpectedResult     *model.APIDefinitionPage
   533  		ExpectedErrMessage string
   534  	}{
   535  		{
   536  			Name:     "Success",
   537  			PageSize: pageSize,
   538  			RepositoryFn: func() *automock.APIRepository {
   539  				repo := &automock.APIRepository{}
   540  				repo.On("ListByApplicationIDPage", ctx, tenantID, appID, pageSize, cursor).Return(apiDefinitionsPage, nil).Once()
   541  				return repo
   542  			},
   543  			ExpectedResult:     apiDefinitionsPage,
   544  			ExpectedErrMessage: "",
   545  		},
   546  		{
   547  			Name:     "Returns error when APIDefinition paging failed",
   548  			PageSize: pageSize,
   549  			RepositoryFn: func() *automock.APIRepository {
   550  				repo := &automock.APIRepository{}
   551  				repo.On("ListByApplicationIDPage", ctx, tenantID, appID, pageSize, cursor).Return(nil, testErr).Once()
   552  				return repo
   553  			},
   554  			ExpectedResult:     nil,
   555  			ExpectedErrMessage: testErr.Error(),
   556  		},
   557  		{
   558  			Name:     "Returns error when pageSize is out of the range 1-200",
   559  			PageSize: invalidPageSize,
   560  			RepositoryFn: func() *automock.APIRepository {
   561  				repo := &automock.APIRepository{}
   562  				return repo
   563  			},
   564  			ExpectedResult:     nil,
   565  			ExpectedErrMessage: "page size must be between 1 and 200",
   566  		},
   567  	}
   568  
   569  	for _, testCase := range testCases {
   570  		t.Run(testCase.Name, func(t *testing.T) {
   571  			repo := testCase.RepositoryFn()
   572  
   573  			svc := api.NewService(repo, nil, nil, nil)
   574  
   575  			// WHEN
   576  			docs, err := svc.ListByApplicationIDPage(ctx, appID, testCase.PageSize, cursor)
   577  
   578  			// THEN
   579  			if testCase.ExpectedErrMessage == "" {
   580  				require.NoError(t, err)
   581  				assert.Equal(t, testCase.ExpectedResult, docs)
   582  			} else {
   583  				require.Error(t, err)
   584  				assert.Contains(t, err.Error(), testCase.ExpectedErrMessage)
   585  			}
   586  
   587  			repo.AssertExpectations(t)
   588  		})
   589  	}
   590  	t.Run("Error when tenant not in context", func(t *testing.T) {
   591  		svc := api.NewService(nil, nil, nil, nil)
   592  		// WHEN
   593  		_, err := svc.ListByApplicationID(context.TODO(), "")
   594  		// THEN
   595  		require.Error(t, err)
   596  		assert.Contains(t, err.Error(), "cannot read tenant from context")
   597  	})
   598  }
   599  
   600  func TestService_Create(t *testing.T) {
   601  	// GIVEN
   602  	testErr := errors.New("Test error")
   603  
   604  	id := "foo"
   605  	bundleID := "bndlid"
   606  	bundleID2 := "bndlid2"
   607  	name := "Foo"
   608  	targetURL2 := "https://test2-url.com"
   609  
   610  	fixedTimestamp := time.Now()
   611  	frURL := "foo.bar"
   612  	spec := "test"
   613  	spec2 := "test2"
   614  	isDefaultBundle := true
   615  
   616  	modelInput := model.APIDefinitionInput{
   617  		Name:         name,
   618  		TargetURLs:   api.ConvertTargetURLToJSONArray(targetURL),
   619  		VersionInput: &model.VersionInput{},
   620  	}
   621  
   622  	modelSpecsInput := []*model.SpecInput{
   623  		{
   624  			Data: &spec,
   625  			FetchRequest: &model.FetchRequestInput{
   626  				URL: frURL,
   627  			},
   628  		},
   629  		{
   630  			Data: &spec2,
   631  			FetchRequest: &model.FetchRequestInput{
   632  				URL: frURL,
   633  			},
   634  		},
   635  	}
   636  
   637  	modelAPIDefinitionForApp := fixAPIDefinitionWithPackageModel(id, name)
   638  	modelAPIDefinitionForApp.ApplicationID = &appID
   639  
   640  	modelAPIDefinitionForAppTemplateVersion := fixAPIDefinitionWithPackageModel(id, name)
   641  	modelAPIDefinitionForAppTemplateVersion.ApplicationTemplateVersionID = &appTemplateVersionID
   642  
   643  	bundleReferenceInput := &model.BundleReferenceInput{
   644  		APIDefaultTargetURL: str.Ptr(api.ExtractTargetURLFromJSONArray(modelAPIDefinitionForApp.TargetURLs)),
   645  	}
   646  	secondBundleReferenceInput := &model.BundleReferenceInput{
   647  		APIDefaultTargetURL: &targetURL2,
   648  	}
   649  
   650  	bundleReferenceInputWithDefaultBundle := &model.BundleReferenceInput{
   651  		APIDefaultTargetURL: str.Ptr(api.ExtractTargetURLFromJSONArray(modelAPIDefinitionForApp.TargetURLs)),
   652  		IsDefaultBundle:     &isDefaultBundle,
   653  	}
   654  
   655  	singleDefaultTargetURLPerBundle := map[string]string{bundleID: targetURL}
   656  	defaultTargetURLPerBundle := map[string]string{bundleID: targetURL, bundleID2: targetURL2}
   657  
   658  	ctx := context.TODO()
   659  	ctxWithTenant := tenant.SaveToContext(ctx, tenantID, externalTenantID)
   660  
   661  	testCases := []struct {
   662  		Name                      string
   663  		RepositoryFn              func() *automock.APIRepository
   664  		UIDServiceFn              func() *automock.UIDService
   665  		SpecServiceFn             func() *automock.SpecService
   666  		BundleReferenceFn         func() *automock.BundleReferenceService
   667  		Input                     model.APIDefinitionInput
   668  		SpecsInput                []*model.SpecInput
   669  		ResourceType              resource.Type
   670  		ResourceID                string
   671  		DefaultTargetURLPerBundle map[string]string
   672  		DefaultBundleID           string
   673  		Ctx                       context.Context
   674  		ExpectedErr               error
   675  	}{
   676  		{
   677  			Name: "Success for application",
   678  			RepositoryFn: func() *automock.APIRepository {
   679  				repo := &automock.APIRepository{}
   680  				repo.On("Create", ctxWithTenant, tenantID, modelAPIDefinitionForApp).Return(nil).Once()
   681  				return repo
   682  			},
   683  			UIDServiceFn: fixUIDService(id),
   684  			SpecServiceFn: func() *automock.SpecService {
   685  				svc := &automock.SpecService{}
   686  				svc.On("CreateByReferenceObjectID", ctxWithTenant, *modelSpecsInput[0], resource.Application, model.APISpecReference, id).Return("id", nil).Once()
   687  				svc.On("CreateByReferenceObjectID", ctxWithTenant, *modelSpecsInput[1], resource.Application, model.APISpecReference, id).Return("id", nil).Once()
   688  				return svc
   689  			},
   690  			BundleReferenceFn: func() *automock.BundleReferenceService {
   691  				svc := &automock.BundleReferenceService{}
   692  				svc.On("CreateByReferenceObjectID", ctxWithTenant, *bundleReferenceInput, model.BundleAPIReference, str.Ptr(id), str.Ptr(bundleID)).Return(nil).Once()
   693  				return svc
   694  			},
   695  			ResourceType: resource.Application,
   696  			ResourceID:   appID,
   697  			Input:        modelInput,
   698  			SpecsInput:   modelSpecsInput,
   699  		},
   700  		{
   701  			Name: "Success for application template version",
   702  			RepositoryFn: func() *automock.APIRepository {
   703  				repo := &automock.APIRepository{}
   704  				repo.On("CreateGlobal", ctx, modelAPIDefinitionForAppTemplateVersion).Return(nil).Once()
   705  				return repo
   706  			},
   707  			UIDServiceFn: fixUIDService(id),
   708  			SpecServiceFn: func() *automock.SpecService {
   709  				svc := &automock.SpecService{}
   710  				svc.On("CreateByReferenceObjectID", ctx, *modelSpecsInput[0], resource.ApplicationTemplateVersion, model.APISpecReference, id).Return("id", nil).Once()
   711  				svc.On("CreateByReferenceObjectID", ctx, *modelSpecsInput[1], resource.ApplicationTemplateVersion, model.APISpecReference, id).Return("id", nil).Once()
   712  				return svc
   713  			},
   714  			BundleReferenceFn: func() *automock.BundleReferenceService {
   715  				svc := &automock.BundleReferenceService{}
   716  				svc.On("CreateByReferenceObjectID", ctx, *bundleReferenceInput, model.BundleAPIReference, str.Ptr(id), str.Ptr(bundleID)).Return(nil).Once()
   717  				return svc
   718  			},
   719  			ResourceType: resource.ApplicationTemplateVersion,
   720  			ResourceID:   appTemplateVersionID,
   721  			Input:        modelInput,
   722  			Ctx:          ctx,
   723  			SpecsInput:   modelSpecsInput,
   724  		},
   725  		{
   726  			Name: "Success in ORD scenario where defaultTargetURLPerBundle map is passed",
   727  			RepositoryFn: func() *automock.APIRepository {
   728  				repo := &automock.APIRepository{}
   729  				repo.On("Create", ctxWithTenant, tenantID, modelAPIDefinitionForApp).Return(nil).Once()
   730  				return repo
   731  			},
   732  			UIDServiceFn: fixUIDService(id),
   733  			SpecServiceFn: func() *automock.SpecService {
   734  				svc := &automock.SpecService{}
   735  				svc.On("CreateByReferenceObjectID", ctxWithTenant, *modelSpecsInput[0], resource.Application, model.APISpecReference, id).Return("id", nil).Once()
   736  				svc.On("CreateByReferenceObjectID", ctxWithTenant, *modelSpecsInput[1], resource.Application, model.APISpecReference, id).Return("id", nil).Once()
   737  				return svc
   738  			},
   739  			BundleReferenceFn: func() *automock.BundleReferenceService {
   740  				svc := &automock.BundleReferenceService{}
   741  				svc.On("CreateByReferenceObjectID", ctxWithTenant, *bundleReferenceInputWithDefaultBundle, model.BundleAPIReference, str.Ptr(id), str.Ptr(bundleID)).Return(nil).Once()
   742  				svc.On("CreateByReferenceObjectID", ctxWithTenant, *secondBundleReferenceInput, model.BundleAPIReference, str.Ptr(id), str.Ptr(bundleID2)).Return(nil).Once()
   743  				return svc
   744  			},
   745  			ResourceType:              resource.Application,
   746  			ResourceID:                appID,
   747  			Input:                     modelInput,
   748  			SpecsInput:                modelSpecsInput,
   749  			DefaultTargetURLPerBundle: defaultTargetURLPerBundle,
   750  			DefaultBundleID:           bundleID,
   751  		},
   752  		{
   753  			Name: "Error - API Creation",
   754  			RepositoryFn: func() *automock.APIRepository {
   755  				repo := &automock.APIRepository{}
   756  				repo.On("Create", ctxWithTenant, tenantID, modelAPIDefinitionForApp).Return(testErr).Once()
   757  				return repo
   758  			},
   759  			UIDServiceFn:  fixUIDService(id),
   760  			SpecServiceFn: emptySpecService,
   761  			BundleReferenceFn: func() *automock.BundleReferenceService {
   762  				return &automock.BundleReferenceService{}
   763  			},
   764  			ResourceType: resource.Application,
   765  			ResourceID:   appID,
   766  			Input:        modelInput,
   767  			SpecsInput:   modelSpecsInput,
   768  			ExpectedErr:  testErr,
   769  		},
   770  		{
   771  			Name: "Error - API Creation for Application Template Version",
   772  			RepositoryFn: func() *automock.APIRepository {
   773  				repo := &automock.APIRepository{}
   774  				repo.On("CreateGlobal", ctx, modelAPIDefinitionForAppTemplateVersion).Return(testErr).Once()
   775  				return repo
   776  			},
   777  			UIDServiceFn:  fixUIDService(id),
   778  			SpecServiceFn: emptySpecService,
   779  			BundleReferenceFn: func() *automock.BundleReferenceService {
   780  				return &automock.BundleReferenceService{}
   781  			},
   782  			ResourceType: resource.ApplicationTemplateVersion,
   783  			ResourceID:   appTemplateVersionID,
   784  			Input:        modelInput,
   785  			Ctx:          ctx,
   786  			SpecsInput:   modelSpecsInput,
   787  			ExpectedErr:  testErr,
   788  		},
   789  		{
   790  			Name: "Error - Spec Creation",
   791  			RepositoryFn: func() *automock.APIRepository {
   792  				repo := &automock.APIRepository{}
   793  				repo.On("Create", ctxWithTenant, tenantID, modelAPIDefinitionForApp).Return(nil).Once()
   794  				return repo
   795  			},
   796  			UIDServiceFn: fixUIDService(id),
   797  			SpecServiceFn: func() *automock.SpecService {
   798  				svc := &automock.SpecService{}
   799  				svc.On("CreateByReferenceObjectID", ctxWithTenant, *modelSpecsInput[0], resource.Application, model.APISpecReference, id).Return("", testErr).Once()
   800  				return svc
   801  			},
   802  			BundleReferenceFn: func() *automock.BundleReferenceService {
   803  				return &automock.BundleReferenceService{}
   804  			},
   805  			ResourceType: resource.Application,
   806  			ResourceID:   appID,
   807  			Input:        modelInput,
   808  			SpecsInput:   modelSpecsInput,
   809  			ExpectedErr:  testErr,
   810  		},
   811  		{
   812  			Name: "Error - BundleReference API Creation",
   813  			RepositoryFn: func() *automock.APIRepository {
   814  				repo := &automock.APIRepository{}
   815  				repo.On("Create", ctxWithTenant, tenantID, modelAPIDefinitionForApp).Return(nil).Once()
   816  				return repo
   817  			},
   818  			UIDServiceFn: fixUIDService(id),
   819  			SpecServiceFn: func() *automock.SpecService {
   820  				svc := &automock.SpecService{}
   821  				svc.On("CreateByReferenceObjectID", ctxWithTenant, *modelSpecsInput[0], resource.Application, model.APISpecReference, id).Return("id", nil).Once()
   822  				svc.On("CreateByReferenceObjectID", ctxWithTenant, *modelSpecsInput[1], resource.Application, model.APISpecReference, id).Return("id", nil).Once()
   823  				return svc
   824  			},
   825  			BundleReferenceFn: func() *automock.BundleReferenceService {
   826  				svc := &automock.BundleReferenceService{}
   827  				svc.On("CreateByReferenceObjectID", ctxWithTenant, *bundleReferenceInput, model.BundleAPIReference, str.Ptr(id), str.Ptr(bundleID)).Return(testErr).Once()
   828  				return svc
   829  			},
   830  			ResourceType: resource.Application,
   831  			ResourceID:   appID,
   832  			Input:        modelInput,
   833  			SpecsInput:   modelSpecsInput,
   834  			ExpectedErr:  testErr,
   835  		},
   836  		{
   837  			Name: "Error in ORD scenario - BundleReference API Creation",
   838  			RepositoryFn: func() *automock.APIRepository {
   839  				repo := &automock.APIRepository{}
   840  				repo.On("Create", ctxWithTenant, tenantID, modelAPIDefinitionForApp).Return(nil).Once()
   841  				return repo
   842  			},
   843  			UIDServiceFn: fixUIDService(id),
   844  			SpecServiceFn: func() *automock.SpecService {
   845  				svc := &automock.SpecService{}
   846  				svc.On("CreateByReferenceObjectID", ctxWithTenant, *modelSpecsInput[0], resource.Application, model.APISpecReference, id).Return("id", nil).Once()
   847  				svc.On("CreateByReferenceObjectID", ctxWithTenant, *modelSpecsInput[1], resource.Application, model.APISpecReference, id).Return("id", nil).Once()
   848  				return svc
   849  			},
   850  			BundleReferenceFn: func() *automock.BundleReferenceService {
   851  				svc := &automock.BundleReferenceService{}
   852  				svc.On("CreateByReferenceObjectID", ctxWithTenant, *bundleReferenceInput, model.BundleAPIReference, str.Ptr(id), str.Ptr(bundleID)).Return(testErr).Once()
   853  				return svc
   854  			},
   855  			ResourceType:              resource.Application,
   856  			ResourceID:                appID,
   857  			Input:                     modelInput,
   858  			SpecsInput:                modelSpecsInput,
   859  			DefaultTargetURLPerBundle: singleDefaultTargetURLPerBundle,
   860  			ExpectedErr:               testErr,
   861  		},
   862  	}
   863  
   864  	for _, testCase := range testCases {
   865  		t.Run(testCase.Name, func(t *testing.T) {
   866  			// GIVEN
   867  			repo := testCase.RepositoryFn()
   868  			uidService := testCase.UIDServiceFn()
   869  			specService := testCase.SpecServiceFn()
   870  			bundleReferenceService := testCase.BundleReferenceFn()
   871  
   872  			svc := api.NewService(repo, uidService, specService, bundleReferenceService)
   873  			svc.SetTimestampGen(func() time.Time { return fixedTimestamp })
   874  
   875  			defaultCtx := ctxWithTenant
   876  			if testCase.Ctx != nil {
   877  				defaultCtx = testCase.Ctx
   878  			}
   879  
   880  			// WHEN
   881  			result, err := svc.Create(defaultCtx, testCase.ResourceType, testCase.ResourceID, &bundleID, str.Ptr(packageID), testCase.Input, testCase.SpecsInput, testCase.DefaultTargetURLPerBundle, 0, testCase.DefaultBundleID)
   882  
   883  			// THEN
   884  			if testCase.ExpectedErr != nil {
   885  				require.Error(t, err)
   886  				assert.Contains(t, err.Error(), testCase.ExpectedErr.Error())
   887  			} else {
   888  				assert.IsType(t, "string", result)
   889  			}
   890  
   891  			repo.AssertExpectations(t)
   892  			specService.AssertExpectations(t)
   893  			uidService.AssertExpectations(t)
   894  			bundleReferenceService.AssertExpectations(t)
   895  		})
   896  	}
   897  	t.Run("Error when tenant not in context", func(t *testing.T) {
   898  		svc := api.NewService(nil, fixUIDService(id)(), nil, nil)
   899  		// WHEN
   900  		_, err := svc.Create(context.TODO(), "", "", nil, nil, model.APIDefinitionInput{}, []*model.SpecInput{}, nil, 0, "")
   901  		// THEN
   902  		require.Error(t, err)
   903  		assert.Contains(t, err.Error(), "cannot read tenant from context")
   904  	})
   905  }
   906  
   907  func TestService_CreateInApplication(t *testing.T) {
   908  	// GIVEN
   909  	testErr := errors.New("Test error")
   910  
   911  	id := "foo"
   912  	name := "Foo"
   913  	targetURL := "https://test-url.com"
   914  
   915  	timestamp := time.Now()
   916  	frURL := "foo.bar"
   917  	spec := "test"
   918  
   919  	modelInput := model.APIDefinitionInput{
   920  		Name:         name,
   921  		TargetURLs:   api.ConvertTargetURLToJSONArray(targetURL),
   922  		VersionInput: &model.VersionInput{},
   923  	}
   924  
   925  	modelSpecInput := &model.SpecInput{
   926  		Data: &spec,
   927  		FetchRequest: &model.FetchRequestInput{
   928  			URL: frURL,
   929  		},
   930  	}
   931  
   932  	modelSpecsInput := []*model.SpecInput{
   933  		modelSpecInput,
   934  	}
   935  
   936  	modelAPIDefinition := &model.APIDefinition{
   937  		ApplicationID: &appID,
   938  		Name:          name,
   939  		TargetURLs:    api.ConvertTargetURLToJSONArray(targetURL),
   940  		Version:       &model.Version{},
   941  		BaseEntity: &model.BaseEntity{
   942  			ID:    id,
   943  			Ready: true,
   944  		},
   945  	}
   946  
   947  	ctx := context.TODO()
   948  	ctx = tenant.SaveToContext(ctx, tenantID, externalTenantID)
   949  
   950  	testCases := []struct {
   951  		Name                      string
   952  		RepositoryFn              func() *automock.APIRepository
   953  		UIDServiceFn              func() *automock.UIDService
   954  		SpecServiceFn             func() *automock.SpecService
   955  		Input                     model.APIDefinitionInput
   956  		SpecsInput                *model.SpecInput
   957  		DefaultTargetURLPerBundle map[string]string
   958  		DefaultBundleID           string
   959  		ExpectedErr               error
   960  	}{
   961  		{
   962  			Name: "Success",
   963  			RepositoryFn: func() *automock.APIRepository {
   964  				repo := &automock.APIRepository{}
   965  				repo.On("Create", ctx, tenantID, modelAPIDefinition).Return(nil).Once()
   966  				return repo
   967  			},
   968  			UIDServiceFn: func() *automock.UIDService {
   969  				svc := &automock.UIDService{}
   970  				svc.On("Generate").Return(id).Once()
   971  				return svc
   972  			},
   973  			SpecServiceFn: func() *automock.SpecService {
   974  				svc := &automock.SpecService{}
   975  				svc.On("CreateByReferenceObjectID", ctx, *modelSpecsInput[0], resource.Application, model.APISpecReference, id).Return("id", nil).Once()
   976  				return svc
   977  			},
   978  			Input:      modelInput,
   979  			SpecsInput: modelSpecInput,
   980  		},
   981  		{
   982  			Name: "Error - API Creation",
   983  			RepositoryFn: func() *automock.APIRepository {
   984  				repo := &automock.APIRepository{}
   985  				repo.On("Create", ctx, tenantID, modelAPIDefinition).Return(testErr).Once()
   986  				return repo
   987  			},
   988  			UIDServiceFn: func() *automock.UIDService {
   989  				svc := &automock.UIDService{}
   990  				svc.On("Generate").Return(id).Once()
   991  				return svc
   992  			},
   993  			SpecServiceFn: emptySpecService,
   994  			Input:         modelInput,
   995  			SpecsInput:    modelSpecInput,
   996  			ExpectedErr:   testErr,
   997  		},
   998  		{
   999  			Name: "Error - Spec Creation",
  1000  			RepositoryFn: func() *automock.APIRepository {
  1001  				repo := &automock.APIRepository{}
  1002  				repo.On("Create", ctx, tenantID, modelAPIDefinition).Return(nil).Once()
  1003  				return repo
  1004  			},
  1005  			UIDServiceFn: func() *automock.UIDService {
  1006  				svc := &automock.UIDService{}
  1007  				svc.On("Generate").Return(id).Once()
  1008  				return svc
  1009  			},
  1010  			SpecServiceFn: func() *automock.SpecService {
  1011  				svc := &automock.SpecService{}
  1012  				svc.On("CreateByReferenceObjectID", ctx, *modelSpecsInput[0], resource.Application, model.APISpecReference, id).Return("", testErr).Once()
  1013  				return svc
  1014  			},
  1015  			Input:       modelInput,
  1016  			SpecsInput:  modelSpecInput,
  1017  			ExpectedErr: testErr,
  1018  		},
  1019  	}
  1020  
  1021  	for _, testCase := range testCases {
  1022  		t.Run(testCase.Name, func(t *testing.T) {
  1023  			// GIVEN
  1024  			repo := testCase.RepositoryFn()
  1025  			uidService := testCase.UIDServiceFn()
  1026  			specService := testCase.SpecServiceFn()
  1027  			defer mock.AssertExpectationsForObjects(t, repo, specService, uidService)
  1028  
  1029  			svc := api.NewService(repo, uidService, specService, nil)
  1030  			svc.SetTimestampGen(func() time.Time { return timestamp })
  1031  
  1032  			// WHEN
  1033  			result, err := svc.CreateInApplication(ctx, appID, testCase.Input, testCase.SpecsInput)
  1034  
  1035  			// THEN
  1036  			if testCase.ExpectedErr != nil {
  1037  				require.Error(t, err)
  1038  				assert.Contains(t, err.Error(), testCase.ExpectedErr.Error())
  1039  			} else {
  1040  				assert.IsType(t, "string", result)
  1041  			}
  1042  		})
  1043  	}
  1044  	t.Run("Error when tenant not in context", func(t *testing.T) {
  1045  		svc := api.NewService(nil, uid.NewService(), nil, nil)
  1046  		// WHEN
  1047  		_, err := svc.CreateInApplication(context.TODO(), "", model.APIDefinitionInput{}, &model.SpecInput{})
  1048  		// THEN
  1049  		require.Error(t, err)
  1050  		assert.Contains(t, err.Error(), "cannot read tenant from context")
  1051  	})
  1052  }
  1053  
  1054  func TestService_Update(t *testing.T) {
  1055  	// GIVEN
  1056  	testErr := errors.New("Test error")
  1057  
  1058  	id := "foo"
  1059  	var bundleID *string
  1060  	fixedTimestamp := time.Now()
  1061  	frURL := "foo.bar"
  1062  	spec := "spec"
  1063  
  1064  	modelInput := model.APIDefinitionInput{
  1065  		Name:         "Foo",
  1066  		TargetURLs:   api.ConvertTargetURLToJSONArray("https://test-url.com"),
  1067  		VersionInput: &model.VersionInput{},
  1068  	}
  1069  
  1070  	modelSpecInput := model.SpecInput{
  1071  		Data: &spec,
  1072  		FetchRequest: &model.FetchRequestInput{
  1073  			URL: frURL,
  1074  		},
  1075  	}
  1076  
  1077  	modelSpec := &model.Spec{
  1078  		ID:         id,
  1079  		ObjectType: model.APISpecReference,
  1080  		ObjectID:   id,
  1081  		Data:       &spec,
  1082  	}
  1083  
  1084  	inputAPIDefinitionModel := mock.MatchedBy(func(api *model.APIDefinition) bool {
  1085  		return api.Name == modelInput.Name
  1086  	})
  1087  
  1088  	apiDefinitionModel := &model.APIDefinition{
  1089  		Name:       "Bar",
  1090  		TargetURLs: api.ConvertTargetURLToJSONArray("https://test-url-updated.com"),
  1091  		Version:    &model.Version{},
  1092  	}
  1093  
  1094  	bundleReferenceInput := &model.BundleReferenceInput{
  1095  		APIDefaultTargetURL: str.Ptr(api.ExtractTargetURLFromJSONArray(modelInput.TargetURLs)),
  1096  	}
  1097  
  1098  	ctx := context.TODO()
  1099  	ctx = tenant.SaveToContext(ctx, tenantID, externalTenantID)
  1100  
  1101  	testCases := []struct {
  1102  		Name              string
  1103  		RepositoryFn      func() *automock.APIRepository
  1104  		SpecServiceFn     func() *automock.SpecService
  1105  		BundleReferenceFn func() *automock.BundleReferenceService
  1106  		Input             model.APIDefinitionInput
  1107  		InputID           string
  1108  		SpecInput         *model.SpecInput
  1109  		ResourceType      resource.Type
  1110  		ExpectedErr       error
  1111  	}{
  1112  		{
  1113  			Name: "Success When Spec is Found should update it",
  1114  			RepositoryFn: func() *automock.APIRepository {
  1115  				repo := &automock.APIRepository{}
  1116  				repo.On("GetByID", ctx, tenantID, id).Return(apiDefinitionModel, nil).Once()
  1117  				repo.On("Update", ctx, tenantID, inputAPIDefinitionModel).Return(nil).Once()
  1118  				return repo
  1119  			},
  1120  			SpecServiceFn: func() *automock.SpecService {
  1121  				svc := &automock.SpecService{}
  1122  				svc.On("GetByReferenceObjectID", ctx, resource.Application, model.APISpecReference, id).Return(modelSpec, nil).Once()
  1123  				svc.On("UpdateByReferenceObjectID", ctx, id, modelSpecInput, resource.Application, model.APISpecReference, id).Return(nil).Once()
  1124  				return svc
  1125  			},
  1126  			BundleReferenceFn: func() *automock.BundleReferenceService {
  1127  				svc := &automock.BundleReferenceService{}
  1128  				svc.On("UpdateByReferenceObjectID", ctx, *bundleReferenceInput, model.BundleAPIReference, str.Ptr(id), bundleID).Return(nil).Once()
  1129  				return svc
  1130  			},
  1131  			InputID:      "foo",
  1132  			Input:        modelInput,
  1133  			SpecInput:    &modelSpecInput,
  1134  			ResourceType: resource.Application,
  1135  			ExpectedErr:  nil,
  1136  		},
  1137  		{
  1138  			Name: "Success When Spec is not found should create in",
  1139  			RepositoryFn: func() *automock.APIRepository {
  1140  				repo := &automock.APIRepository{}
  1141  				repo.On("GetByID", ctx, tenantID, id).Return(apiDefinitionModel, nil).Once()
  1142  				repo.On("Update", ctx, tenantID, inputAPIDefinitionModel).Return(nil).Once()
  1143  				return repo
  1144  			},
  1145  			SpecServiceFn: func() *automock.SpecService {
  1146  				svc := &automock.SpecService{}
  1147  				svc.On("GetByReferenceObjectID", ctx, resource.Application, model.APISpecReference, id).Return(nil, nil).Once()
  1148  				svc.On("CreateByReferenceObjectID", ctx, modelSpecInput, resource.Application, model.APISpecReference, id).Return("id", nil).Once()
  1149  				return svc
  1150  			},
  1151  			BundleReferenceFn: func() *automock.BundleReferenceService {
  1152  				svc := &automock.BundleReferenceService{}
  1153  				svc.On("UpdateByReferenceObjectID", ctx, *bundleReferenceInput, model.BundleAPIReference, str.Ptr(id), bundleID).Return(nil).Once()
  1154  				return svc
  1155  			},
  1156  			InputID:      "foo",
  1157  			Input:        modelInput,
  1158  			SpecInput:    &modelSpecInput,
  1159  			ResourceType: resource.Application,
  1160  			ExpectedErr:  nil,
  1161  		},
  1162  		{
  1163  			Name: "Update Error",
  1164  			RepositoryFn: func() *automock.APIRepository {
  1165  				repo := &automock.APIRepository{}
  1166  				repo.On("GetByID", ctx, tenantID, "foo").Return(apiDefinitionModel, nil).Once()
  1167  				repo.On("Update", ctx, tenantID, inputAPIDefinitionModel).Return(testErr).Once()
  1168  				return repo
  1169  			},
  1170  			SpecServiceFn: emptySpecService,
  1171  			BundleReferenceFn: func() *automock.BundleReferenceService {
  1172  				return &automock.BundleReferenceService{}
  1173  			},
  1174  			InputID:      "foo",
  1175  			Input:        modelInput,
  1176  			SpecInput:    &modelSpecInput,
  1177  			ResourceType: resource.Application,
  1178  			ExpectedErr:  testErr,
  1179  		},
  1180  		{
  1181  			Name: "Get Spec Error",
  1182  			RepositoryFn: func() *automock.APIRepository {
  1183  				repo := &automock.APIRepository{}
  1184  				repo.On("GetByID", ctx, tenantID, "foo").Return(apiDefinitionModel, nil).Once()
  1185  				repo.On("Update", ctx, tenantID, inputAPIDefinitionModel).Return(nil).Once()
  1186  				return repo
  1187  			},
  1188  			SpecServiceFn: func() *automock.SpecService {
  1189  				svc := &automock.SpecService{}
  1190  				svc.On("GetByReferenceObjectID", ctx, resource.Application, model.APISpecReference, id).Return(nil, testErr).Once()
  1191  				return svc
  1192  			},
  1193  			BundleReferenceFn: func() *automock.BundleReferenceService {
  1194  				svc := &automock.BundleReferenceService{}
  1195  				svc.On("UpdateByReferenceObjectID", ctx, *bundleReferenceInput, model.BundleAPIReference, str.Ptr(id), bundleID).Return(nil).Once()
  1196  				return svc
  1197  			},
  1198  			InputID:      "foo",
  1199  			Input:        modelInput,
  1200  			SpecInput:    &modelSpecInput,
  1201  			ResourceType: resource.Application,
  1202  			ExpectedErr:  testErr,
  1203  		},
  1204  		{
  1205  			Name: "Spec Creation Error",
  1206  			RepositoryFn: func() *automock.APIRepository {
  1207  				repo := &automock.APIRepository{}
  1208  				repo.On("GetByID", ctx, tenantID, "foo").Return(apiDefinitionModel, nil).Once()
  1209  				repo.On("Update", ctx, tenantID, inputAPIDefinitionModel).Return(nil).Once()
  1210  				return repo
  1211  			},
  1212  			SpecServiceFn: func() *automock.SpecService {
  1213  				svc := &automock.SpecService{}
  1214  				svc.On("GetByReferenceObjectID", ctx, resource.Application, model.APISpecReference, id).Return(nil, nil).Once()
  1215  				svc.On("CreateByReferenceObjectID", ctx, modelSpecInput, resource.Application, model.APISpecReference, id).Return("", testErr).Once()
  1216  				return svc
  1217  			},
  1218  			BundleReferenceFn: func() *automock.BundleReferenceService {
  1219  				svc := &automock.BundleReferenceService{}
  1220  				svc.On("UpdateByReferenceObjectID", ctx, *bundleReferenceInput, model.BundleAPIReference, str.Ptr(id), bundleID).Return(nil).Once()
  1221  				return svc
  1222  			},
  1223  			InputID:      "foo",
  1224  			Input:        modelInput,
  1225  			SpecInput:    &modelSpecInput,
  1226  			ResourceType: resource.Application,
  1227  			ExpectedErr:  testErr,
  1228  		},
  1229  		{
  1230  			Name: "Spec Update Error",
  1231  			RepositoryFn: func() *automock.APIRepository {
  1232  				repo := &automock.APIRepository{}
  1233  				repo.On("GetByID", ctx, tenantID, "foo").Return(apiDefinitionModel, nil).Once()
  1234  				repo.On("Update", ctx, tenantID, inputAPIDefinitionModel).Return(nil).Once()
  1235  				return repo
  1236  			},
  1237  			SpecServiceFn: func() *automock.SpecService {
  1238  				svc := &automock.SpecService{}
  1239  				svc.On("GetByReferenceObjectID", ctx, resource.Application, model.APISpecReference, id).Return(modelSpec, nil).Once()
  1240  				svc.On("UpdateByReferenceObjectID", ctx, id, modelSpecInput, resource.Application, model.APISpecReference, id).Return(testErr).Once()
  1241  				return svc
  1242  			},
  1243  			BundleReferenceFn: func() *automock.BundleReferenceService {
  1244  				svc := &automock.BundleReferenceService{}
  1245  				svc.On("UpdateByReferenceObjectID", ctx, *bundleReferenceInput, model.BundleAPIReference, str.Ptr(id), bundleID).Return(nil).Once()
  1246  				return svc
  1247  			},
  1248  			InputID:      "foo",
  1249  			Input:        modelInput,
  1250  			SpecInput:    &modelSpecInput,
  1251  			ResourceType: resource.Application,
  1252  			ExpectedErr:  testErr,
  1253  		},
  1254  		{
  1255  			Name: "BundleReference API Update Error",
  1256  			RepositoryFn: func() *automock.APIRepository {
  1257  				repo := &automock.APIRepository{}
  1258  				repo.On("GetByID", ctx, tenantID, "foo").Return(apiDefinitionModel, nil).Once()
  1259  				repo.On("Update", ctx, tenantID, inputAPIDefinitionModel).Return(nil).Once()
  1260  				return repo
  1261  			},
  1262  			SpecServiceFn: emptySpecService,
  1263  			BundleReferenceFn: func() *automock.BundleReferenceService {
  1264  				svc := &automock.BundleReferenceService{}
  1265  				svc.On("UpdateByReferenceObjectID", ctx, *bundleReferenceInput, model.BundleAPIReference, str.Ptr(id), bundleID).Return(testErr).Once()
  1266  				return svc
  1267  			},
  1268  			InputID:      "foo",
  1269  			Input:        modelInput,
  1270  			SpecInput:    &modelSpecInput,
  1271  			ResourceType: resource.Application,
  1272  			ExpectedErr:  testErr,
  1273  		},
  1274  	}
  1275  
  1276  	for _, testCase := range testCases {
  1277  		t.Run(testCase.Name, func(t *testing.T) {
  1278  			// GIVEN
  1279  			repo := testCase.RepositoryFn()
  1280  			specSvc := testCase.SpecServiceFn()
  1281  			bundleReferenceSvc := testCase.BundleReferenceFn()
  1282  
  1283  			svc := api.NewService(repo, nil, specSvc, bundleReferenceSvc)
  1284  			svc.SetTimestampGen(func() time.Time { return fixedTimestamp })
  1285  
  1286  			// WHEN
  1287  			err := svc.Update(ctx, testCase.ResourceType, testCase.InputID, testCase.Input, testCase.SpecInput)
  1288  
  1289  			// THEN
  1290  			if testCase.ExpectedErr == nil {
  1291  				require.NoError(t, err)
  1292  			} else {
  1293  				require.Error(t, err)
  1294  				assert.Contains(t, err.Error(), testCase.ExpectedErr.Error())
  1295  			}
  1296  
  1297  			repo.AssertExpectations(t)
  1298  			specSvc.AssertExpectations(t)
  1299  			bundleReferenceSvc.AssertExpectations(t)
  1300  		})
  1301  	}
  1302  	t.Run("Error when tenant not in context", func(t *testing.T) {
  1303  		svc := api.NewService(nil, nil, nil, nil)
  1304  		// WHEN
  1305  		err := svc.Update(context.TODO(), "", "", model.APIDefinitionInput{}, &model.SpecInput{})
  1306  		// THEN
  1307  		require.Error(t, err)
  1308  		assert.Contains(t, err.Error(), "cannot read tenant from context")
  1309  	})
  1310  }
  1311  
  1312  func TestService_UpdateInManyBundles(t *testing.T) {
  1313  	// GIVEN
  1314  	testErr := errors.New("Test error")
  1315  
  1316  	id := "foo"
  1317  	isDefaultBundle := true
  1318  
  1319  	modelInput := model.APIDefinitionInput{
  1320  		Name:         "Foo",
  1321  		TargetURLs:   api.ConvertTargetURLToJSONArray(firstTargetURL),
  1322  		VersionInput: &model.VersionInput{},
  1323  	}
  1324  
  1325  	modelSpecInput := model.SpecInput{
  1326  		Data: &spec,
  1327  		FetchRequest: &model.FetchRequestInput{
  1328  			URL: frURL,
  1329  		},
  1330  	}
  1331  
  1332  	modelSpec := &model.Spec{
  1333  		ID:         id,
  1334  		ObjectType: model.APISpecReference,
  1335  		ObjectID:   id,
  1336  		Data:       &spec,
  1337  	}
  1338  
  1339  	inputAPIDefinitionModel := mock.MatchedBy(func(api *model.APIDefinition) bool {
  1340  		return api.Name == modelInput.Name
  1341  	})
  1342  
  1343  	apiDefinitionModelForApp := &model.APIDefinition{
  1344  		Name:          "Bar",
  1345  		TargetURLs:    api.ConvertTargetURLToJSONArray("https://test-url-updated.com"),
  1346  		Version:       &model.Version{},
  1347  		ApplicationID: &appID,
  1348  	}
  1349  
  1350  	apiDefinitionModelForAppTemplateVersion := &model.APIDefinition{
  1351  		Name:                         "Bar",
  1352  		TargetURLs:                   api.ConvertTargetURLToJSONArray("https://test-url-updated.com"),
  1353  		Version:                      &model.Version{},
  1354  		ApplicationTemplateVersionID: &appTemplateVersionID,
  1355  	}
  1356  
  1357  	bundleReferenceInput := &model.BundleReferenceInput{
  1358  		APIDefaultTargetURL: str.Ptr(api.ExtractTargetURLFromJSONArray(modelInput.TargetURLs)),
  1359  	}
  1360  	secondBundleReferenceInput := &model.BundleReferenceInput{
  1361  		APIDefaultTargetURL: str.Ptr(secondTargetURL),
  1362  	}
  1363  
  1364  	bundleReferenceInputWithDefaultBundle := &model.BundleReferenceInput{
  1365  		APIDefaultTargetURL: str.Ptr(api.ExtractTargetURLFromJSONArray(modelInput.TargetURLs)),
  1366  		IsDefaultBundle:     &isDefaultBundle,
  1367  	}
  1368  
  1369  	secondBundleReferenceInputWithDefaultBundle := &model.BundleReferenceInput{
  1370  		APIDefaultTargetURL: str.Ptr(secondTargetURL),
  1371  		IsDefaultBundle:     &isDefaultBundle,
  1372  	}
  1373  
  1374  	defaultTargetURLPerBundleForUpdate := map[string]string{firstBundleID: firstTargetURL}
  1375  	defaultTargetURLPerBundleForCreation := map[string]string{secondBundleID: secondTargetURL}
  1376  	bundleIDsForDeletion := []string{thirdBundleID}
  1377  
  1378  	ctx := context.TODO()
  1379  	ctxWithTenant := tenant.SaveToContext(ctx, tenantID, externalTenantID)
  1380  
  1381  	testCases := []struct {
  1382  		Name                                 string
  1383  		RepositoryFn                         func() *automock.APIRepository
  1384  		SpecServiceFn                        func() *automock.SpecService
  1385  		BundleReferenceFn                    func() *automock.BundleReferenceService
  1386  		Input                                model.APIDefinitionInput
  1387  		InputID                              string
  1388  		SpecInput                            *model.SpecInput
  1389  		DefaultTargetURLPerBundleForUpdate   map[string]string
  1390  		DefaultTargetURLPerBundleForCreation map[string]string
  1391  		BundleIDsForDeletion                 []string
  1392  		DefaultBundleID                      string
  1393  		ResourceType                         resource.Type
  1394  		Ctx                                  context.Context
  1395  		ExpectedErr                          error
  1396  	}{
  1397  		{
  1398  			Name: "Success in ORD case for Application",
  1399  			RepositoryFn: func() *automock.APIRepository {
  1400  				repo := &automock.APIRepository{}
  1401  				repo.On("GetByID", ctxWithTenant, tenantID, id).Return(apiDefinitionModelForApp, nil).Once()
  1402  				repo.On("Update", ctxWithTenant, tenantID, inputAPIDefinitionModel).Return(nil).Once()
  1403  				return repo
  1404  			},
  1405  			SpecServiceFn: func() *automock.SpecService {
  1406  				svc := &automock.SpecService{}
  1407  				svc.On("GetByReferenceObjectID", ctxWithTenant, resource.Application, model.APISpecReference, id).Return(modelSpec, nil).Once()
  1408  				svc.On("UpdateByReferenceObjectID", ctxWithTenant, id, modelSpecInput, resource.Application, model.APISpecReference, id).Return(nil).Once()
  1409  				return svc
  1410  			},
  1411  			BundleReferenceFn: func() *automock.BundleReferenceService {
  1412  				svc := &automock.BundleReferenceService{}
  1413  				svc.On("UpdateByReferenceObjectID", ctxWithTenant, *bundleReferenceInputWithDefaultBundle, model.BundleAPIReference, str.Ptr(id), &firstBundleID).Return(nil).Once()
  1414  				svc.On("CreateByReferenceObjectID", ctxWithTenant, *secondBundleReferenceInput, model.BundleAPIReference, str.Ptr(id), &secondBundleID).Return(nil).Once()
  1415  				svc.On("DeleteByReferenceObjectID", ctxWithTenant, model.BundleAPIReference, str.Ptr(id), &thirdBundleID).Return(nil).Once()
  1416  				return svc
  1417  			},
  1418  			InputID:                              "foo",
  1419  			Input:                                modelInput,
  1420  			SpecInput:                            &modelSpecInput,
  1421  			DefaultTargetURLPerBundleForUpdate:   defaultTargetURLPerBundleForUpdate,
  1422  			DefaultTargetURLPerBundleForCreation: defaultTargetURLPerBundleForCreation,
  1423  			BundleIDsForDeletion:                 bundleIDsForDeletion,
  1424  			DefaultBundleID:                      firstBundleID,
  1425  			ResourceType:                         resource.Application,
  1426  			ExpectedErr:                          nil,
  1427  		},
  1428  		{
  1429  			Name: "Success in ORD case for Application Template Version",
  1430  			RepositoryFn: func() *automock.APIRepository {
  1431  				repo := &automock.APIRepository{}
  1432  				repo.On("GetByIDGlobal", ctx, id).Return(apiDefinitionModelForAppTemplateVersion, nil).Once()
  1433  				repo.On("UpdateGlobal", ctx, inputAPIDefinitionModel).Return(nil).Once()
  1434  				return repo
  1435  			},
  1436  			SpecServiceFn: func() *automock.SpecService {
  1437  				svc := &automock.SpecService{}
  1438  				svc.On("GetByReferenceObjectID", ctx, resource.ApplicationTemplateVersion, model.APISpecReference, id).Return(modelSpec, nil).Once()
  1439  				svc.On("UpdateByReferenceObjectID", ctx, id, modelSpecInput, resource.ApplicationTemplateVersion, model.APISpecReference, id).Return(nil).Once()
  1440  				return svc
  1441  			},
  1442  			BundleReferenceFn: func() *automock.BundleReferenceService {
  1443  				svc := &automock.BundleReferenceService{}
  1444  				svc.On("UpdateByReferenceObjectID", ctx, *bundleReferenceInputWithDefaultBundle, model.BundleAPIReference, str.Ptr(id), &firstBundleID).Return(nil).Once()
  1445  				svc.On("CreateByReferenceObjectID", ctx, *secondBundleReferenceInput, model.BundleAPIReference, str.Ptr(id), &secondBundleID).Return(nil).Once()
  1446  				svc.On("DeleteByReferenceObjectID", ctx, model.BundleAPIReference, str.Ptr(id), &thirdBundleID).Return(nil).Once()
  1447  				return svc
  1448  			},
  1449  			InputID:                              "foo",
  1450  			Input:                                modelInput,
  1451  			SpecInput:                            &modelSpecInput,
  1452  			DefaultTargetURLPerBundleForUpdate:   defaultTargetURLPerBundleForUpdate,
  1453  			DefaultTargetURLPerBundleForCreation: defaultTargetURLPerBundleForCreation,
  1454  			BundleIDsForDeletion:                 bundleIDsForDeletion,
  1455  			DefaultBundleID:                      firstBundleID,
  1456  			ResourceType:                         resource.ApplicationTemplateVersion,
  1457  			Ctx:                                  ctx,
  1458  			ExpectedErr:                          nil,
  1459  		},
  1460  		{
  1461  			Name: "Success in ORD case when there is defaultBundle for BundleReference that has to be created",
  1462  			RepositoryFn: func() *automock.APIRepository {
  1463  				repo := &automock.APIRepository{}
  1464  				repo.On("GetByID", ctxWithTenant, tenantID, id).Return(apiDefinitionModelForApp, nil).Once()
  1465  				repo.On("Update", ctxWithTenant, tenantID, inputAPIDefinitionModel).Return(nil).Once()
  1466  				return repo
  1467  			},
  1468  			SpecServiceFn: func() *automock.SpecService {
  1469  				svc := &automock.SpecService{}
  1470  				svc.On("GetByReferenceObjectID", ctxWithTenant, resource.Application, model.APISpecReference, id).Return(modelSpec, nil).Once()
  1471  				svc.On("UpdateByReferenceObjectID", ctxWithTenant, id, modelSpecInput, resource.Application, model.APISpecReference, id).Return(nil).Once()
  1472  				return svc
  1473  			},
  1474  			BundleReferenceFn: func() *automock.BundleReferenceService {
  1475  				svc := &automock.BundleReferenceService{}
  1476  				svc.On("UpdateByReferenceObjectID", ctxWithTenant, *bundleReferenceInput, model.BundleAPIReference, str.Ptr(id), &firstBundleID).Return(nil).Once()
  1477  				svc.On("CreateByReferenceObjectID", ctxWithTenant, *secondBundleReferenceInputWithDefaultBundle, model.BundleAPIReference, str.Ptr(id), &secondBundleID).Return(nil).Once()
  1478  				svc.On("DeleteByReferenceObjectID", ctxWithTenant, model.BundleAPIReference, str.Ptr(id), &thirdBundleID).Return(nil).Once()
  1479  				return svc
  1480  			},
  1481  			InputID:                              "foo",
  1482  			Input:                                modelInput,
  1483  			SpecInput:                            &modelSpecInput,
  1484  			DefaultTargetURLPerBundleForUpdate:   defaultTargetURLPerBundleForUpdate,
  1485  			DefaultTargetURLPerBundleForCreation: defaultTargetURLPerBundleForCreation,
  1486  			BundleIDsForDeletion:                 bundleIDsForDeletion,
  1487  			DefaultBundleID:                      secondBundleID,
  1488  			ResourceType:                         resource.Application,
  1489  			ExpectedErr:                          nil,
  1490  		},
  1491  		{
  1492  			Name: "Error on BundleReference Update",
  1493  			RepositoryFn: func() *automock.APIRepository {
  1494  				repo := &automock.APIRepository{}
  1495  				repo.On("GetByID", ctxWithTenant, tenantID, id).Return(apiDefinitionModelForApp, nil).Once()
  1496  				repo.On("Update", ctxWithTenant, tenantID, inputAPIDefinitionModel).Return(nil).Once()
  1497  				return repo
  1498  			},
  1499  			SpecServiceFn: emptySpecService,
  1500  			BundleReferenceFn: func() *automock.BundleReferenceService {
  1501  				svc := &automock.BundleReferenceService{}
  1502  				svc.On("UpdateByReferenceObjectID", ctxWithTenant, *bundleReferenceInput, model.BundleAPIReference, str.Ptr(id), &firstBundleID).Return(testErr).Once()
  1503  				return svc
  1504  			},
  1505  			InputID:                              "foo",
  1506  			Input:                                modelInput,
  1507  			SpecInput:                            &modelSpecInput,
  1508  			DefaultTargetURLPerBundleForUpdate:   defaultTargetURLPerBundleForUpdate,
  1509  			DefaultTargetURLPerBundleForCreation: defaultTargetURLPerBundleForCreation,
  1510  			BundleIDsForDeletion:                 bundleIDsForDeletion,
  1511  			ResourceType:                         resource.Application,
  1512  			ExpectedErr:                          testErr,
  1513  		},
  1514  		{
  1515  			Name: "Error on BundleReference Creation",
  1516  			RepositoryFn: func() *automock.APIRepository {
  1517  				repo := &automock.APIRepository{}
  1518  				repo.On("GetByID", ctxWithTenant, tenantID, id).Return(apiDefinitionModelForApp, nil).Once()
  1519  				repo.On("Update", ctxWithTenant, tenantID, inputAPIDefinitionModel).Return(nil).Once()
  1520  				return repo
  1521  			},
  1522  			SpecServiceFn: emptySpecService,
  1523  			BundleReferenceFn: func() *automock.BundleReferenceService {
  1524  				svc := &automock.BundleReferenceService{}
  1525  				svc.On("UpdateByReferenceObjectID", ctxWithTenant, *bundleReferenceInput, model.BundleAPIReference, str.Ptr(id), &firstBundleID).Return(nil).Once()
  1526  				svc.On("CreateByReferenceObjectID", ctxWithTenant, *secondBundleReferenceInput, model.BundleAPIReference, str.Ptr(id), &secondBundleID).Return(testErr).Once()
  1527  				return svc
  1528  			},
  1529  			InputID:                              "foo",
  1530  			Input:                                modelInput,
  1531  			SpecInput:                            &modelSpecInput,
  1532  			DefaultTargetURLPerBundleForUpdate:   defaultTargetURLPerBundleForUpdate,
  1533  			DefaultTargetURLPerBundleForCreation: defaultTargetURLPerBundleForCreation,
  1534  			BundleIDsForDeletion:                 bundleIDsForDeletion,
  1535  			ResourceType:                         resource.Application,
  1536  			ExpectedErr:                          testErr,
  1537  		},
  1538  		{
  1539  			Name: "Error on BundleReference Deletion",
  1540  			RepositoryFn: func() *automock.APIRepository {
  1541  				repo := &automock.APIRepository{}
  1542  				repo.On("GetByID", ctxWithTenant, tenantID, id).Return(apiDefinitionModelForApp, nil).Once()
  1543  				repo.On("Update", ctxWithTenant, tenantID, inputAPIDefinitionModel).Return(nil).Once()
  1544  				return repo
  1545  			},
  1546  			SpecServiceFn: emptySpecService,
  1547  			BundleReferenceFn: func() *automock.BundleReferenceService {
  1548  				svc := &automock.BundleReferenceService{}
  1549  				svc.On("UpdateByReferenceObjectID", ctxWithTenant, *bundleReferenceInput, model.BundleAPIReference, str.Ptr(id), &firstBundleID).Return(nil).Once()
  1550  				svc.On("CreateByReferenceObjectID", ctxWithTenant, *secondBundleReferenceInput, model.BundleAPIReference, str.Ptr(id), &secondBundleID).Return(nil).Once()
  1551  				svc.On("DeleteByReferenceObjectID", ctxWithTenant, model.BundleAPIReference, str.Ptr(id), &thirdBundleID).Return(testErr).Once()
  1552  				return svc
  1553  			},
  1554  			InputID:                              "foo",
  1555  			Input:                                modelInput,
  1556  			SpecInput:                            &modelSpecInput,
  1557  			DefaultTargetURLPerBundleForUpdate:   defaultTargetURLPerBundleForUpdate,
  1558  			DefaultTargetURLPerBundleForCreation: defaultTargetURLPerBundleForCreation,
  1559  			BundleIDsForDeletion:                 bundleIDsForDeletion,
  1560  			ResourceType:                         resource.Application,
  1561  			ExpectedErr:                          testErr,
  1562  		},
  1563  	}
  1564  
  1565  	for _, testCase := range testCases {
  1566  		t.Run(testCase.Name, func(t *testing.T) {
  1567  			// GIVEN
  1568  			repo := testCase.RepositoryFn()
  1569  			specSvc := testCase.SpecServiceFn()
  1570  			bundleReferenceSvc := testCase.BundleReferenceFn()
  1571  
  1572  			svc := api.NewService(repo, nil, specSvc, bundleReferenceSvc)
  1573  			svc.SetTimestampGen(func() time.Time { return fixedTimestamp })
  1574  
  1575  			defaultCtx := ctxWithTenant
  1576  			if testCase.Ctx != nil {
  1577  				defaultCtx = testCase.Ctx
  1578  			}
  1579  
  1580  			// WHEN
  1581  			err := svc.UpdateInManyBundles(defaultCtx, testCase.ResourceType, testCase.InputID, testCase.Input, testCase.SpecInput, testCase.DefaultTargetURLPerBundleForUpdate, testCase.DefaultTargetURLPerBundleForCreation, testCase.BundleIDsForDeletion, 0, testCase.DefaultBundleID)
  1582  
  1583  			// then
  1584  			if testCase.ExpectedErr == nil {
  1585  				require.NoError(t, err)
  1586  			} else {
  1587  				require.Error(t, err)
  1588  				assert.Contains(t, err.Error(), testCase.ExpectedErr.Error())
  1589  			}
  1590  
  1591  			repo.AssertExpectations(t)
  1592  			specSvc.AssertExpectations(t)
  1593  			bundleReferenceSvc.AssertExpectations(t)
  1594  		})
  1595  	}
  1596  	t.Run("Error when tenant not in context", func(t *testing.T) {
  1597  		svc := api.NewService(nil, nil, nil, nil)
  1598  		// WHEN
  1599  		err := svc.UpdateInManyBundles(context.TODO(), resource.Application, "", model.APIDefinitionInput{}, &model.SpecInput{}, nil, nil, nil, 0, "")
  1600  		// THEN
  1601  		require.Error(t, err)
  1602  		assert.Contains(t, err.Error(), "cannot read tenant from context")
  1603  	})
  1604  }
  1605  
  1606  func TestService_UpdateForApplication(t *testing.T) {
  1607  	// GIVEN
  1608  	testErr := errors.New("Test error")
  1609  
  1610  	id := "foo"
  1611  	firstTargetURL := "https://test-url.com"
  1612  	timestamp := time.Now()
  1613  	frURL := "foo.bar"
  1614  	spec := "spec"
  1615  
  1616  	modelInput := model.APIDefinitionInput{
  1617  		Name:         "Foo",
  1618  		TargetURLs:   api.ConvertTargetURLToJSONArray(firstTargetURL),
  1619  		VersionInput: &model.VersionInput{},
  1620  	}
  1621  
  1622  	modelSpecInput := model.SpecInput{
  1623  		Data: &spec,
  1624  		FetchRequest: &model.FetchRequestInput{
  1625  			URL: frURL,
  1626  		},
  1627  	}
  1628  
  1629  	modelSpec := &model.Spec{
  1630  		ID:         id,
  1631  		ObjectType: model.APISpecReference,
  1632  		ObjectID:   id,
  1633  		Data:       &spec,
  1634  	}
  1635  
  1636  	inputAPIDefinitionModel := mock.MatchedBy(func(api *model.APIDefinition) bool {
  1637  		return api.Name == modelInput.Name
  1638  	})
  1639  
  1640  	apiDefinitionModel := &model.APIDefinition{
  1641  		Name:       "Bar",
  1642  		TargetURLs: api.ConvertTargetURLToJSONArray("https://test-url-updated.com"),
  1643  		Version:    &model.Version{},
  1644  	}
  1645  
  1646  	ctx := context.TODO()
  1647  	ctx = tenant.SaveToContext(ctx, tenantID, externalTenantID)
  1648  
  1649  	testCases := []struct {
  1650  		Name              string
  1651  		RepositoryFn      func() *automock.APIRepository
  1652  		SpecServiceFn     func() *automock.SpecService
  1653  		BundleReferenceFn func() *automock.BundleReferenceService
  1654  		SpecInput         *model.SpecInput
  1655  		Input             model.APIDefinitionInput
  1656  		InputID           string
  1657  		ExpectedErr       error
  1658  	}{
  1659  		{
  1660  			Name: "Success with spec update",
  1661  			RepositoryFn: func() *automock.APIRepository {
  1662  				repo := &automock.APIRepository{}
  1663  				repo.On("GetByID", ctx, tenantID, id).Return(apiDefinitionModel, nil).Once()
  1664  				repo.On("Update", ctx, tenantID, inputAPIDefinitionModel).Return(nil).Once()
  1665  				return repo
  1666  			},
  1667  			SpecServiceFn: func() *automock.SpecService {
  1668  				svc := &automock.SpecService{}
  1669  				svc.On("GetByReferenceObjectID", ctx, resource.Application, model.APISpecReference, id).Return(modelSpec, nil).Once()
  1670  				svc.On("UpdateByReferenceObjectID", ctx, id, modelSpecInput, resource.Application, model.APISpecReference, id).Return(nil).Once()
  1671  				return svc
  1672  			},
  1673  			SpecInput:   &modelSpecInput,
  1674  			InputID:     "foo",
  1675  			Input:       modelInput,
  1676  			ExpectedErr: nil,
  1677  		},
  1678  		{
  1679  			Name: "Success with spec create",
  1680  			RepositoryFn: func() *automock.APIRepository {
  1681  				repo := &automock.APIRepository{}
  1682  				repo.On("GetByID", ctx, tenantID, id).Return(apiDefinitionModel, nil).Once()
  1683  				repo.On("Update", ctx, tenantID, inputAPIDefinitionModel).Return(nil).Once()
  1684  				return repo
  1685  			},
  1686  			SpecServiceFn: func() *automock.SpecService {
  1687  				svc := &automock.SpecService{}
  1688  				svc.On("GetByReferenceObjectID", ctx, resource.Application, model.APISpecReference, id).Return(nil, nil).Once()
  1689  				svc.On("CreateByReferenceObjectID", ctx, modelSpecInput, resource.Application, model.APISpecReference, id).Return(specID, nil).Once()
  1690  				return svc
  1691  			},
  1692  			SpecInput:   &modelSpecInput,
  1693  			InputID:     "foo",
  1694  			Input:       modelInput,
  1695  			ExpectedErr: nil,
  1696  		},
  1697  		{
  1698  			Name: "Error when getting API",
  1699  			RepositoryFn: func() *automock.APIRepository {
  1700  				repo := &automock.APIRepository{}
  1701  				repo.On("GetByID", ctx, tenantID, id).Return(nil, testErr).Once()
  1702  				return repo
  1703  			},
  1704  			SpecServiceFn: emptySpecService,
  1705  			SpecInput:     &modelSpecInput,
  1706  			InputID:       "foo",
  1707  			Input:         modelInput,
  1708  			ExpectedErr:   testErr,
  1709  		},
  1710  		{
  1711  			Name: "Error when updating API",
  1712  			RepositoryFn: func() *automock.APIRepository {
  1713  				repo := &automock.APIRepository{}
  1714  				repo.On("GetByID", ctx, tenantID, id).Return(apiDefinitionModel, nil).Once()
  1715  				repo.On("Update", ctx, tenantID, inputAPIDefinitionModel).Return(testErr).Once()
  1716  				return repo
  1717  			},
  1718  			SpecServiceFn: emptySpecService,
  1719  			SpecInput:     &modelSpecInput,
  1720  			InputID:       "foo",
  1721  			Input:         modelInput,
  1722  			ExpectedErr:   testErr,
  1723  		},
  1724  		{
  1725  			Name: "Error when getting specs after API update",
  1726  			RepositoryFn: func() *automock.APIRepository {
  1727  				repo := &automock.APIRepository{}
  1728  				repo.On("GetByID", ctx, tenantID, id).Return(apiDefinitionModel, nil).Once()
  1729  				repo.On("Update", ctx, tenantID, inputAPIDefinitionModel).Return(nil).Once()
  1730  				return repo
  1731  			},
  1732  			SpecServiceFn: func() *automock.SpecService {
  1733  				svc := &automock.SpecService{}
  1734  				svc.On("GetByReferenceObjectID", ctx, resource.Application, model.APISpecReference, id).Return(nil, testErr).Once()
  1735  				return svc
  1736  			},
  1737  			SpecInput:   &modelSpecInput,
  1738  			InputID:     "foo",
  1739  			Input:       modelInput,
  1740  			ExpectedErr: testErr,
  1741  		},
  1742  		{
  1743  			Name: "Error when creating specs after API update",
  1744  			RepositoryFn: func() *automock.APIRepository {
  1745  				repo := &automock.APIRepository{}
  1746  				repo.On("GetByID", ctx, tenantID, id).Return(apiDefinitionModel, nil).Once()
  1747  				repo.On("Update", ctx, tenantID, inputAPIDefinitionModel).Return(nil).Once()
  1748  				return repo
  1749  			},
  1750  			SpecServiceFn: func() *automock.SpecService {
  1751  				svc := &automock.SpecService{}
  1752  				svc.On("GetByReferenceObjectID", ctx, resource.Application, model.APISpecReference, id).Return(nil, nil).Once()
  1753  				svc.On("CreateByReferenceObjectID", ctx, modelSpecInput, resource.Application, model.APISpecReference, id).Return("", testErr).Once()
  1754  				return svc
  1755  			},
  1756  			SpecInput:   &modelSpecInput,
  1757  			InputID:     "foo",
  1758  			Input:       modelInput,
  1759  			ExpectedErr: testErr,
  1760  		},
  1761  		{
  1762  			Name: "Error when updating specs after API update",
  1763  			RepositoryFn: func() *automock.APIRepository {
  1764  				repo := &automock.APIRepository{}
  1765  				repo.On("GetByID", ctx, tenantID, id).Return(apiDefinitionModel, nil).Once()
  1766  				repo.On("Update", ctx, tenantID, inputAPIDefinitionModel).Return(nil).Once()
  1767  				return repo
  1768  			},
  1769  			SpecServiceFn: func() *automock.SpecService {
  1770  				svc := &automock.SpecService{}
  1771  				svc.On("GetByReferenceObjectID", ctx, resource.Application, model.APISpecReference, id).Return(modelSpec, nil).Once()
  1772  				svc.On("UpdateByReferenceObjectID", ctx, id, modelSpecInput, resource.Application, model.APISpecReference, id).Return(testErr).Once()
  1773  				return svc
  1774  			},
  1775  			SpecInput:   &modelSpecInput,
  1776  			InputID:     "foo",
  1777  			Input:       modelInput,
  1778  			ExpectedErr: testErr,
  1779  		},
  1780  	}
  1781  
  1782  	for _, testCase := range testCases {
  1783  		t.Run(testCase.Name, func(t *testing.T) {
  1784  			// GIVEN
  1785  			repo := testCase.RepositoryFn()
  1786  			specSvc := testCase.SpecServiceFn()
  1787  			defer mock.AssertExpectationsForObjects(t, repo, specSvc)
  1788  
  1789  			svc := api.NewService(repo, nil, specSvc, nil)
  1790  			svc.SetTimestampGen(func() time.Time { return timestamp })
  1791  
  1792  			// WHEN
  1793  			err := svc.UpdateForApplication(ctx, testCase.InputID, testCase.Input, testCase.SpecInput)
  1794  
  1795  			// then
  1796  			if testCase.ExpectedErr == nil {
  1797  				require.NoError(t, err)
  1798  			} else {
  1799  				require.Error(t, err)
  1800  				assert.Contains(t, err.Error(), testCase.ExpectedErr.Error())
  1801  			}
  1802  		})
  1803  	}
  1804  	t.Run("Error when tenant not in context", func(t *testing.T) {
  1805  		svc := api.NewService(nil, nil, nil, nil)
  1806  		// WHEN
  1807  		err := svc.UpdateForApplication(context.TODO(), "", model.APIDefinitionInput{}, &model.SpecInput{})
  1808  		// THEN
  1809  		require.Error(t, err)
  1810  		assert.Contains(t, err.Error(), "cannot read tenant from context")
  1811  	})
  1812  }
  1813  
  1814  func TestService_Delete(t *testing.T) {
  1815  	// GIVEN
  1816  	testErr := errors.New("Test error")
  1817  
  1818  	id := "foo"
  1819  
  1820  	ctx := context.TODO()
  1821  	ctx = tenant.SaveToContext(ctx, tenantID, externalTenantID)
  1822  
  1823  	testCases := []struct {
  1824  		Name         string
  1825  		RepositoryFn func() *automock.APIRepository
  1826  		InputID      string
  1827  		ResourceType resource.Type
  1828  		ExpectedErr  error
  1829  	}{
  1830  		{
  1831  			Name: "Success for Application",
  1832  			RepositoryFn: func() *automock.APIRepository {
  1833  				repo := &automock.APIRepository{}
  1834  				repo.On("Delete", ctx, tenantID, id).Return(nil).Once()
  1835  				return repo
  1836  			},
  1837  			InputID:      id,
  1838  			ResourceType: resource.Application,
  1839  			ExpectedErr:  nil,
  1840  		},
  1841  		{
  1842  			Name: "Success for Application Template Version",
  1843  			RepositoryFn: func() *automock.APIRepository {
  1844  				repo := &automock.APIRepository{}
  1845  				repo.On("DeleteGlobal", ctx, id).Return(nil).Once()
  1846  				return repo
  1847  			},
  1848  			InputID:      id,
  1849  			ResourceType: resource.ApplicationTemplateVersion,
  1850  			ExpectedErr:  nil,
  1851  		},
  1852  		{
  1853  			Name: "Delete Error for Application",
  1854  			RepositoryFn: func() *automock.APIRepository {
  1855  				repo := &automock.APIRepository{}
  1856  				repo.On("Delete", ctx, tenantID, id).Return(testErr).Once()
  1857  				return repo
  1858  			},
  1859  			InputID:      id,
  1860  			ResourceType: resource.Application,
  1861  			ExpectedErr:  testErr,
  1862  		},
  1863  		{
  1864  			Name: "Delete Error for Application Tempalate Version",
  1865  			RepositoryFn: func() *automock.APIRepository {
  1866  				repo := &automock.APIRepository{}
  1867  				repo.On("DeleteGlobal", ctx, id).Return(testErr).Once()
  1868  				return repo
  1869  			},
  1870  			InputID:      id,
  1871  			ResourceType: resource.ApplicationTemplateVersion,
  1872  			ExpectedErr:  testErr,
  1873  		},
  1874  	}
  1875  
  1876  	for _, testCase := range testCases {
  1877  		t.Run(testCase.Name, func(t *testing.T) {
  1878  			// GIVEN
  1879  			repo := testCase.RepositoryFn()
  1880  
  1881  			svc := api.NewService(repo, nil, nil, nil)
  1882  
  1883  			// WHEN
  1884  			err := svc.Delete(ctx, testCase.ResourceType, testCase.InputID)
  1885  
  1886  			// then
  1887  			if testCase.ExpectedErr == nil {
  1888  				require.NoError(t, err)
  1889  			} else {
  1890  				require.Error(t, err)
  1891  				assert.Contains(t, err.Error(), testCase.ExpectedErr.Error())
  1892  			}
  1893  
  1894  			repo.AssertExpectations(t)
  1895  		})
  1896  	}
  1897  	t.Run("Error when tenant not in context", func(t *testing.T) {
  1898  		svc := api.NewService(nil, nil, nil, nil)
  1899  		// WHEN
  1900  		err := svc.Delete(context.TODO(), resource.Application, "")
  1901  		// THEN
  1902  		require.Error(t, err)
  1903  		assert.Contains(t, err.Error(), "cannot read tenant from context")
  1904  	})
  1905  }
  1906  
  1907  func TestService_DeleteAllByBundleID(t *testing.T) {
  1908  	// GIVEN
  1909  	testErr := errors.New("Test error")
  1910  	id := "foo"
  1911  
  1912  	ctx := context.TODO()
  1913  	ctx = tenant.SaveToContext(ctx, tenantID, externalTenantID)
  1914  
  1915  	testCases := []struct {
  1916  		Name         string
  1917  		RepositoryFn func() *automock.APIRepository
  1918  		InputID      string
  1919  		ExpectedErr  error
  1920  	}{
  1921  		{
  1922  			Name: "Success",
  1923  			RepositoryFn: func() *automock.APIRepository {
  1924  				repo := &automock.APIRepository{}
  1925  				repo.On("DeleteAllByBundleID", ctx, tenantID, id).Return(nil).Once()
  1926  				return repo
  1927  			},
  1928  			InputID:     id,
  1929  			ExpectedErr: nil,
  1930  		},
  1931  		{
  1932  			Name: "Delete Error",
  1933  			RepositoryFn: func() *automock.APIRepository {
  1934  				repo := &automock.APIRepository{}
  1935  				repo.On("DeleteAllByBundleID", ctx, tenantID, id).Return(testErr).Once()
  1936  				return repo
  1937  			},
  1938  			InputID:     id,
  1939  			ExpectedErr: testErr,
  1940  		},
  1941  	}
  1942  
  1943  	for _, testCase := range testCases {
  1944  		t.Run(testCase.Name, func(t *testing.T) {
  1945  			// GIVEN
  1946  			repo := testCase.RepositoryFn()
  1947  
  1948  			svc := api.NewService(repo, nil, nil, nil)
  1949  
  1950  			// WHEN
  1951  			err := svc.DeleteAllByBundleID(ctx, testCase.InputID)
  1952  
  1953  			// then
  1954  			if testCase.ExpectedErr == nil {
  1955  				require.NoError(t, err)
  1956  			} else {
  1957  				require.Error(t, err)
  1958  				assert.Contains(t, err.Error(), testCase.ExpectedErr.Error())
  1959  			}
  1960  
  1961  			repo.AssertExpectations(t)
  1962  		})
  1963  	}
  1964  	t.Run("Error when tenant not in context", func(t *testing.T) {
  1965  		svc := api.NewService(nil, nil, nil, nil)
  1966  		// WHEN
  1967  		err := svc.DeleteAllByBundleID(context.TODO(), "")
  1968  		// THEN
  1969  		require.Error(t, err)
  1970  		assert.Contains(t, err.Error(), "cannot read tenant from context")
  1971  	})
  1972  }
  1973  
  1974  func TestService_ListFetchRequests(t *testing.T) {
  1975  	// GIVEN
  1976  	ctx := context.TODO()
  1977  	ctx = tenant.SaveToContext(ctx, tenantID, externalTenantID)
  1978  
  1979  	testErr := errors.New("Test error")
  1980  
  1981  	frURL := "foo.bar"
  1982  	firstFRID := "frID"
  1983  	secondFRID := "frID2"
  1984  	firstSpecID := "specID"
  1985  	secondSpecID := "specID2"
  1986  	specIDs := []string{firstSpecID, secondSpecID}
  1987  	fixedTimestamp := time.Now()
  1988  
  1989  	firstFetchRequest := fixModelFetchRequest(firstFRID, frURL, fixedTimestamp)
  1990  	secondFetchRequest := fixModelFetchRequest(secondFRID, frURL, fixedTimestamp)
  1991  	fetchRequests := []*model.FetchRequest{firstFetchRequest, secondFetchRequest}
  1992  
  1993  	testCases := []struct {
  1994  		Name                  string
  1995  		SpecServiceFn         func() *automock.SpecService
  1996  		ExpectedFetchRequests []*model.FetchRequest
  1997  		ExpectedErrMessage    string
  1998  	}{
  1999  		{
  2000  			Name: "Success",
  2001  			SpecServiceFn: func() *automock.SpecService {
  2002  				svc := &automock.SpecService{}
  2003  				svc.On("ListFetchRequestsByReferenceObjectIDs", ctx, tenantID, specIDs, model.APISpecReference).Return(fetchRequests, nil).Once()
  2004  				return svc
  2005  			},
  2006  			ExpectedFetchRequests: fetchRequests,
  2007  		},
  2008  		{
  2009  			Name: "Success - Fetch Request Not Found",
  2010  			SpecServiceFn: func() *automock.SpecService {
  2011  				svc := &automock.SpecService{}
  2012  				svc.On("ListFetchRequestsByReferenceObjectIDs", ctx, tenantID, specIDs, model.APISpecReference).Return(nil, apperrors.NewNotFoundError(resource.FetchRequest, "")).Once()
  2013  				return svc
  2014  			},
  2015  			ExpectedFetchRequests: nil,
  2016  		},
  2017  		{
  2018  			Name: "Error while listing Fetch Requests",
  2019  			SpecServiceFn: func() *automock.SpecService {
  2020  				svc := &automock.SpecService{}
  2021  				svc.On("ListFetchRequestsByReferenceObjectIDs", ctx, tenantID, specIDs, model.APISpecReference).Return(nil, testErr).Once()
  2022  				return svc
  2023  			},
  2024  			ExpectedFetchRequests: nil,
  2025  			ExpectedErrMessage:    testErr.Error(),
  2026  		},
  2027  	}
  2028  
  2029  	for _, testCase := range testCases {
  2030  		t.Run(testCase.Name, func(t *testing.T) {
  2031  			specService := testCase.SpecServiceFn()
  2032  
  2033  			svc := api.NewService(nil, nil, specService, nil)
  2034  
  2035  			// WHEN
  2036  			frs, err := svc.ListFetchRequests(ctx, specIDs)
  2037  
  2038  			// then
  2039  			if testCase.ExpectedErrMessage == "" {
  2040  				require.NoError(t, err)
  2041  				assert.Equal(t, frs, testCase.ExpectedFetchRequests)
  2042  			} else {
  2043  				require.Error(t, err)
  2044  				assert.Contains(t, err.Error(), testCase.ExpectedErrMessage)
  2045  			}
  2046  
  2047  			specService.AssertExpectations(t)
  2048  		})
  2049  	}
  2050  	t.Run("Returns error on loading tenant", func(t *testing.T) {
  2051  		svc := api.NewService(nil, nil, nil, nil)
  2052  		// WHEN
  2053  		_, err := svc.ListFetchRequests(context.TODO(), nil)
  2054  		assert.True(t, apperrors.IsCannotReadTenant(err))
  2055  	})
  2056  }
  2057  
  2058  func fixUIDService(id string) func() *automock.UIDService {
  2059  	return func() *automock.UIDService {
  2060  		svc := &automock.UIDService{}
  2061  		svc.On("Generate").Return(id).Once()
  2062  		return svc
  2063  	}
  2064  }
  2065  
  2066  func emptySpecService() *automock.SpecService {
  2067  	svc := &automock.SpecService{}
  2068  	return svc
  2069  }