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