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 }