github.com/kyma-incubator/compass/components/director@v0.0.0-20230623144113-d764f56ff805/internal/domain/formationassignment/service_test.go (about) 1 package formationassignment_test 2 3 import ( 4 "context" 5 "encoding/json" 6 "fmt" 7 "net/http" 8 "testing" 9 10 "github.com/kyma-incubator/compass/components/director/pkg/str" 11 12 "github.com/kyma-incubator/compass/components/director/pkg/apperrors" 13 "github.com/kyma-incubator/compass/components/director/pkg/graphql" 14 "github.com/kyma-incubator/compass/components/director/pkg/resource" 15 "github.com/kyma-incubator/compass/components/director/pkg/webhook" 16 webhookclient "github.com/kyma-incubator/compass/components/director/pkg/webhook_client" 17 18 "github.com/kyma-incubator/compass/components/director/internal/domain/formationassignment" 19 "github.com/kyma-incubator/compass/components/director/internal/domain/formationassignment/automock" 20 "github.com/kyma-incubator/compass/components/director/internal/domain/tenant" 21 "github.com/kyma-incubator/compass/components/director/internal/model" 22 "github.com/kyma-incubator/compass/components/director/pkg/pagination" 23 "github.com/pkg/errors" 24 "github.com/stretchr/testify/mock" 25 "github.com/stretchr/testify/require" 26 ) 27 28 var ( 29 emptyCtx = context.TODO() 30 externalTnt = "externalTenant" 31 ctxWithTenant = tenant.SaveToContext(emptyCtx, TestTenantID, externalTnt) 32 33 testErr = errors.New("Test Error") 34 notFoundError = apperrors.NewNotFoundError(resource.FormationAssignment, TestID) 35 36 faInput = fixFormationAssignmentModelInput(TestConfigValueRawJSON) 37 fa = fixFormationAssignmentModelWithFormationID(TestFormationID) 38 39 assignOperation = model.AssignFormation 40 41 first = 2 42 after = "test" 43 44 readyState = string(model.ReadyAssignmentState) 45 configPendingState = string(model.ConfigPendingAssignmentState) 46 initialState = string(model.InitialAssignmentState) 47 deleteErrorState = string(model.DeleteErrorAssignmentState) 48 invalidState = "asd" 49 50 formation = &model.Formation{ 51 ID: TestFormationID, 52 TenantID: TestTenantID, 53 FormationTemplateID: TestFormationTemplateID, 54 Name: TestFormationName, 55 State: TestReadyState, 56 } 57 reverseFa = fixReverseFormationAssignment(fa) 58 59 rtmTypeLabelKey = "rtmTypeLabelKey" 60 appTypeLabelKey = "appTypeLabelKey" 61 62 appLbl = &model.Label{Value: appSubtype} 63 ) 64 65 func TestService_Create(t *testing.T) { 66 testCases := []struct { 67 Name string 68 Context context.Context 69 FormationAssignmentInput *model.FormationAssignmentInput 70 FormationAssignmentRepo func() *automock.FormationAssignmentRepository 71 ExpectedOutput string 72 ExpectedErrorMsg string 73 }{ 74 { 75 Name: "Success", 76 Context: ctxWithTenant, 77 FormationAssignmentInput: faInput, 78 FormationAssignmentRepo: func() *automock.FormationAssignmentRepository { 79 repo := &automock.FormationAssignmentRepository{} 80 repo.On("Create", ctxWithTenant, faModel).Return(nil).Once() 81 return repo 82 }, 83 ExpectedOutput: TestID, 84 }, 85 { 86 Name: "Error when loading tenant from context", 87 Context: emptyCtx, 88 ExpectedOutput: "", 89 ExpectedErrorMsg: "while loading tenant from context: cannot read tenant from context", 90 }, 91 { 92 Name: "Error when creating formation assignment", 93 Context: ctxWithTenant, 94 FormationAssignmentInput: faInput, 95 FormationAssignmentRepo: func() *automock.FormationAssignmentRepository { 96 repo := &automock.FormationAssignmentRepository{} 97 repo.On("Create", ctxWithTenant, faModel).Return(testErr).Once() 98 return repo 99 }, 100 ExpectedOutput: "", 101 ExpectedErrorMsg: "while creating formation assignment for formation with ID:", 102 }, 103 } 104 105 for _, testCase := range testCases { 106 t.Run(testCase.Name, func(t *testing.T) { 107 faRepo := &automock.FormationAssignmentRepository{} 108 if testCase.FormationAssignmentRepo != nil { 109 faRepo = testCase.FormationAssignmentRepo() 110 } 111 112 uuidSvc := fixUUIDService() 113 114 svc := formationassignment.NewService(faRepo, uuidSvc, nil, nil, nil, nil, nil, nil, nil, nil, "", "") 115 116 // WHEN 117 r, err := svc.Create(testCase.Context, testCase.FormationAssignmentInput) 118 119 if testCase.ExpectedErrorMsg != "" { 120 require.Error(t, err) 121 require.Contains(t, err.Error(), testCase.ExpectedErrorMsg) 122 } else { 123 require.NoError(t, err) 124 } 125 126 // THEN 127 require.Equal(t, testCase.ExpectedOutput, r) 128 129 mock.AssertExpectationsForObjects(t, faRepo, uuidSvc) 130 }) 131 } 132 } 133 134 func TestService_CreateIfNotExists(t *testing.T) { 135 testCases := []struct { 136 Name string 137 Context context.Context 138 FormationAssignmentInput *model.FormationAssignmentInput 139 FormationAssignmentRepo func() *automock.FormationAssignmentRepository 140 UUIDService func() *automock.UIDService 141 ExpectedOutput string 142 ExpectedErrorMsg string 143 }{ 144 { 145 Name: "Success when formation assignment does not exist", 146 Context: ctxWithTenant, 147 FormationAssignmentInput: faInput, 148 FormationAssignmentRepo: func() *automock.FormationAssignmentRepository { 149 repo := &automock.FormationAssignmentRepository{} 150 repo.On("GetByTargetAndSource", ctxWithTenant, faModel.Target, faModel.Source, TestTenantID, faModel.FormationID).Return(nil, apperrors.NewNotFoundError(resource.FormationAssignment, faModel.Source)).Once() 151 repo.On("Create", ctxWithTenant, faModel).Return(nil).Once() 152 return repo 153 }, 154 ExpectedOutput: TestID, 155 }, 156 { 157 Name: "error when fetching formation assignment returns error different from not found", 158 Context: ctxWithTenant, 159 FormationAssignmentInput: faInput, 160 UUIDService: unusedUIDService, 161 FormationAssignmentRepo: func() *automock.FormationAssignmentRepository { 162 repo := &automock.FormationAssignmentRepository{} 163 repo.On("GetByTargetAndSource", ctxWithTenant, faModel.Target, faModel.Source, TestTenantID, faModel.FormationID).Return(nil, testErr).Once() 164 return repo 165 }, 166 ExpectedErrorMsg: testErr.Error(), 167 }, 168 { 169 Name: "Success when formation assignment does not exist", 170 Context: ctxWithTenant, 171 FormationAssignmentInput: faInput, 172 UUIDService: unusedUIDService, 173 FormationAssignmentRepo: func() *automock.FormationAssignmentRepository { 174 repo := &automock.FormationAssignmentRepository{} 175 repo.On("GetByTargetAndSource", ctxWithTenant, faModel.Target, faModel.Source, TestTenantID, faModel.FormationID).Return(faModel, nil).Once() 176 return repo 177 }, 178 ExpectedOutput: TestID, 179 }, 180 { 181 Name: "Error when loading tenant from context", 182 Context: emptyCtx, 183 UUIDService: unusedUIDService, 184 ExpectedOutput: "", 185 ExpectedErrorMsg: "while loading tenant from context: cannot read tenant from context", 186 }, 187 } 188 189 for _, testCase := range testCases { 190 t.Run(testCase.Name, func(t *testing.T) { 191 faRepo := &automock.FormationAssignmentRepository{} 192 if testCase.FormationAssignmentRepo != nil { 193 faRepo = testCase.FormationAssignmentRepo() 194 } 195 196 uuidSvc := fixUUIDService() 197 if testCase.UUIDService != nil { 198 uuidSvc = testCase.UUIDService() 199 } 200 201 svc := formationassignment.NewService(faRepo, uuidSvc, nil, nil, nil, nil, nil, nil, nil, nil, "", "") 202 203 // WHEN 204 r, err := svc.CreateIfNotExists(testCase.Context, testCase.FormationAssignmentInput) 205 206 if testCase.ExpectedErrorMsg != "" { 207 require.Error(t, err) 208 require.Contains(t, err.Error(), testCase.ExpectedErrorMsg) 209 } else { 210 require.NoError(t, err) 211 } 212 213 // THEN 214 require.Equal(t, testCase.ExpectedOutput, r) 215 216 mock.AssertExpectationsForObjects(t, faRepo, uuidSvc) 217 }) 218 } 219 } 220 221 func TestService_Get(t *testing.T) { 222 testCases := []struct { 223 Name string 224 Context context.Context 225 FormationAssignmentRepo func() *automock.FormationAssignmentRepository 226 ExpectedOutput *model.FormationAssignment 227 ExpectedErrorMsg string 228 }{ 229 { 230 Name: "Success", 231 Context: ctxWithTenant, 232 FormationAssignmentRepo: func() *automock.FormationAssignmentRepository { 233 repo := &automock.FormationAssignmentRepository{} 234 repo.On("Get", ctxWithTenant, TestID, TestTenantID).Return(faModel, nil).Once() 235 return repo 236 }, 237 ExpectedOutput: faModel, 238 }, 239 { 240 Name: "Error when loading tenant from context", 241 Context: emptyCtx, 242 ExpectedOutput: nil, 243 ExpectedErrorMsg: "while loading tenant from context: cannot read tenant from context", 244 }, 245 { 246 Name: "Error when getting formation assignment", 247 Context: ctxWithTenant, 248 FormationAssignmentRepo: func() *automock.FormationAssignmentRepository { 249 repo := &automock.FormationAssignmentRepository{} 250 repo.On("Get", ctxWithTenant, TestID, TestTenantID).Return(nil, testErr).Once() 251 return repo 252 }, 253 ExpectedOutput: nil, 254 ExpectedErrorMsg: fmt.Sprintf("while getting formation assignment with ID: %q and tenant: %q", TestID, TestTenantID), 255 }, 256 } 257 258 for _, testCase := range testCases { 259 t.Run(testCase.Name, func(t *testing.T) { 260 faRepo := &automock.FormationAssignmentRepository{} 261 if testCase.FormationAssignmentRepo != nil { 262 faRepo = testCase.FormationAssignmentRepo() 263 } 264 265 svc := formationassignment.NewService(faRepo, nil, nil, nil, nil, nil, nil, nil, nil, nil, "", "") 266 267 // WHEN 268 r, err := svc.Get(testCase.Context, TestID) 269 270 if testCase.ExpectedErrorMsg != "" { 271 require.Error(t, err) 272 require.Contains(t, err.Error(), testCase.ExpectedErrorMsg) 273 } else { 274 require.NoError(t, err) 275 } 276 277 // THEN 278 require.Equal(t, testCase.ExpectedOutput, r) 279 280 mock.AssertExpectationsForObjects(t, faRepo) 281 }) 282 } 283 } 284 285 func TestService_GetGlobalByID(t *testing.T) { 286 testCases := []struct { 287 Name string 288 Context context.Context 289 FormationAssignmentRepo func() *automock.FormationAssignmentRepository 290 ExpectedOutput *model.FormationAssignment 291 ExpectedErrorMsg string 292 }{ 293 { 294 Name: "Success", 295 Context: ctxWithTenant, 296 FormationAssignmentRepo: func() *automock.FormationAssignmentRepository { 297 repo := &automock.FormationAssignmentRepository{} 298 repo.On("GetGlobalByID", ctxWithTenant, TestID).Return(faModel, nil).Once() 299 return repo 300 }, 301 ExpectedOutput: faModel, 302 }, 303 { 304 Name: "Error when getting formation assignment globally", 305 Context: ctxWithTenant, 306 FormationAssignmentRepo: func() *automock.FormationAssignmentRepository { 307 repo := &automock.FormationAssignmentRepository{} 308 repo.On("GetGlobalByID", ctxWithTenant, TestID).Return(nil, testErr).Once() 309 return repo 310 }, 311 ExpectedOutput: nil, 312 ExpectedErrorMsg: fmt.Sprintf("while getting formation assignment with ID: %q globally", TestID), 313 }, 314 } 315 316 for _, testCase := range testCases { 317 t.Run(testCase.Name, func(t *testing.T) { 318 faRepo := &automock.FormationAssignmentRepository{} 319 if testCase.FormationAssignmentRepo != nil { 320 faRepo = testCase.FormationAssignmentRepo() 321 } 322 323 svc := formationassignment.NewService(faRepo, nil, nil, nil, nil, nil, nil, nil, nil, nil, "", "") 324 325 // WHEN 326 r, err := svc.GetGlobalByID(testCase.Context, TestID) 327 328 if testCase.ExpectedErrorMsg != "" { 329 require.Error(t, err) 330 require.Contains(t, err.Error(), testCase.ExpectedErrorMsg) 331 } else { 332 require.NoError(t, err) 333 } 334 335 // THEN 336 require.Equal(t, testCase.ExpectedOutput, r) 337 338 mock.AssertExpectationsForObjects(t, faRepo) 339 }) 340 } 341 } 342 343 func TestService_GetGlobalByIDAndFormationID(t *testing.T) { 344 testCases := []struct { 345 Name string 346 Context context.Context 347 FormationAssignmentRepo func() *automock.FormationAssignmentRepository 348 ExpectedOutput *model.FormationAssignment 349 ExpectedErrorMsg string 350 }{ 351 { 352 Name: "Success", 353 Context: ctxWithTenant, 354 FormationAssignmentRepo: func() *automock.FormationAssignmentRepository { 355 repo := &automock.FormationAssignmentRepository{} 356 repo.On("GetGlobalByIDAndFormationID", ctxWithTenant, TestID, TestFormationID).Return(faModel, nil).Once() 357 return repo 358 }, 359 ExpectedOutput: faModel, 360 }, 361 { 362 Name: "Error when getting formation assignment globally", 363 Context: ctxWithTenant, 364 FormationAssignmentRepo: func() *automock.FormationAssignmentRepository { 365 repo := &automock.FormationAssignmentRepository{} 366 repo.On("GetGlobalByIDAndFormationID", ctxWithTenant, TestID, TestFormationID).Return(nil, testErr).Once() 367 return repo 368 }, 369 ExpectedOutput: nil, 370 ExpectedErrorMsg: fmt.Sprintf("while getting formation assignment with ID: %q and formation ID: %q globally", TestID, TestFormationID), 371 }, 372 } 373 374 for _, testCase := range testCases { 375 t.Run(testCase.Name, func(t *testing.T) { 376 faRepo := &automock.FormationAssignmentRepository{} 377 if testCase.FormationAssignmentRepo != nil { 378 faRepo = testCase.FormationAssignmentRepo() 379 } 380 381 svc := formationassignment.NewService(faRepo, nil, nil, nil, nil, nil, nil, nil, nil, nil, "", "") 382 383 // WHEN 384 r, err := svc.GetGlobalByIDAndFormationID(testCase.Context, TestID, TestFormationID) 385 386 if testCase.ExpectedErrorMsg != "" { 387 require.Error(t, err) 388 require.Contains(t, err.Error(), testCase.ExpectedErrorMsg) 389 } else { 390 require.NoError(t, err) 391 } 392 393 // THEN 394 require.Equal(t, testCase.ExpectedOutput, r) 395 396 mock.AssertExpectationsForObjects(t, faRepo) 397 }) 398 } 399 } 400 401 func TestService_GetForFormation(t *testing.T) { 402 testCases := []struct { 403 Name string 404 Context context.Context 405 FormationAssignmentRepo func() *automock.FormationAssignmentRepository 406 ExpectedOutput *model.FormationAssignment 407 ExpectedErrorMsg string 408 }{ 409 { 410 Name: "Success", 411 Context: ctxWithTenant, 412 FormationAssignmentRepo: func() *automock.FormationAssignmentRepository { 413 repo := &automock.FormationAssignmentRepository{} 414 repo.On("GetForFormation", ctxWithTenant, TestTenantID, TestID, TestFormationID).Return(faModel, nil).Once() 415 return repo 416 }, 417 ExpectedOutput: faModel, 418 }, 419 { 420 Name: "Error when loading tenant from context", 421 Context: emptyCtx, 422 ExpectedOutput: nil, 423 ExpectedErrorMsg: "while loading tenant from context: cannot read tenant from context", 424 }, 425 { 426 Name: "Error when getting formation assignment for formation", 427 Context: ctxWithTenant, 428 FormationAssignmentRepo: func() *automock.FormationAssignmentRepository { 429 repo := &automock.FormationAssignmentRepository{} 430 repo.On("GetForFormation", ctxWithTenant, TestTenantID, TestID, TestFormationID).Return(nil, testErr).Once() 431 return repo 432 }, 433 ExpectedOutput: nil, 434 ExpectedErrorMsg: fmt.Sprintf("while getting formation assignment with ID: %q for formation with ID: %q", TestID, TestFormationID), 435 }, 436 } 437 438 for _, testCase := range testCases { 439 t.Run(testCase.Name, func(t *testing.T) { 440 faRepo := &automock.FormationAssignmentRepository{} 441 if testCase.FormationAssignmentRepo != nil { 442 faRepo = testCase.FormationAssignmentRepo() 443 } 444 445 svc := formationassignment.NewService(faRepo, nil, nil, nil, nil, nil, nil, nil, nil, nil, "", "") 446 447 // WHEN 448 r, err := svc.GetForFormation(testCase.Context, TestID, TestFormationID) 449 450 if testCase.ExpectedErrorMsg != "" { 451 require.Error(t, err) 452 require.Contains(t, err.Error(), testCase.ExpectedErrorMsg) 453 } else { 454 require.NoError(t, err) 455 } 456 457 // THEN 458 require.Equal(t, testCase.ExpectedOutput, r) 459 460 mock.AssertExpectationsForObjects(t, faRepo) 461 }) 462 } 463 } 464 465 func TestService_GetReverseBySourceAndTarget(t *testing.T) { 466 testCases := []struct { 467 Name string 468 Context context.Context 469 FormationAssignmentRepo func() *automock.FormationAssignmentRepository 470 ExpectedOutput *model.FormationAssignment 471 ExpectedErrorMsg string 472 }{ 473 { 474 Name: "Success", 475 Context: ctxWithTenant, 476 FormationAssignmentRepo: func() *automock.FormationAssignmentRepository { 477 repo := &automock.FormationAssignmentRepository{} 478 repo.On("GetReverseBySourceAndTarget", ctxWithTenant, TestTenantID, TestFormationID, TestSource, TestTarget).Return(faModel, nil).Once() 479 return repo 480 }, 481 ExpectedOutput: faModel, 482 }, 483 { 484 Name: "Error when loading tenant from context", 485 Context: emptyCtx, 486 ExpectedOutput: nil, 487 ExpectedErrorMsg: "while loading tenant from context: cannot read tenant from context", 488 }, 489 { 490 Name: "Error when getting reverse formation assignment by formation ID, source and target", 491 Context: ctxWithTenant, 492 FormationAssignmentRepo: func() *automock.FormationAssignmentRepository { 493 repo := &automock.FormationAssignmentRepository{} 494 repo.On("GetReverseBySourceAndTarget", ctxWithTenant, TestTenantID, TestFormationID, TestSource, TestTarget).Return(nil, testErr).Once() 495 return repo 496 }, 497 ExpectedOutput: nil, 498 ExpectedErrorMsg: fmt.Sprintf("while getting reverse formation assignment for formation ID: %q and source: %q and target: %q", TestFormationID, TestSource, TestTarget), 499 }, 500 } 501 502 for _, testCase := range testCases { 503 t.Run(testCase.Name, func(t *testing.T) { 504 faRepo := &automock.FormationAssignmentRepository{} 505 if testCase.FormationAssignmentRepo != nil { 506 faRepo = testCase.FormationAssignmentRepo() 507 } 508 509 svc := formationassignment.NewService(faRepo, nil, nil, nil, nil, nil, nil, nil, nil, nil, "", "") 510 511 // WHEN 512 r, err := svc.GetReverseBySourceAndTarget(testCase.Context, TestFormationID, TestSource, TestTarget) 513 514 if testCase.ExpectedErrorMsg != "" { 515 require.Error(t, err) 516 require.Contains(t, err.Error(), testCase.ExpectedErrorMsg) 517 } else { 518 require.NoError(t, err) 519 } 520 521 // THEN 522 require.Equal(t, testCase.ExpectedOutput, r) 523 524 mock.AssertExpectationsForObjects(t, faRepo) 525 }) 526 } 527 } 528 529 func TestService_List(t *testing.T) { 530 // GIVEN 531 faModelPage := &model.FormationAssignmentPage{ 532 Data: []*model.FormationAssignment{faModel}, 533 PageInfo: &pagination.Page{ 534 StartCursor: "start", 535 EndCursor: "end", 536 HasNextPage: false, 537 }, 538 TotalCount: 1, 539 } 540 541 testCases := []struct { 542 Name string 543 Context context.Context 544 InputPageSize int 545 InputCursor string 546 FormationAssignmentRepo func() *automock.FormationAssignmentRepository 547 ExpectedOutput *model.FormationAssignmentPage 548 ExpectedErrorMsg string 549 }{ 550 { 551 Name: "Success", 552 Context: ctxWithTenant, 553 InputPageSize: first, 554 InputCursor: after, 555 FormationAssignmentRepo: func() *automock.FormationAssignmentRepository { 556 repo := &automock.FormationAssignmentRepository{} 557 repo.On("List", ctxWithTenant, first, after, TestTenantID).Return(faModelPage, nil).Once() 558 return repo 559 }, 560 ExpectedOutput: faModelPage, 561 }, 562 { 563 Name: "Error when loading tenant from context", 564 Context: emptyCtx, 565 InputPageSize: first, 566 ExpectedOutput: nil, 567 ExpectedErrorMsg: "while loading tenant from context: cannot read tenant from context", 568 }, 569 { 570 Name: "Error when listing formation assignment", 571 Context: ctxWithTenant, 572 InputPageSize: first, 573 InputCursor: after, 574 FormationAssignmentRepo: func() *automock.FormationAssignmentRepository { 575 repo := &automock.FormationAssignmentRepository{} 576 repo.On("List", ctxWithTenant, first, after, TestTenantID).Return(nil, testErr).Once() 577 return repo 578 }, 579 ExpectedOutput: nil, 580 ExpectedErrorMsg: testErr.Error(), 581 }, 582 { 583 Name: "Error when page size is invalid", 584 Context: ctxWithTenant, 585 InputPageSize: 0, 586 ExpectedOutput: nil, 587 ExpectedErrorMsg: "page size must be between 1 and 200", 588 }, 589 } 590 591 for _, testCase := range testCases { 592 t.Run(testCase.Name, func(t *testing.T) { 593 faRepo := &automock.FormationAssignmentRepository{} 594 if testCase.FormationAssignmentRepo != nil { 595 faRepo = testCase.FormationAssignmentRepo() 596 } 597 598 svc := formationassignment.NewService(faRepo, nil, nil, nil, nil, nil, nil, nil, nil, nil, "", "") 599 600 // WHEN 601 r, err := svc.List(testCase.Context, testCase.InputPageSize, testCase.InputCursor) 602 603 if testCase.ExpectedErrorMsg != "" { 604 require.Error(t, err) 605 require.Contains(t, err.Error(), testCase.ExpectedErrorMsg) 606 } else { 607 require.NoError(t, err) 608 } 609 610 // THEN 611 require.Equal(t, testCase.ExpectedOutput, r) 612 613 mock.AssertExpectationsForObjects(t, faRepo) 614 }) 615 } 616 } 617 618 func TestService_ListByFormationIDs(t *testing.T) { 619 // GIVEN 620 faModelPages := []*model.FormationAssignmentPage{ 621 { 622 Data: []*model.FormationAssignment{faModel}, 623 PageInfo: &pagination.Page{ 624 StartCursor: "start", 625 EndCursor: "end", 626 HasNextPage: false, 627 }, 628 TotalCount: 1, 629 }, 630 } 631 632 formationsIDs := []string{"formation-id-1", "formation-id-2"} 633 634 testCases := []struct { 635 Name string 636 Context context.Context 637 InputPageSize int 638 InputCursor string 639 FormationAssignmentRepo func() *automock.FormationAssignmentRepository 640 ExpectedOutput []*model.FormationAssignmentPage 641 ExpectedErrorMsg string 642 }{ 643 { 644 Name: "Success", 645 Context: ctxWithTenant, 646 InputPageSize: first, 647 InputCursor: after, 648 FormationAssignmentRepo: func() *automock.FormationAssignmentRepository { 649 repo := &automock.FormationAssignmentRepository{} 650 repo.On("ListByFormationIDs", ctxWithTenant, TestTenantID, formationsIDs, first, after).Return(faModelPages, nil).Once() 651 return repo 652 }, 653 ExpectedOutput: faModelPages, 654 }, 655 { 656 Name: "Error when loading tenant from context", 657 Context: emptyCtx, 658 InputPageSize: first, 659 ExpectedOutput: nil, 660 ExpectedErrorMsg: "while loading tenant from context: cannot read tenant from context", 661 }, 662 { 663 Name: "Error when listing formation assignments by formations IDs", 664 Context: ctxWithTenant, 665 InputPageSize: first, 666 InputCursor: after, 667 FormationAssignmentRepo: func() *automock.FormationAssignmentRepository { 668 repo := &automock.FormationAssignmentRepository{} 669 repo.On("ListByFormationIDs", ctxWithTenant, TestTenantID, formationsIDs, first, after).Return(nil, testErr).Once() 670 return repo 671 }, 672 ExpectedOutput: nil, 673 ExpectedErrorMsg: testErr.Error(), 674 }, 675 { 676 Name: "Error when page size is invalid", 677 Context: ctxWithTenant, 678 InputPageSize: 0, 679 ExpectedOutput: nil, 680 ExpectedErrorMsg: "page size must be between 1 and 200", 681 }, 682 } 683 684 for _, testCase := range testCases { 685 t.Run(testCase.Name, func(t *testing.T) { 686 faRepo := &automock.FormationAssignmentRepository{} 687 if testCase.FormationAssignmentRepo != nil { 688 faRepo = testCase.FormationAssignmentRepo() 689 } 690 691 svc := formationassignment.NewService(faRepo, nil, nil, nil, nil, nil, nil, nil, nil, nil, "", "") 692 693 // WHEN 694 r, err := svc.ListByFormationIDs(testCase.Context, formationsIDs, testCase.InputPageSize, testCase.InputCursor) 695 696 if testCase.ExpectedErrorMsg != "" { 697 require.Error(t, err) 698 require.Contains(t, err.Error(), testCase.ExpectedErrorMsg) 699 } else { 700 require.NoError(t, err) 701 } 702 703 // THEN 704 require.Equal(t, testCase.ExpectedOutput, r) 705 706 mock.AssertExpectationsForObjects(t, faRepo) 707 }) 708 } 709 } 710 711 func TestService_ListByFormationIDsNoPaging(t *testing.T) { 712 // GIVEN 713 faModels := [][]*model.FormationAssignment{{faModel}} 714 715 formationsIDs := []string{"formation-id-1", "formation-id-2"} 716 717 testCases := []struct { 718 Name string 719 Context context.Context 720 FormationAssignmentRepo func() *automock.FormationAssignmentRepository 721 ExpectedOutput [][]*model.FormationAssignment 722 ExpectedErrorMsg string 723 }{ 724 { 725 Name: "Success", 726 Context: ctxWithTenant, 727 FormationAssignmentRepo: func() *automock.FormationAssignmentRepository { 728 repo := &automock.FormationAssignmentRepository{} 729 repo.On("ListByFormationIDsNoPaging", ctxWithTenant, TestTenantID, formationsIDs).Return(faModels, nil).Once() 730 return repo 731 }, 732 ExpectedOutput: faModels, 733 }, 734 { 735 Name: "Error when loading tenant from context", 736 Context: emptyCtx, 737 ExpectedOutput: nil, 738 ExpectedErrorMsg: "while loading tenant from context: cannot read tenant from context", 739 }, 740 { 741 Name: "Error when listing formation assignments by formations IDs", 742 Context: ctxWithTenant, 743 FormationAssignmentRepo: func() *automock.FormationAssignmentRepository { 744 repo := &automock.FormationAssignmentRepository{} 745 repo.On("ListByFormationIDsNoPaging", ctxWithTenant, TestTenantID, formationsIDs).Return(nil, testErr).Once() 746 return repo 747 }, 748 ExpectedOutput: nil, 749 ExpectedErrorMsg: testErr.Error(), 750 }, 751 } 752 753 for _, testCase := range testCases { 754 t.Run(testCase.Name, func(t *testing.T) { 755 faRepo := &automock.FormationAssignmentRepository{} 756 if testCase.FormationAssignmentRepo != nil { 757 faRepo = testCase.FormationAssignmentRepo() 758 } 759 760 svc := formationassignment.NewService(faRepo, nil, nil, nil, nil, nil, nil, nil, nil, nil, "", "") 761 762 // WHEN 763 r, err := svc.ListByFormationIDsNoPaging(testCase.Context, formationsIDs) 764 765 if testCase.ExpectedErrorMsg != "" { 766 require.Error(t, err) 767 require.Contains(t, err.Error(), testCase.ExpectedErrorMsg) 768 } else { 769 require.NoError(t, err) 770 } 771 772 // THEN 773 require.Equal(t, testCase.ExpectedOutput, r) 774 775 mock.AssertExpectationsForObjects(t, faRepo) 776 }) 777 } 778 } 779 780 func TestService_GetAssignmentsForFormationWithStates(t *testing.T) { 781 // GIVEN 782 faModels := []*model.FormationAssignment{faModel} 783 784 testCases := []struct { 785 Name string 786 FormationAssignmentRepo func() *automock.FormationAssignmentRepository 787 ExpectedOutput []*model.FormationAssignment 788 ExpectedErrorMsg string 789 }{ 790 { 791 Name: "Success", 792 FormationAssignmentRepo: func() *automock.FormationAssignmentRepository { 793 repo := &automock.FormationAssignmentRepository{} 794 repo.On("GetAssignmentsForFormationWithStates", ctxWithTenant, TestTenantID, TestFormationID, []string{TestStateInitial}).Return(faModels, nil).Once() 795 return repo 796 }, 797 ExpectedOutput: faModels, 798 }, 799 { 800 Name: "Error when listing formation assignments by formations IDs", 801 FormationAssignmentRepo: func() *automock.FormationAssignmentRepository { 802 repo := &automock.FormationAssignmentRepository{} 803 repo.On("GetAssignmentsForFormationWithStates", ctxWithTenant, TestTenantID, TestFormationID, []string{TestStateInitial}).Return(nil, testErr).Once() 804 return repo 805 }, 806 ExpectedOutput: nil, 807 ExpectedErrorMsg: "while getting formation assignments with states for formation with ID", 808 }, 809 } 810 811 for _, testCase := range testCases { 812 t.Run(testCase.Name, func(t *testing.T) { 813 faRepo := &automock.FormationAssignmentRepository{} 814 if testCase.FormationAssignmentRepo != nil { 815 faRepo = testCase.FormationAssignmentRepo() 816 } 817 818 svc := formationassignment.NewService(faRepo, nil, nil, nil, nil, nil, nil, nil, nil, nil, "", "") 819 820 // WHEN 821 r, err := svc.GetAssignmentsForFormationWithStates(ctxWithTenant, TestTenantID, TestFormationID, []string{TestStateInitial}) 822 823 if testCase.ExpectedErrorMsg != "" { 824 require.Error(t, err) 825 require.Contains(t, err.Error(), testCase.ExpectedErrorMsg) 826 } else { 827 require.NoError(t, err) 828 } 829 830 // THEN 831 require.Equal(t, testCase.ExpectedOutput, r) 832 833 mock.AssertExpectationsForObjects(t, faRepo) 834 }) 835 } 836 } 837 838 func TestService_ListFormationAssignmentsForObjectID(t *testing.T) { 839 // GIVEN 840 841 formationID := "formationID" 842 objectID := "objectID" 843 result := []*model.FormationAssignment{faModel} 844 845 testCases := []struct { 846 Name string 847 Context context.Context 848 FormationAssignmentRepo func() *automock.FormationAssignmentRepository 849 ExpectedOutput []*model.FormationAssignment 850 ExpectedErrorMsg string 851 }{ 852 { 853 Name: "Success", 854 Context: ctxWithTenant, 855 FormationAssignmentRepo: func() *automock.FormationAssignmentRepository { 856 repo := &automock.FormationAssignmentRepository{} 857 repo.On("ListAllForObject", ctxWithTenant, TestTenantID, formationID, objectID).Return(result, nil).Once() 858 return repo 859 }, 860 ExpectedOutput: result, 861 }, 862 { 863 Name: "Error when loading tenant from context", 864 Context: emptyCtx, 865 ExpectedOutput: nil, 866 ExpectedErrorMsg: "while loading tenant from context: cannot read tenant from context", 867 }, 868 { 869 Name: "Error when listing formation assignment", 870 Context: ctxWithTenant, 871 FormationAssignmentRepo: func() *automock.FormationAssignmentRepository { 872 repo := &automock.FormationAssignmentRepository{} 873 repo.On("ListAllForObject", ctxWithTenant, TestTenantID, formationID, objectID).Return(nil, testErr).Once() 874 return repo 875 }, 876 ExpectedOutput: nil, 877 ExpectedErrorMsg: testErr.Error(), 878 }, 879 } 880 881 for _, testCase := range testCases { 882 t.Run(testCase.Name, func(t *testing.T) { 883 faRepo := &automock.FormationAssignmentRepository{} 884 if testCase.FormationAssignmentRepo != nil { 885 faRepo = testCase.FormationAssignmentRepo() 886 } 887 888 svc := formationassignment.NewService(faRepo, nil, nil, nil, nil, nil, nil, nil, nil, nil, "", "") 889 890 // WHEN 891 r, err := svc.ListFormationAssignmentsForObjectID(testCase.Context, formationID, objectID) 892 893 if testCase.ExpectedErrorMsg != "" { 894 require.Error(t, err) 895 require.Contains(t, err.Error(), testCase.ExpectedErrorMsg) 896 } else { 897 require.NoError(t, err) 898 } 899 900 // THEN 901 require.Equal(t, testCase.ExpectedOutput, r) 902 903 mock.AssertExpectationsForObjects(t, faRepo) 904 }) 905 } 906 } 907 908 func TestService_ListFormationAssignmentsForObjectIDs(t *testing.T) { 909 // GIVEN 910 911 formationID := "formationID" 912 objectID := "objectID" 913 result := []*model.FormationAssignment{faModel} 914 915 testCases := []struct { 916 Name string 917 Context context.Context 918 FormationAssignmentRepo func() *automock.FormationAssignmentRepository 919 ExpectedOutput []*model.FormationAssignment 920 ExpectedErrorMsg string 921 }{ 922 { 923 Name: "Success", 924 Context: ctxWithTenant, 925 FormationAssignmentRepo: func() *automock.FormationAssignmentRepository { 926 repo := &automock.FormationAssignmentRepository{} 927 repo.On("ListAllForObjectIDs", ctxWithTenant, TestTenantID, formationID, []string{objectID}).Return(result, nil).Once() 928 return repo 929 }, 930 ExpectedOutput: result, 931 }, 932 { 933 Name: "Error when loading tenant from context", 934 Context: emptyCtx, 935 ExpectedOutput: nil, 936 ExpectedErrorMsg: "while loading tenant from context: cannot read tenant from context", 937 }, 938 { 939 Name: "Error when listing formation assignment", 940 Context: ctxWithTenant, 941 FormationAssignmentRepo: func() *automock.FormationAssignmentRepository { 942 repo := &automock.FormationAssignmentRepository{} 943 repo.On("ListAllForObjectIDs", ctxWithTenant, TestTenantID, formationID, []string{objectID}).Return(nil, testErr).Once() 944 return repo 945 }, 946 ExpectedOutput: nil, 947 ExpectedErrorMsg: testErr.Error(), 948 }, 949 } 950 951 for _, testCase := range testCases { 952 t.Run(testCase.Name, func(t *testing.T) { 953 faRepo := &automock.FormationAssignmentRepository{} 954 if testCase.FormationAssignmentRepo != nil { 955 faRepo = testCase.FormationAssignmentRepo() 956 } 957 958 svc := formationassignment.NewService(faRepo, nil, nil, nil, nil, nil, nil, nil, nil, nil, "", "") 959 960 // WHEN 961 r, err := svc.ListFormationAssignmentsForObjectIDs(testCase.Context, formationID, []string{objectID}) 962 963 if testCase.ExpectedErrorMsg != "" { 964 require.Error(t, err) 965 require.Contains(t, err.Error(), testCase.ExpectedErrorMsg) 966 } else { 967 require.NoError(t, err) 968 } 969 970 // THEN 971 require.Equal(t, testCase.ExpectedOutput, r) 972 973 mock.AssertExpectationsForObjects(t, faRepo) 974 }) 975 } 976 } 977 978 func TestService_Update(t *testing.T) { 979 // GIVEN 980 testCases := []struct { 981 Name string 982 Context context.Context 983 FormationAssignment *model.FormationAssignment 984 FormationAssignmentRepo func() *automock.FormationAssignmentRepository 985 ExpectedErrorMsg string 986 }{ 987 { 988 Name: "Success", 989 Context: ctxWithTenant, 990 FormationAssignment: fa, 991 FormationAssignmentRepo: func() *automock.FormationAssignmentRepository { 992 repo := &automock.FormationAssignmentRepository{} 993 repo.On("Exists", ctxWithTenant, TestID, TestTenantID).Return(true, nil).Once() 994 repo.On("Update", ctxWithTenant, faModel).Return(nil).Once() 995 return repo 996 }, 997 }, 998 { 999 Name: "Error when loading tenant from context", 1000 Context: emptyCtx, 1001 ExpectedErrorMsg: "while loading tenant from context: cannot read tenant from context", 1002 }, 1003 { 1004 Name: "Error when checking for formation assignment existence", 1005 Context: ctxWithTenant, 1006 FormationAssignment: fa, 1007 FormationAssignmentRepo: func() *automock.FormationAssignmentRepository { 1008 repo := &automock.FormationAssignmentRepository{} 1009 repo.On("Exists", ctxWithTenant, TestID, TestTenantID).Return(false, testErr).Once() 1010 return repo 1011 }, 1012 ExpectedErrorMsg: fmt.Sprintf("while ensuring formation assignment with ID: %q exists", TestID), 1013 }, 1014 { 1015 Name: "Error when formation assignment does not exists", 1016 Context: ctxWithTenant, 1017 FormationAssignment: fa, 1018 FormationAssignmentRepo: func() *automock.FormationAssignmentRepository { 1019 repo := &automock.FormationAssignmentRepository{} 1020 repo.On("Exists", ctxWithTenant, TestID, TestTenantID).Return(false, nil).Once() 1021 return repo 1022 }, 1023 ExpectedErrorMsg: "Object not found", 1024 }, 1025 { 1026 Name: "Error when updating formation assignment", 1027 Context: ctxWithTenant, 1028 FormationAssignment: fa, 1029 FormationAssignmentRepo: func() *automock.FormationAssignmentRepository { 1030 repo := &automock.FormationAssignmentRepository{} 1031 repo.On("Exists", ctxWithTenant, TestID, TestTenantID).Return(true, nil).Once() 1032 repo.On("Update", ctxWithTenant, faModel).Return(testErr).Once() 1033 return repo 1034 }, 1035 ExpectedErrorMsg: testErr.Error(), 1036 }, 1037 } 1038 1039 for _, testCase := range testCases { 1040 t.Run(testCase.Name, func(t *testing.T) { 1041 faRepo := &automock.FormationAssignmentRepository{} 1042 if testCase.FormationAssignmentRepo != nil { 1043 faRepo = testCase.FormationAssignmentRepo() 1044 } 1045 1046 svc := formationassignment.NewService(faRepo, nil, nil, nil, nil, nil, nil, nil, nil, nil, "", "") 1047 1048 // WHEN 1049 err := svc.Update(testCase.Context, TestID, testCase.FormationAssignment) 1050 1051 if testCase.ExpectedErrorMsg != "" { 1052 require.Error(t, err) 1053 require.Contains(t, err.Error(), testCase.ExpectedErrorMsg) 1054 } else { 1055 require.NoError(t, err) 1056 } 1057 1058 mock.AssertExpectationsForObjects(t, faRepo) 1059 }) 1060 } 1061 } 1062 1063 func TestService_Delete(t *testing.T) { 1064 testCases := []struct { 1065 Name string 1066 Context context.Context 1067 FormationAssignmentRepo func() *automock.FormationAssignmentRepository 1068 ExpectedErrorMsg string 1069 }{ 1070 { 1071 Name: "Success", 1072 Context: ctxWithTenant, 1073 FormationAssignmentRepo: func() *automock.FormationAssignmentRepository { 1074 repo := &automock.FormationAssignmentRepository{} 1075 repo.On("Delete", ctxWithTenant, TestID, TestTenantID).Return(nil).Once() 1076 return repo 1077 }, 1078 }, 1079 { 1080 Name: "Error when loading tenant from context", 1081 Context: emptyCtx, 1082 ExpectedErrorMsg: "while loading tenant from context: cannot read tenant from context", 1083 }, 1084 { 1085 Name: "Error when deleting formation assignment", 1086 Context: ctxWithTenant, 1087 FormationAssignmentRepo: func() *automock.FormationAssignmentRepository { 1088 repo := &automock.FormationAssignmentRepository{} 1089 repo.On("Delete", ctxWithTenant, TestID, TestTenantID).Return(testErr).Once() 1090 return repo 1091 }, 1092 ExpectedErrorMsg: testErr.Error(), 1093 }, 1094 } 1095 1096 for _, testCase := range testCases { 1097 t.Run(testCase.Name, func(t *testing.T) { 1098 faRepo := &automock.FormationAssignmentRepository{} 1099 if testCase.FormationAssignmentRepo != nil { 1100 faRepo = testCase.FormationAssignmentRepo() 1101 } 1102 1103 svc := formationassignment.NewService(faRepo, nil, nil, nil, nil, nil, nil, nil, nil, nil, "", "") 1104 1105 // WHEN 1106 err := svc.Delete(testCase.Context, TestID) 1107 1108 if testCase.ExpectedErrorMsg != "" { 1109 require.Error(t, err) 1110 require.Contains(t, err.Error(), testCase.ExpectedErrorMsg) 1111 } else { 1112 require.NoError(t, err) 1113 } 1114 1115 mock.AssertExpectationsForObjects(t, faRepo) 1116 }) 1117 } 1118 } 1119 1120 func TestService_DeleteAssignmentsForObjectID(t *testing.T) { 1121 testCases := []struct { 1122 Name string 1123 Context context.Context 1124 FormationAssignmentRepo func() *automock.FormationAssignmentRepository 1125 ExpectedErrorMsg string 1126 }{ 1127 { 1128 Name: "Success", 1129 Context: ctxWithTenant, 1130 FormationAssignmentRepo: func() *automock.FormationAssignmentRepository { 1131 repo := &automock.FormationAssignmentRepository{} 1132 repo.On("DeleteAssignmentsForObjectID", ctxWithTenant, TestTenantID, TestID, TestSource).Return(nil).Once() 1133 return repo 1134 }, 1135 }, 1136 { 1137 Name: "Error when loading tenant from context", 1138 Context: emptyCtx, 1139 ExpectedErrorMsg: "while loading tenant from context: cannot read tenant from context", 1140 }, 1141 { 1142 Name: "Error when deleting formation assignment", 1143 Context: ctxWithTenant, 1144 FormationAssignmentRepo: func() *automock.FormationAssignmentRepository { 1145 repo := &automock.FormationAssignmentRepository{} 1146 repo.On("DeleteAssignmentsForObjectID", ctxWithTenant, TestTenantID, TestID, TestSource).Return(testErr).Once() 1147 return repo 1148 }, 1149 ExpectedErrorMsg: testErr.Error(), 1150 }, 1151 } 1152 1153 for _, testCase := range testCases { 1154 t.Run(testCase.Name, func(t *testing.T) { 1155 faRepo := &automock.FormationAssignmentRepository{} 1156 if testCase.FormationAssignmentRepo != nil { 1157 faRepo = testCase.FormationAssignmentRepo() 1158 } 1159 1160 svc := formationassignment.NewService(faRepo, nil, nil, nil, nil, nil, nil, nil, nil, nil, "", "") 1161 1162 // WHEN 1163 err := svc.DeleteAssignmentsForObjectID(testCase.Context, TestID, TestSource) 1164 1165 if testCase.ExpectedErrorMsg != "" { 1166 require.Error(t, err) 1167 require.Contains(t, err.Error(), testCase.ExpectedErrorMsg) 1168 } else { 1169 require.NoError(t, err) 1170 } 1171 1172 mock.AssertExpectationsForObjects(t, faRepo) 1173 }) 1174 } 1175 } 1176 1177 func TestService_Exists(t *testing.T) { 1178 testCases := []struct { 1179 Name string 1180 Context context.Context 1181 FormationAssignmentRepo func() *automock.FormationAssignmentRepository 1182 ExpectedErrorMsg string 1183 }{ 1184 { 1185 Name: "Success", 1186 Context: ctxWithTenant, 1187 FormationAssignmentRepo: func() *automock.FormationAssignmentRepository { 1188 repo := &automock.FormationAssignmentRepository{} 1189 repo.On("Exists", ctxWithTenant, TestID, TestTenantID).Return(true, nil).Once() 1190 return repo 1191 }, 1192 }, 1193 { 1194 Name: "Error when loading tenant from context", 1195 Context: emptyCtx, 1196 ExpectedErrorMsg: "while loading tenant from context: cannot read tenant from context", 1197 }, 1198 { 1199 Name: "Error when checking for formation assignment existence", 1200 Context: ctxWithTenant, 1201 FormationAssignmentRepo: func() *automock.FormationAssignmentRepository { 1202 repo := &automock.FormationAssignmentRepository{} 1203 repo.On("Exists", ctxWithTenant, TestID, TestTenantID).Return(false, testErr).Once() 1204 return repo 1205 }, 1206 ExpectedErrorMsg: fmt.Sprintf("while checking formation assignment existence for ID: %q and tenant: %q", TestID, TestTenantID), 1207 }, 1208 } 1209 1210 for _, testCase := range testCases { 1211 t.Run(testCase.Name, func(t *testing.T) { 1212 faRepo := &automock.FormationAssignmentRepository{} 1213 if testCase.FormationAssignmentRepo != nil { 1214 faRepo = testCase.FormationAssignmentRepo() 1215 } 1216 1217 svc := formationassignment.NewService(faRepo, nil, nil, nil, nil, nil, nil, nil, nil, nil, "", "") 1218 1219 // WHEN 1220 exists, err := svc.Exists(testCase.Context, TestID) 1221 1222 if testCase.ExpectedErrorMsg != "" { 1223 require.Error(t, err) 1224 require.Contains(t, err.Error(), testCase.ExpectedErrorMsg) 1225 require.False(t, exists) 1226 } else { 1227 require.NoError(t, err) 1228 require.True(t, exists) 1229 } 1230 1231 mock.AssertExpectationsForObjects(t, faRepo) 1232 }) 1233 } 1234 } 1235 1236 func TestService_GenerateAssignments(t *testing.T) { 1237 // GIVEN 1238 objectID := "objectID" 1239 applications := []*model.Application{{BaseEntity: &model.BaseEntity{ID: "app"}}} 1240 runtimes := []*model.Runtime{{ID: "runtime"}} 1241 runtimeContexts := []*model.RuntimeContext{{ID: "runtimeContext"}} 1242 1243 formationParticipantsIDs := []string{applications[0].ID, runtimes[0].ID, runtimeContexts[0].ID} 1244 1245 formationAssignmentsForApplication := fixFormationAssignmentsWithObjectTypeAndID(model.FormationAssignmentTypeApplication, objectID, applications[0].ID, runtimes[0].ID, runtimeContexts[0].ID) 1246 formationAssignmentsForRuntime := fixFormationAssignmentsWithObjectTypeAndID(model.FormationAssignmentTypeRuntime, objectID, applications[0].ID, runtimes[0].ID, runtimeContexts[0].ID) 1247 formationAssignmentsForRuntimeContext := fixFormationAssignmentsWithObjectTypeAndID(model.FormationAssignmentTypeRuntimeContext, objectID, applications[0].ID, runtimes[0].ID, runtimeContexts[0].ID) 1248 formationAssignmentsForSelf := fixFormationAssignmentsForSelf(applications[0].ID, runtimes[0].ID, runtimeContexts[0].ID) 1249 formationAssignmentsForRuntimeContextWithParentInTheFormation := fixFormationAssignmentsForRtmCtxWithAppAndRtmCtx(model.FormationAssignmentTypeRuntimeContext, objectID, applications[0].ID, runtimeContexts[0].ID) 1250 1251 allAssignments := append(append(formationAssignmentsForApplication, append(formationAssignmentsForRuntime, append(formationAssignmentsForRuntimeContext, formationAssignmentsForRuntimeContextWithParentInTheFormation...)...)...), formationAssignmentsForSelf...) 1252 1253 formationAssignmentIDs := []string{"ID1", "ID2", "ID3", "ID4", "ID5", "ID6", "ID7"} 1254 formationAssignmentIDsRtmCtxParentInFormation := []string{"ID1", "ID2", "ID3", "ID4", "ID5"} 1255 1256 formation := &model.Formation{ 1257 Name: "testFormation", 1258 ID: "ID", 1259 } 1260 testCases := []struct { 1261 Name string 1262 Context context.Context 1263 ObjectType graphql.FormationObjectType 1264 FormationAssignmentRepo func() *automock.FormationAssignmentRepository 1265 ApplicationRepo func() *automock.ApplicationRepository 1266 RuntimeRepo func() *automock.RuntimeRepository 1267 RuntimeContextRepo func() *automock.RuntimeContextRepository 1268 UIDService func() *automock.UIDService 1269 ExpectedOutput []*model.FormationAssignment 1270 ExpectedErrorMsg string 1271 }{ 1272 { 1273 Name: "Success", 1274 Context: ctxWithTenant, 1275 ObjectType: graphql.FormationObjectTypeApplication, 1276 FormationAssignmentRepo: func() *automock.FormationAssignmentRepository { 1277 repo := &automock.FormationAssignmentRepository{} 1278 repo.On("ListAllForObjectIDs", ctxWithTenant, TestTenantID, formation.ID, formationParticipantsIDs).Return(allAssignments, nil).Once() 1279 for i := range formationAssignmentsForApplication { 1280 repo.On("GetByTargetAndSource", ctxWithTenant, formationAssignmentsForApplication[i].Target, formationAssignmentsForApplication[i].Source, TestTenantID, formationAssignmentsForApplication[i].FormationID).Return(nil, apperrors.NewNotFoundErrorWithType(resource.FormationAssignment)).Once() 1281 repo.On("Create", ctxWithTenant, formationAssignmentsForApplication[i]).Return(nil).Once() 1282 } 1283 repo.On("ListForIDs", ctxWithTenant, TestTenantID, formationAssignmentIDs).Return(formationAssignmentsForApplication, nil).Once() 1284 1285 return repo 1286 }, 1287 UIDService: func() *automock.UIDService { 1288 uidSvc := &automock.UIDService{} 1289 for i := range formationAssignmentIDs { 1290 uidSvc.On("Generate").Return(formationAssignmentIDs[i]).Once() 1291 } 1292 return uidSvc 1293 }, 1294 ApplicationRepo: func() *automock.ApplicationRepository { 1295 repo := &automock.ApplicationRepository{} 1296 repo.On("ListByScenariosNoPaging", ctxWithTenant, TestTenantID, []string{formation.Name}).Return(applications, nil).Once() 1297 return repo 1298 }, 1299 RuntimeRepo: func() *automock.RuntimeRepository { 1300 repo := &automock.RuntimeRepository{} 1301 repo.On("ListByScenarios", ctxWithTenant, TestTenantID, []string{formation.Name}).Return(runtimes, nil).Once() 1302 return repo 1303 }, 1304 RuntimeContextRepo: func() *automock.RuntimeContextRepository { 1305 repo := &automock.RuntimeContextRepository{} 1306 repo.On("ListByScenarios", ctxWithTenant, TestTenantID, []string{formation.Name}).Return(runtimeContexts, nil).Once() 1307 return repo 1308 }, 1309 ExpectedOutput: formationAssignmentsForApplication, 1310 }, 1311 { 1312 Name: "Success does not create formation assignment for entity that is being unassigned asynchronously", 1313 Context: ctxWithTenant, 1314 ObjectType: graphql.FormationObjectTypeApplication, 1315 FormationAssignmentRepo: func() *automock.FormationAssignmentRepository { 1316 repo := &automock.FormationAssignmentRepository{} 1317 unassignAppFormationAssignments := fixFormationAssignmentsWithObjectTypeAndID(model.FormationAssignmentTypeApplication, objectID, applications[0].ID, runtimes[0].ID, runtimeContexts[0].ID) 1318 repo.On("ListAllForObjectIDs", ctxWithTenant, TestTenantID, formation.ID, []string{applications[0].ID, objectID, runtimes[0].ID, runtimeContexts[0].ID}).Return(append(allAssignments, unassignAppFormationAssignments...), nil).Once() 1319 for i := range formationAssignmentsForApplication { 1320 repo.On("GetByTargetAndSource", ctxWithTenant, formationAssignmentsForApplication[i].Target, formationAssignmentsForApplication[i].Source, TestTenantID, formationAssignmentsForApplication[i].FormationID).Return(nil, apperrors.NewNotFoundErrorWithType(resource.FormationAssignment)).Once() 1321 repo.On("Create", ctxWithTenant, formationAssignmentsForApplication[i]).Return(nil).Once() 1322 } 1323 repo.On("ListForIDs", ctxWithTenant, TestTenantID, formationAssignmentIDs).Return(formationAssignmentsForApplication, nil).Once() 1324 1325 return repo 1326 }, 1327 UIDService: func() *automock.UIDService { 1328 uidSvc := &automock.UIDService{} 1329 for i := range formationAssignmentIDs { 1330 uidSvc.On("Generate").Return(formationAssignmentIDs[i]).Once() 1331 } 1332 return uidSvc 1333 }, 1334 ApplicationRepo: func() *automock.ApplicationRepository { 1335 repo := &automock.ApplicationRepository{} 1336 repo.On("ListByScenariosNoPaging", ctxWithTenant, TestTenantID, []string{formation.Name}).Return(append(applications, &model.Application{BaseEntity: &model.BaseEntity{ID: objectID}}), nil).Once() 1337 return repo 1338 }, 1339 RuntimeRepo: func() *automock.RuntimeRepository { 1340 repo := &automock.RuntimeRepository{} 1341 repo.On("ListByScenarios", ctxWithTenant, TestTenantID, []string{formation.Name}).Return(runtimes, nil).Once() 1342 return repo 1343 }, 1344 RuntimeContextRepo: func() *automock.RuntimeContextRepository { 1345 repo := &automock.RuntimeContextRepository{} 1346 repo.On("ListByScenarios", ctxWithTenant, TestTenantID, []string{formation.Name}).Return(runtimeContexts, nil).Once() 1347 return repo 1348 }, 1349 ExpectedOutput: formationAssignmentsForApplication, 1350 }, 1351 { 1352 Name: "Success does not create formation assignment for application and itself", 1353 Context: ctxWithTenant, 1354 ObjectType: graphql.FormationObjectTypeApplication, 1355 FormationAssignmentRepo: func() *automock.FormationAssignmentRepository { 1356 repo := &automock.FormationAssignmentRepository{} 1357 repo.On("ListAllForObjectIDs", ctxWithTenant, TestTenantID, formation.ID, []string{applications[0].ID, objectID, runtimes[0].ID, runtimeContexts[0].ID}).Return(allAssignments, nil).Once() 1358 for i := range formationAssignmentsForApplication { 1359 repo.On("GetByTargetAndSource", ctxWithTenant, formationAssignmentsForApplication[i].Target, formationAssignmentsForApplication[i].Source, TestTenantID, formationAssignmentsForApplication[i].FormationID).Return(nil, apperrors.NewNotFoundErrorWithType(resource.FormationAssignment)).Once() 1360 repo.On("Create", ctxWithTenant, formationAssignmentsForApplication[i]).Return(nil).Once() 1361 } 1362 repo.On("ListForIDs", ctxWithTenant, TestTenantID, formationAssignmentIDs).Return(formationAssignmentsForApplication, nil).Once() 1363 1364 return repo 1365 }, 1366 UIDService: func() *automock.UIDService { 1367 uidSvc := &automock.UIDService{} 1368 for i := range formationAssignmentIDs { 1369 uidSvc.On("Generate").Return(formationAssignmentIDs[i]).Once() 1370 } 1371 return uidSvc 1372 }, 1373 ApplicationRepo: func() *automock.ApplicationRepository { 1374 repo := &automock.ApplicationRepository{} 1375 repo.On("ListByScenariosNoPaging", ctxWithTenant, TestTenantID, []string{formation.Name}).Return(append(applications, &model.Application{BaseEntity: &model.BaseEntity{ID: objectID}}), nil).Once() 1376 return repo 1377 }, 1378 RuntimeRepo: func() *automock.RuntimeRepository { 1379 repo := &automock.RuntimeRepository{} 1380 repo.On("ListByScenarios", ctxWithTenant, TestTenantID, []string{formation.Name}).Return(runtimes, nil).Once() 1381 return repo 1382 }, 1383 RuntimeContextRepo: func() *automock.RuntimeContextRepository { 1384 repo := &automock.RuntimeContextRepository{} 1385 repo.On("ListByScenarios", ctxWithTenant, TestTenantID, []string{formation.Name}).Return(runtimeContexts, nil).Once() 1386 return repo 1387 }, 1388 ExpectedOutput: formationAssignmentsForApplication, 1389 }, 1390 { 1391 Name: "Success does not create formation assignment for runtime and itself", 1392 Context: ctxWithTenant, 1393 ObjectType: graphql.FormationObjectTypeRuntime, 1394 FormationAssignmentRepo: func() *automock.FormationAssignmentRepository { 1395 repo := &automock.FormationAssignmentRepository{} 1396 repo.On("ListAllForObjectIDs", ctxWithTenant, TestTenantID, formation.ID, []string{applications[0].ID, runtimes[0].ID, objectID, runtimeContexts[0].ID}).Return(allAssignments, nil).Once() 1397 for i := range formationAssignmentsForRuntime { 1398 repo.On("GetByTargetAndSource", ctxWithTenant, formationAssignmentsForRuntime[i].Target, formationAssignmentsForRuntime[i].Source, TestTenantID, formationAssignmentsForApplication[i].FormationID).Return(nil, apperrors.NewNotFoundErrorWithType(resource.FormationAssignment)).Once() 1399 repo.On("Create", ctxWithTenant, formationAssignmentsForRuntime[i]).Return(nil).Once() 1400 } 1401 repo.On("ListForIDs", ctxWithTenant, TestTenantID, formationAssignmentIDs).Return(formationAssignmentsForRuntime, nil).Once() 1402 1403 return repo 1404 }, 1405 UIDService: func() *automock.UIDService { 1406 uidSvc := &automock.UIDService{} 1407 for i := range formationAssignmentIDs { 1408 uidSvc.On("Generate").Return(formationAssignmentIDs[i]).Once() 1409 } 1410 return uidSvc 1411 }, 1412 ApplicationRepo: func() *automock.ApplicationRepository { 1413 repo := &automock.ApplicationRepository{} 1414 repo.On("ListByScenariosNoPaging", ctxWithTenant, TestTenantID, []string{formation.Name}).Return(applications, nil).Once() 1415 return repo 1416 }, 1417 RuntimeRepo: func() *automock.RuntimeRepository { 1418 repo := &automock.RuntimeRepository{} 1419 repo.On("ListByScenarios", ctxWithTenant, TestTenantID, []string{formation.Name}).Return(append(runtimes, &model.Runtime{ID: objectID}), nil).Once() 1420 return repo 1421 }, 1422 RuntimeContextRepo: func() *automock.RuntimeContextRepository { 1423 repo := &automock.RuntimeContextRepository{} 1424 repo.On("ListByScenarios", ctxWithTenant, TestTenantID, []string{formation.Name}).Return(runtimeContexts, nil).Once() 1425 return repo 1426 }, 1427 ExpectedOutput: formationAssignmentsForRuntime, 1428 }, 1429 { 1430 Name: "Success does not create formation assignment for runtime context and itself", 1431 Context: ctxWithTenant, 1432 ObjectType: graphql.FormationObjectTypeRuntimeContext, 1433 FormationAssignmentRepo: func() *automock.FormationAssignmentRepository { 1434 repo := &automock.FormationAssignmentRepository{} 1435 repo.On("ListAllForObjectIDs", ctxWithTenant, TestTenantID, formation.ID, append(formationParticipantsIDs, objectID)).Return(allAssignments, nil).Once() 1436 for i := range formationAssignmentsForRuntimeContext { 1437 repo.On("GetByTargetAndSource", ctxWithTenant, formationAssignmentsForRuntimeContext[i].Target, formationAssignmentsForRuntimeContext[i].Source, TestTenantID, formationAssignmentsForApplication[i].FormationID).Return(nil, apperrors.NewNotFoundErrorWithType(resource.FormationAssignment)).Once() 1438 repo.On("Create", ctxWithTenant, formationAssignmentsForRuntimeContext[i]).Return(nil).Once() 1439 } 1440 repo.On("ListForIDs", ctxWithTenant, TestTenantID, formationAssignmentIDs).Return(formationAssignmentsForRuntimeContext, nil).Once() 1441 1442 return repo 1443 }, 1444 UIDService: func() *automock.UIDService { 1445 uidSvc := &automock.UIDService{} 1446 for i := range formationAssignmentIDs { 1447 uidSvc.On("Generate").Return(formationAssignmentIDs[i]).Once() 1448 } 1449 return uidSvc 1450 }, 1451 ApplicationRepo: func() *automock.ApplicationRepository { 1452 repo := &automock.ApplicationRepository{} 1453 repo.On("ListByScenariosNoPaging", ctxWithTenant, TestTenantID, []string{formation.Name}).Return(applications, nil).Once() 1454 return repo 1455 }, 1456 RuntimeRepo: func() *automock.RuntimeRepository { 1457 repo := &automock.RuntimeRepository{} 1458 repo.On("ListByScenarios", ctxWithTenant, TestTenantID, []string{formation.Name}).Return(runtimes, nil).Once() 1459 return repo 1460 }, 1461 RuntimeContextRepo: func() *automock.RuntimeContextRepository { 1462 repo := &automock.RuntimeContextRepository{} 1463 repo.On("ListByScenarios", ctxWithTenant, TestTenantID, []string{formation.Name}).Return(append(runtimeContexts, &model.RuntimeContext{ID: objectID}), nil).Once() 1464 repo.On("GetByID", ctxWithTenant, TestTenantID, objectID).Return(&model.RuntimeContext{RuntimeID: "random"}, nil) 1465 return repo 1466 }, 1467 ExpectedOutput: formationAssignmentsForRuntimeContext, 1468 }, 1469 { 1470 Name: "Success does not create formation assignment for runtime context and it's parent runtime", 1471 Context: ctxWithTenant, 1472 ObjectType: graphql.FormationObjectTypeRuntimeContext, 1473 FormationAssignmentRepo: func() *automock.FormationAssignmentRepository { 1474 repo := &automock.FormationAssignmentRepository{} 1475 repo.On("ListAllForObjectIDs", ctxWithTenant, TestTenantID, formation.ID, append(formationParticipantsIDs, objectID)).Return(allAssignments, nil).Once() 1476 for i := range formationAssignmentsForRuntimeContextWithParentInTheFormation { 1477 repo.On("GetByTargetAndSource", ctxWithTenant, formationAssignmentsForRuntimeContextWithParentInTheFormation[i].Target, formationAssignmentsForRuntimeContextWithParentInTheFormation[i].Source, TestTenantID, formationAssignmentsForApplication[i].FormationID).Return(nil, apperrors.NewNotFoundErrorWithType(resource.FormationAssignment)).Once() 1478 repo.On("Create", ctxWithTenant, formationAssignmentsForRuntimeContextWithParentInTheFormation[i]).Return(nil).Once() 1479 } 1480 repo.On("ListForIDs", ctxWithTenant, TestTenantID, formationAssignmentIDsRtmCtxParentInFormation).Return(formationAssignmentsForRuntimeContextWithParentInTheFormation, nil).Once() 1481 1482 return repo 1483 }, 1484 UIDService: func() *automock.UIDService { 1485 uidSvc := &automock.UIDService{} 1486 for i := range formationAssignmentIDsRtmCtxParentInFormation { 1487 uidSvc.On("Generate").Return(formationAssignmentIDsRtmCtxParentInFormation[i]).Once() 1488 } 1489 return uidSvc 1490 }, 1491 ApplicationRepo: func() *automock.ApplicationRepository { 1492 repo := &automock.ApplicationRepository{} 1493 repo.On("ListByScenariosNoPaging", ctxWithTenant, TestTenantID, []string{formation.Name}).Return(applications, nil).Once() 1494 return repo 1495 }, 1496 RuntimeRepo: func() *automock.RuntimeRepository { 1497 repo := &automock.RuntimeRepository{} 1498 repo.On("ListByScenarios", ctxWithTenant, TestTenantID, []string{formation.Name}).Return(runtimes, nil).Once() 1499 return repo 1500 }, 1501 RuntimeContextRepo: func() *automock.RuntimeContextRepository { 1502 repo := &automock.RuntimeContextRepository{} 1503 repo.On("ListByScenarios", ctxWithTenant, TestTenantID, []string{formation.Name}).Return(append(runtimeContexts, &model.RuntimeContext{ID: objectID}), nil).Once() 1504 repo.On("GetByID", ctxWithTenant, TestTenantID, objectID).Return(&model.RuntimeContext{RuntimeID: runtimes[0].ID}, nil) 1505 return repo 1506 }, 1507 ExpectedOutput: formationAssignmentsForRuntimeContextWithParentInTheFormation, 1508 }, 1509 { 1510 Name: "Error while listing applications", 1511 Context: ctxWithTenant, 1512 ObjectType: graphql.FormationObjectTypeApplication, 1513 FormationAssignmentRepo: unusedFormationAssignmentRepository, 1514 UIDService: unusedUIDService, 1515 ApplicationRepo: func() *automock.ApplicationRepository { 1516 repo := &automock.ApplicationRepository{} 1517 repo.On("ListByScenariosNoPaging", ctxWithTenant, TestTenantID, []string{formation.Name}).Return(nil, testErr).Once() 1518 return repo 1519 }, 1520 RuntimeRepo: unusedRuntimeRepository, 1521 RuntimeContextRepo: unusedRuntimeContextRepository, 1522 ExpectedOutput: nil, 1523 ExpectedErrorMsg: testErr.Error(), 1524 }, 1525 { 1526 Name: "Error while listing runtimes", 1527 Context: ctxWithTenant, 1528 ObjectType: graphql.FormationObjectTypeRuntime, 1529 FormationAssignmentRepo: unusedFormationAssignmentRepository, 1530 UIDService: unusedUIDService, 1531 ApplicationRepo: func() *automock.ApplicationRepository { 1532 repo := &automock.ApplicationRepository{} 1533 repo.On("ListByScenariosNoPaging", ctxWithTenant, TestTenantID, []string{formation.Name}).Return(applications, nil).Once() 1534 return repo 1535 }, 1536 RuntimeRepo: func() *automock.RuntimeRepository { 1537 repo := &automock.RuntimeRepository{} 1538 repo.On("ListByScenarios", ctxWithTenant, TestTenantID, []string{formation.Name}).Return(nil, testErr).Once() 1539 return repo 1540 }, 1541 RuntimeContextRepo: unusedRuntimeContextRepository, 1542 ExpectedOutput: nil, 1543 ExpectedErrorMsg: testErr.Error(), 1544 }, 1545 { 1546 Name: "Error while listing runtime contexts", 1547 Context: ctxWithTenant, 1548 ObjectType: graphql.FormationObjectTypeRuntimeContext, 1549 FormationAssignmentRepo: unusedFormationAssignmentRepository, 1550 UIDService: unusedUIDService, 1551 ApplicationRepo: func() *automock.ApplicationRepository { 1552 repo := &automock.ApplicationRepository{} 1553 repo.On("ListByScenariosNoPaging", ctxWithTenant, TestTenantID, []string{formation.Name}).Return(applications, nil).Once() 1554 return repo 1555 }, 1556 RuntimeRepo: func() *automock.RuntimeRepository { 1557 repo := &automock.RuntimeRepository{} 1558 repo.On("ListByScenarios", ctxWithTenant, TestTenantID, []string{formation.Name}).Return(runtimes, nil).Once() 1559 return repo 1560 }, 1561 RuntimeContextRepo: func() *automock.RuntimeContextRepository { 1562 repo := &automock.RuntimeContextRepository{} 1563 repo.On("ListByScenarios", ctxWithTenant, TestTenantID, []string{formation.Name}).Return(nil, testErr).Once() 1564 return repo 1565 }, 1566 ExpectedOutput: nil, 1567 ExpectedErrorMsg: testErr.Error(), 1568 }, 1569 { 1570 Name: "Error while listing all formation assignments", 1571 Context: ctxWithTenant, 1572 ObjectType: graphql.FormationObjectTypeRuntimeContext, 1573 UIDService: unusedUIDService, 1574 FormationAssignmentRepo: func() *automock.FormationAssignmentRepository { 1575 repo := &automock.FormationAssignmentRepository{} 1576 repo.On("ListAllForObjectIDs", ctxWithTenant, TestTenantID, formation.ID, append(formationParticipantsIDs, objectID)).Return(nil, testErr).Once() 1577 1578 return repo 1579 }, 1580 ApplicationRepo: func() *automock.ApplicationRepository { 1581 repo := &automock.ApplicationRepository{} 1582 repo.On("ListByScenariosNoPaging", ctxWithTenant, TestTenantID, []string{formation.Name}).Return(applications, nil).Once() 1583 return repo 1584 }, 1585 RuntimeRepo: func() *automock.RuntimeRepository { 1586 repo := &automock.RuntimeRepository{} 1587 repo.On("ListByScenarios", ctxWithTenant, TestTenantID, []string{formation.Name}).Return(runtimes, nil).Once() 1588 return repo 1589 }, 1590 RuntimeContextRepo: func() *automock.RuntimeContextRepository { 1591 repo := &automock.RuntimeContextRepository{} 1592 repo.On("ListByScenarios", ctxWithTenant, TestTenantID, []string{formation.Name}).Return(append(runtimeContexts, &model.RuntimeContext{ID: objectID}), nil).Once() 1593 return repo 1594 }, 1595 ExpectedOutput: nil, 1596 ExpectedErrorMsg: testErr.Error(), 1597 }, 1598 { 1599 Name: "Error while getting runtime context by ID", 1600 Context: ctxWithTenant, 1601 ObjectType: graphql.FormationObjectTypeRuntimeContext, 1602 FormationAssignmentRepo: func() *automock.FormationAssignmentRepository { 1603 repo := &automock.FormationAssignmentRepository{} 1604 repo.On("ListAllForObjectIDs", ctxWithTenant, TestTenantID, formation.ID, append(formationParticipantsIDs, objectID)).Return(allAssignments, nil).Once() 1605 return repo 1606 }, 1607 UIDService: unusedUIDService, 1608 ApplicationRepo: func() *automock.ApplicationRepository { 1609 repo := &automock.ApplicationRepository{} 1610 repo.On("ListByScenariosNoPaging", ctxWithTenant, TestTenantID, []string{formation.Name}).Return(applications, nil).Once() 1611 return repo 1612 }, 1613 RuntimeRepo: func() *automock.RuntimeRepository { 1614 repo := &automock.RuntimeRepository{} 1615 repo.On("ListByScenarios", ctxWithTenant, TestTenantID, []string{formation.Name}).Return(runtimes, nil).Once() 1616 return repo 1617 }, 1618 RuntimeContextRepo: func() *automock.RuntimeContextRepository { 1619 repo := &automock.RuntimeContextRepository{} 1620 repo.On("ListByScenarios", ctxWithTenant, TestTenantID, []string{formation.Name}).Return(append(runtimeContexts, &model.RuntimeContext{ID: objectID}), nil).Once() 1621 repo.On("GetByID", ctxWithTenant, TestTenantID, objectID).Return(nil, testErr) 1622 return repo 1623 }, 1624 ExpectedOutput: nil, 1625 ExpectedErrorMsg: testErr.Error(), 1626 }, 1627 { 1628 Name: "Error while creating formation assignment", 1629 Context: ctxWithTenant, 1630 ObjectType: graphql.FormationObjectTypeRuntimeContext, 1631 FormationAssignmentRepo: func() *automock.FormationAssignmentRepository { 1632 repo := &automock.FormationAssignmentRepository{} 1633 repo.On("ListAllForObjectIDs", ctxWithTenant, TestTenantID, formation.ID, append(formationParticipantsIDs, objectID)).Return(allAssignments, nil).Once() 1634 repo.On("GetByTargetAndSource", ctxWithTenant, formationAssignmentsForRuntimeContext[0].Target, formationAssignmentsForRuntimeContext[0].Source, TestTenantID, formationAssignmentsForRuntimeContext[0].FormationID).Return(nil, testErr).Once() 1635 return repo 1636 }, 1637 UIDService: func() *automock.UIDService { 1638 uidSvc := &automock.UIDService{} 1639 uidSvc.On("Generate").Return(formationAssignmentIDs[0]).Once() 1640 return uidSvc 1641 }, 1642 ApplicationRepo: func() *automock.ApplicationRepository { 1643 repo := &automock.ApplicationRepository{} 1644 repo.On("ListByScenariosNoPaging", ctxWithTenant, TestTenantID, []string{formation.Name}).Return(applications, nil).Once() 1645 return repo 1646 }, 1647 RuntimeRepo: func() *automock.RuntimeRepository { 1648 repo := &automock.RuntimeRepository{} 1649 repo.On("ListByScenarios", ctxWithTenant, TestTenantID, []string{formation.Name}).Return(runtimes, nil).Once() 1650 return repo 1651 }, 1652 RuntimeContextRepo: func() *automock.RuntimeContextRepository { 1653 repo := &automock.RuntimeContextRepository{} 1654 repo.On("ListByScenarios", ctxWithTenant, TestTenantID, []string{formation.Name}).Return(append(runtimeContexts, &model.RuntimeContext{ID: objectID}), nil).Once() 1655 repo.On("GetByID", ctxWithTenant, TestTenantID, objectID).Return(&model.RuntimeContext{RuntimeID: "random"}, nil) 1656 return repo 1657 }, 1658 ExpectedOutput: nil, 1659 ExpectedErrorMsg: testErr.Error(), 1660 }, 1661 { 1662 Name: "Error while listing formation assignments", 1663 Context: ctxWithTenant, 1664 ObjectType: graphql.FormationObjectTypeApplication, 1665 FormationAssignmentRepo: func() *automock.FormationAssignmentRepository { 1666 repo := &automock.FormationAssignmentRepository{} 1667 repo.On("ListAllForObjectIDs", ctxWithTenant, TestTenantID, formation.ID, formationParticipantsIDs).Return(allAssignments, nil).Once() 1668 for i := range formationAssignmentsForApplication { 1669 repo.On("GetByTargetAndSource", ctxWithTenant, formationAssignmentsForApplication[i].Target, formationAssignmentsForApplication[i].Source, TestTenantID, formationAssignmentsForApplication[i].FormationID).Return(nil, apperrors.NewNotFoundErrorWithType(resource.FormationAssignment)).Once() 1670 repo.On("Create", ctxWithTenant, formationAssignmentsForApplication[i]).Return(nil).Once() 1671 } 1672 repo.On("ListForIDs", ctxWithTenant, TestTenantID, formationAssignmentIDs).Return(nil, testErr).Once() 1673 1674 return repo 1675 }, 1676 UIDService: func() *automock.UIDService { 1677 uidSvc := &automock.UIDService{} 1678 for i := range formationAssignmentIDs { 1679 uidSvc.On("Generate").Return(formationAssignmentIDs[i]).Once() 1680 } 1681 return uidSvc 1682 }, 1683 ApplicationRepo: func() *automock.ApplicationRepository { 1684 repo := &automock.ApplicationRepository{} 1685 repo.On("ListByScenariosNoPaging", ctxWithTenant, TestTenantID, []string{formation.Name}).Return(applications, nil).Once() 1686 return repo 1687 }, 1688 RuntimeRepo: func() *automock.RuntimeRepository { 1689 repo := &automock.RuntimeRepository{} 1690 repo.On("ListByScenarios", ctxWithTenant, TestTenantID, []string{formation.Name}).Return(runtimes, nil).Once() 1691 return repo 1692 }, 1693 RuntimeContextRepo: func() *automock.RuntimeContextRepository { 1694 repo := &automock.RuntimeContextRepository{} 1695 repo.On("ListByScenarios", ctxWithTenant, TestTenantID, []string{formation.Name}).Return(runtimeContexts, nil).Once() 1696 return repo 1697 }, 1698 ExpectedOutput: nil, 1699 ExpectedErrorMsg: testErr.Error(), 1700 }, 1701 } 1702 1703 for _, testCase := range testCases { 1704 t.Run(testCase.Name, func(t *testing.T) { 1705 formationAssignmentRepo := testCase.FormationAssignmentRepo() 1706 appRepo := testCase.ApplicationRepo() 1707 runtimeRepo := testCase.RuntimeRepo() 1708 runtimeContextRepo := testCase.RuntimeContextRepo() 1709 uidSvc := testCase.UIDService() 1710 svc := formationassignment.NewService(formationAssignmentRepo, uidSvc, appRepo, runtimeRepo, runtimeContextRepo, nil, nil, nil, nil, nil, "", "") 1711 1712 // WHEN 1713 r, err := svc.GenerateAssignments(testCase.Context, TestTenantID, objectID, testCase.ObjectType, formation) 1714 1715 if testCase.ExpectedErrorMsg != "" { 1716 require.Error(t, err) 1717 require.Contains(t, err.Error(), testCase.ExpectedErrorMsg) 1718 } else { 1719 require.NoError(t, err) 1720 } 1721 1722 // THEN 1723 require.Equal(t, testCase.ExpectedOutput, r) 1724 1725 mock.AssertExpectationsForObjects(t, formationAssignmentRepo, appRepo, runtimeRepo, runtimeContextRepo) 1726 }) 1727 } 1728 } 1729 1730 func TestService_ProcessFormationAssignments(t *testing.T) { 1731 // GIVEN 1732 operationContainer := &operationContainer{content: []*formationassignment.AssignmentMappingPairWithOperation{}, err: testErr} 1733 appID := "app" 1734 appID2 := "app2" 1735 appTemplateID := "appTemplate" 1736 runtimeID := "runtime" 1737 runtimeCtxID := "runtimeCtx" 1738 matchedApplicationAssignment := &model.FormationAssignment{ 1739 Source: appID2, 1740 SourceType: TestSourceType, 1741 Target: appID, 1742 TargetType: "targetType", 1743 } 1744 matchedApplicationAssignmentReverse := &model.FormationAssignment{ 1745 Source: appID, 1746 SourceType: "targetType", 1747 Target: appID2, 1748 TargetType: TestSourceType, 1749 } 1750 1751 matchedRuntimeContextAssignment := &model.FormationAssignment{ 1752 Source: appID, 1753 SourceType: "APPLICATION", 1754 Target: runtimeCtxID, 1755 TargetType: "RUNTIME_CONTEXT", 1756 } 1757 matchedRuntimeContextAssignmentReverse := &model.FormationAssignment{ 1758 Source: runtimeCtxID, 1759 SourceType: "RUNTIME_CONTEXT", 1760 Target: appID, 1761 TargetType: "APPLICATION", 1762 } 1763 1764 sourseNotMatchedAssignment := &model.FormationAssignment{ 1765 Source: "source3", 1766 SourceType: "sourceType", 1767 Target: appID, 1768 TargetType: "targetType", 1769 } 1770 1771 sourseNotMatchedAssignmentReverse := &model.FormationAssignment{ 1772 Source: appID, 1773 SourceType: "targetType", 1774 Target: "source3", 1775 TargetType: "sourceType", 1776 } 1777 1778 targetNotMatchedAssignment := &model.FormationAssignment{ 1779 Source: "source4", 1780 SourceType: "sourceType", 1781 Target: "app3", 1782 TargetType: "targetType", 1783 } 1784 1785 targetNotMatchedAssignmentReverse := &model.FormationAssignment{ 1786 Source: "app3", 1787 SourceType: "targetType", 1788 Target: "source4", 1789 TargetType: "sourceType", 1790 } 1791 1792 appToAppRequests, appToAppInputTemplate, appToAppInputTemplateReverse := fixNotificationRequestAndReverseRequest(appID, appID2, []string{appID, appID2}, matchedApplicationAssignment, matchedApplicationAssignmentReverse, "application", "application", true) 1793 appToAppRequests2, appToAppInputTemplate2, appToAppInputTemplateReverse2 := fixNotificationRequestAndReverseRequest(appID, appID2, []string{appID, appID2}, matchedApplicationAssignment, matchedApplicationAssignmentReverse, "application", "application", true) 1794 rtmCtxToAppRequests, rtmCtxToAppInputTemplate, rtmCtxToAppInputTemplateReverse := fixNotificationRequestAndReverseRequest(runtimeID, appID, []string{appID, runtimeCtxID}, matchedRuntimeContextAssignment, matchedRuntimeContextAssignmentReverse, "runtime", "application", true) 1795 1796 appToAppRequestsWithAppTemplateWebhook, _, _ := fixNotificationRequestAndReverseRequest(appID, appID2, []string{appID, appID2}, matchedApplicationAssignment, matchedApplicationAssignmentReverse, "application", "application", true) 1797 appToAppRequestsWithAppTemplateWebhook[0].Webhook.ApplicationID = nil 1798 appToAppRequestsWithAppTemplateWebhook[0].Webhook.ApplicationTemplateID = str.Ptr(appTemplateID) 1799 1800 sourceNotMatchTemplateInput := &automock.TemplateInput{} 1801 sourceNotMatchTemplateInput.Mock.On("GetParticipantsIDs").Return([]string{"random", "notMatch"}).Times(1) 1802 1803 //TODO test two apps and one runtime to verify the mapping 1804 var testCases = []struct { 1805 Name string 1806 Context context.Context 1807 TemplateInput *automock.TemplateInput 1808 TemplateInputReverse *automock.TemplateInput 1809 FormationAssignments []*model.FormationAssignment 1810 Requests []*webhookclient.FormationAssignmentNotificationRequest 1811 Operation func(context.Context, *formationassignment.AssignmentMappingPairWithOperation) (bool, error) 1812 FormationOperation model.FormationOperation 1813 RuntimeContextToRuntimeMapping map[string]string 1814 ApplicationsToApplicationTemplatesMapping map[string]string 1815 ExpectedMappings []*formationassignment.AssignmentMappingPairWithOperation 1816 ExpectedErrorMsg string 1817 }{ 1818 { 1819 Name: "Success when match assignment for application", 1820 Context: ctxWithTenant, 1821 TemplateInput: appToAppInputTemplate, 1822 TemplateInputReverse: appToAppInputTemplateReverse, 1823 FormationAssignments: []*model.FormationAssignment{matchedApplicationAssignment, matchedApplicationAssignmentReverse}, 1824 Requests: appToAppRequests, 1825 Operation: operationContainer.appendThatDoesNotProcessedReverse, 1826 FormationOperation: assignOperation, 1827 ExpectedMappings: []*formationassignment.AssignmentMappingPairWithOperation{ 1828 { 1829 AssignmentMappingPair: &formationassignment.AssignmentMappingPair{ 1830 Assignment: &formationassignment.FormationAssignmentRequestMapping{ 1831 Request: appToAppRequests[0], 1832 FormationAssignment: matchedApplicationAssignment, 1833 }, 1834 ReverseAssignment: &formationassignment.FormationAssignmentRequestMapping{ 1835 Request: appToAppRequests[1], 1836 FormationAssignment: matchedApplicationAssignmentReverse, 1837 }, 1838 }, 1839 Operation: assignOperation, 1840 }, 1841 { 1842 AssignmentMappingPair: &formationassignment.AssignmentMappingPair{ 1843 Assignment: &formationassignment.FormationAssignmentRequestMapping{ 1844 Request: appToAppRequests[1], 1845 FormationAssignment: matchedApplicationAssignmentReverse, 1846 }, 1847 ReverseAssignment: &formationassignment.FormationAssignmentRequestMapping{ 1848 Request: appToAppRequests[0], 1849 FormationAssignment: matchedApplicationAssignment, 1850 }, 1851 }, 1852 Operation: assignOperation, 1853 }, 1854 }, 1855 }, 1856 { 1857 Name: "Success when match assignment for application when webhook comes from applicationTemplate", 1858 Context: ctxWithTenant, 1859 TemplateInput: appToAppInputTemplate, 1860 TemplateInputReverse: appToAppInputTemplateReverse, 1861 FormationAssignments: []*model.FormationAssignment{matchedApplicationAssignment, matchedApplicationAssignmentReverse}, 1862 Requests: appToAppRequestsWithAppTemplateWebhook, 1863 Operation: operationContainer.appendThatDoesNotProcessedReverse, 1864 ApplicationsToApplicationTemplatesMapping: map[string]string{appID: appTemplateID}, 1865 FormationOperation: assignOperation, 1866 ExpectedMappings: []*formationassignment.AssignmentMappingPairWithOperation{ 1867 { 1868 AssignmentMappingPair: &formationassignment.AssignmentMappingPair{ 1869 Assignment: &formationassignment.FormationAssignmentRequestMapping{ 1870 Request: appToAppRequestsWithAppTemplateWebhook[0], 1871 FormationAssignment: matchedApplicationAssignment, 1872 }, 1873 ReverseAssignment: &formationassignment.FormationAssignmentRequestMapping{ 1874 Request: appToAppRequestsWithAppTemplateWebhook[1], 1875 FormationAssignment: matchedApplicationAssignmentReverse, 1876 }, 1877 }, 1878 Operation: assignOperation, 1879 }, 1880 { 1881 AssignmentMappingPair: &formationassignment.AssignmentMappingPair{ 1882 Assignment: &formationassignment.FormationAssignmentRequestMapping{ 1883 Request: appToAppRequestsWithAppTemplateWebhook[1], 1884 FormationAssignment: matchedApplicationAssignmentReverse, 1885 }, 1886 ReverseAssignment: &formationassignment.FormationAssignmentRequestMapping{ 1887 Request: appToAppRequestsWithAppTemplateWebhook[0], 1888 FormationAssignment: matchedApplicationAssignment, 1889 }, 1890 }, 1891 Operation: assignOperation, 1892 }, 1893 }, 1894 }, 1895 { 1896 Name: "Does not process assignments multiple times", 1897 Context: ctxWithTenant, 1898 TemplateInput: appToAppInputTemplate2, 1899 TemplateInputReverse: appToAppInputTemplateReverse2, 1900 FormationAssignments: []*model.FormationAssignment{matchedApplicationAssignment, matchedApplicationAssignmentReverse}, 1901 Requests: appToAppRequests2, 1902 Operation: operationContainer.appendThatProcessedReverse, 1903 FormationOperation: assignOperation, 1904 ExpectedMappings: []*formationassignment.AssignmentMappingPairWithOperation{ 1905 { 1906 AssignmentMappingPair: &formationassignment.AssignmentMappingPair{ 1907 Assignment: &formationassignment.FormationAssignmentRequestMapping{ 1908 Request: appToAppRequests2[0], 1909 FormationAssignment: matchedApplicationAssignment, 1910 }, 1911 ReverseAssignment: &formationassignment.FormationAssignmentRequestMapping{ 1912 Request: appToAppRequests2[1], 1913 FormationAssignment: matchedApplicationAssignmentReverse, 1914 }, 1915 }, 1916 Operation: assignOperation, 1917 }, 1918 }, 1919 }, 1920 { 1921 Name: "Success when match assignment for runtimeContext", 1922 Context: ctxWithTenant, 1923 TemplateInput: rtmCtxToAppInputTemplate, 1924 TemplateInputReverse: rtmCtxToAppInputTemplateReverse, 1925 FormationAssignments: []*model.FormationAssignment{matchedRuntimeContextAssignment, matchedRuntimeContextAssignmentReverse}, 1926 Requests: rtmCtxToAppRequests, 1927 Operation: operationContainer.appendThatDoesNotProcessedReverse, 1928 RuntimeContextToRuntimeMapping: map[string]string{runtimeCtxID: runtimeID}, 1929 FormationOperation: assignOperation, 1930 ExpectedMappings: []*formationassignment.AssignmentMappingPairWithOperation{ 1931 { 1932 AssignmentMappingPair: &formationassignment.AssignmentMappingPair{ 1933 Assignment: &formationassignment.FormationAssignmentRequestMapping{ 1934 Request: rtmCtxToAppRequests[0], 1935 FormationAssignment: matchedRuntimeContextAssignment, 1936 }, 1937 ReverseAssignment: &formationassignment.FormationAssignmentRequestMapping{ 1938 Request: rtmCtxToAppRequests[1], 1939 FormationAssignment: matchedRuntimeContextAssignmentReverse, 1940 }, 1941 }, 1942 Operation: assignOperation, 1943 }, 1944 { 1945 AssignmentMappingPair: &formationassignment.AssignmentMappingPair{ 1946 Assignment: &formationassignment.FormationAssignmentRequestMapping{ 1947 Request: rtmCtxToAppRequests[1], 1948 FormationAssignment: matchedRuntimeContextAssignmentReverse, 1949 }, 1950 ReverseAssignment: &formationassignment.FormationAssignmentRequestMapping{ 1951 Request: rtmCtxToAppRequests[0], 1952 FormationAssignment: matchedRuntimeContextAssignment, 1953 }, 1954 }, 1955 Operation: assignOperation, 1956 }, 1957 }, 1958 }, 1959 { 1960 Name: "Success when no matching assignment for source found", 1961 Context: ctxWithTenant, 1962 TemplateInput: sourceNotMatchTemplateInput, 1963 TemplateInputReverse: &automock.TemplateInput{}, 1964 FormationAssignments: []*model.FormationAssignment{sourseNotMatchedAssignment, sourseNotMatchedAssignmentReverse}, 1965 Requests: []*webhookclient.FormationAssignmentNotificationRequest{ 1966 { 1967 Webhook: graphql.Webhook{ 1968 ApplicationID: &appID, 1969 }, 1970 Object: sourceNotMatchTemplateInput}, 1971 }, 1972 Operation: operationContainer.appendThatDoesNotProcessedReverse, 1973 FormationOperation: assignOperation, 1974 ExpectedMappings: []*formationassignment.AssignmentMappingPairWithOperation{ 1975 { 1976 AssignmentMappingPair: &formationassignment.AssignmentMappingPair{ 1977 Assignment: &formationassignment.FormationAssignmentRequestMapping{ 1978 Request: nil, 1979 FormationAssignment: sourseNotMatchedAssignment, 1980 }, 1981 ReverseAssignment: &formationassignment.FormationAssignmentRequestMapping{ 1982 Request: nil, 1983 FormationAssignment: sourseNotMatchedAssignmentReverse, 1984 }, 1985 }, 1986 Operation: assignOperation, 1987 }, 1988 { 1989 AssignmentMappingPair: &formationassignment.AssignmentMappingPair{ 1990 Assignment: &formationassignment.FormationAssignmentRequestMapping{ 1991 Request: nil, 1992 FormationAssignment: sourseNotMatchedAssignmentReverse, 1993 }, 1994 ReverseAssignment: &formationassignment.FormationAssignmentRequestMapping{ 1995 Request: nil, 1996 FormationAssignment: sourseNotMatchedAssignment, 1997 }, 1998 }, 1999 Operation: assignOperation, 2000 }, 2001 }, 2002 }, 2003 { 2004 Name: "Success when no match assignment for target found", 2005 Context: ctxWithTenant, 2006 TemplateInput: &automock.TemplateInput{}, 2007 TemplateInputReverse: &automock.TemplateInput{}, 2008 FormationAssignments: []*model.FormationAssignment{targetNotMatchedAssignment, targetNotMatchedAssignmentReverse}, 2009 Requests: appToAppRequests, 2010 Operation: operationContainer.appendThatDoesNotProcessedReverse, 2011 FormationOperation: assignOperation, 2012 ExpectedMappings: []*formationassignment.AssignmentMappingPairWithOperation{ 2013 { 2014 AssignmentMappingPair: &formationassignment.AssignmentMappingPair{ 2015 Assignment: &formationassignment.FormationAssignmentRequestMapping{ 2016 Request: nil, 2017 FormationAssignment: targetNotMatchedAssignment, 2018 }, 2019 ReverseAssignment: &formationassignment.FormationAssignmentRequestMapping{ 2020 Request: nil, 2021 FormationAssignment: targetNotMatchedAssignmentReverse, 2022 }, 2023 }, 2024 Operation: assignOperation, 2025 }, 2026 { 2027 AssignmentMappingPair: &formationassignment.AssignmentMappingPair{ 2028 Assignment: &formationassignment.FormationAssignmentRequestMapping{ 2029 Request: nil, 2030 FormationAssignment: targetNotMatchedAssignmentReverse, 2031 }, 2032 ReverseAssignment: &formationassignment.FormationAssignmentRequestMapping{ 2033 Request: nil, 2034 FormationAssignment: targetNotMatchedAssignment, 2035 }, 2036 }, 2037 Operation: assignOperation, 2038 }, 2039 }, 2040 }, 2041 { 2042 Name: "Fails on executing operation", 2043 Context: ctxWithTenant, 2044 TemplateInput: &automock.TemplateInput{}, 2045 TemplateInputReverse: &automock.TemplateInput{}, 2046 FormationAssignments: []*model.FormationAssignment{targetNotMatchedAssignment, targetNotMatchedAssignmentReverse}, 2047 Requests: appToAppRequests, 2048 Operation: operationContainer.fail, 2049 FormationOperation: assignOperation, 2050 ExpectedMappings: []*formationassignment.AssignmentMappingPairWithOperation{}, 2051 ExpectedErrorMsg: testErr.Error(), 2052 }, 2053 } 2054 for _, testCase := range testCases { 2055 t.Run(testCase.Name, func(t *testing.T) { 2056 svc := formationassignment.NewService(nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, "", "") 2057 2058 //WHEN 2059 err := svc.ProcessFormationAssignments(testCase.Context, testCase.FormationAssignments, testCase.RuntimeContextToRuntimeMapping, testCase.ApplicationsToApplicationTemplatesMapping, testCase.Requests, testCase.Operation, testCase.FormationOperation) 2060 2061 if testCase.ExpectedErrorMsg != "" { 2062 require.Error(t, err) 2063 require.Contains(t, err.Error(), testCase.ExpectedErrorMsg) 2064 } else { 2065 require.NoError(t, err) 2066 } 2067 2068 //THEN 2069 require.Equal(t, testCase.ExpectedMappings, operationContainer.content) 2070 2071 mock.AssertExpectationsForObjects(t, testCase.TemplateInput, testCase.TemplateInputReverse) 2072 operationContainer.clear() 2073 }) 2074 } 2075 } 2076 2077 func TestService_ProcessFormationAssignmentPair(t *testing.T) { 2078 // GIVEN 2079 config := "{\"key\":\"value\"}" 2080 ok := 200 2081 incomplete := 204 2082 2083 deletingStateAssignment := &model.FormationAssignment{ 2084 ID: TestID, 2085 TenantID: TestTenantID, 2086 Source: TestSource, 2087 SourceType: model.FormationAssignmentTypeApplication, 2088 Target: TestTarget, 2089 TargetType: model.FormationAssignmentTypeApplication, 2090 FormationID: formation.ID, 2091 State: string(model.DeletingAssignmentState), 2092 } 2093 marshaledErrTechnicalError, err := json.Marshal(formationassignment.AssignmentErrorWrapper{ 2094 Error: formationassignment.AssignmentError{ 2095 Message: testErr.Error(), 2096 ErrorCode: 1, 2097 }, 2098 }) 2099 require.NoError(t, err) 2100 2101 createErrorStateAssignment := &model.FormationAssignment{ 2102 ID: TestID, 2103 TenantID: TestTenantID, 2104 Source: TestSource, 2105 SourceType: model.FormationAssignmentTypeApplication, 2106 Target: TestTarget, 2107 TargetType: model.FormationAssignmentTypeApplication, 2108 FormationID: formation.ID, 2109 State: string(model.CreateErrorAssignmentState), 2110 Value: marshaledErrTechnicalError, 2111 } 2112 initialStateSelfReferencingAssignment := fixFormationAssignmentModelWithParameters(TestID, TestFormationID, TestTenantID, TestSource, TestSource, model.FormationAssignmentTypeApplication, model.FormationAssignmentTypeApplication, string(model.InitialAssignmentState), nil) 2113 initialStateAssignment := fixFormationAssignmentModelWithParameters(TestID, TestFormationID, TestTenantID, TestSource, TestTarget, model.FormationAssignmentTypeApplication, model.FormationAssignmentTypeApplication, string(model.InitialAssignmentState), nil) 2114 reverseInitialStateAssignment := fixFormationAssignmentModelWithParameters(TestID, TestFormationID, TestTenantID, TestTarget, TestSource, model.FormationAssignmentTypeApplication, model.FormationAssignmentTypeApplication, string(model.InitialAssignmentState), nil) 2115 readyStateAssignment := fixFormationAssignmentModelWithParameters(TestID, TestFormationID, TestTenantID, TestSource, TestTarget, model.FormationAssignmentTypeApplication, model.FormationAssignmentTypeApplication, string(model.ReadyAssignmentState), nil) 2116 readyStateSelfReferencingAssignment := fixFormationAssignmentModelWithParameters(TestID, TestFormationID, TestTenantID, TestSource, TestSource, model.FormationAssignmentTypeApplication, model.FormationAssignmentTypeApplication, string(model.ReadyAssignmentState), nil) 2117 configPendingStateWithConfigAssignment := fixFormationAssignmentModelWithParameters(TestID, TestFormationID, TestTenantID, TestSource, TestTarget, model.FormationAssignmentTypeApplication, model.FormationAssignmentTypeApplication, string(model.ConfigPendingAssignmentState), []byte(config)) 2118 configPendingStateAssignment := fixFormationAssignmentModelWithParameters(TestID, TestFormationID, TestTenantID, TestSource, TestTarget, model.FormationAssignmentTypeApplication, model.FormationAssignmentTypeApplication, string(model.ConfigPendingAssignmentState), nil) 2119 configAssignment := fixFormationAssignmentModelWithParameters(TestID, TestFormationID, TestTenantID, TestSource, TestTarget, model.FormationAssignmentTypeApplication, model.FormationAssignmentTypeApplication, string(model.ReadyAssignmentState), []byte(config)) 2120 reverseConfigAssignment := fixFormationAssignmentModelWithParameters(TestID, TestFormationID, TestTenantID, TestTarget, TestSource, model.FormationAssignmentTypeApplication, model.FormationAssignmentTypeApplication, string(model.ReadyAssignmentState), []byte(config)) 2121 reverseConfigPendingAssignment := fixFormationAssignmentModelWithParameters(TestID, TestFormationID, TestTenantID, TestTarget, TestSource, model.FormationAssignmentTypeApplication, model.FormationAssignmentTypeApplication, string(model.ConfigPendingAssignmentState), []byte(config)) 2122 2123 input := &webhook.FormationConfigurationChangeInput{ 2124 Operation: model.AssignFormation, 2125 } 2126 2127 reqWebhook := &webhookclient.FormationAssignmentNotificationRequest{ 2128 Webhook: graphql.Webhook{ 2129 ID: TestWebhookID, 2130 }, 2131 Object: input, 2132 CorrelationID: "", 2133 } 2134 2135 whMode := graphql.WebhookModeAsyncCallback 2136 reqWebhookWithAsyncCallbackMode := &webhookclient.FormationAssignmentNotificationRequest{ 2137 Webhook: graphql.Webhook{ 2138 ID: TestWebhookID, 2139 Mode: &whMode, 2140 Type: graphql.WebhookTypeConfigurationChanged, 2141 }, 2142 Object: input, 2143 CorrelationID: "", 2144 } 2145 2146 extendedFaNotificationInitialReq := fixExtendedFormationAssignmentNotificationReq(reqWebhook, initialStateAssignment) 2147 extendedFaNotificationInitialReqAsync := fixExtendedFormationAssignmentNotificationReq(reqWebhookWithAsyncCallbackMode, initialStateAssignment) 2148 2149 testCases := []struct { 2150 Name string 2151 Context context.Context 2152 FormationAssignmentRepo func() *automock.FormationAssignmentRepository 2153 NotificationService func() *automock.NotificationService 2154 FormationAssignmentPairWithOperation *formationassignment.AssignmentMappingPairWithOperation 2155 FormationRepo func() *automock.FormationRepository 2156 FAStatusService func() *automock.StatusService 2157 FANotificationSvc func() *automock.FaNotificationService 2158 ExpectedIsReverseProcessed bool 2159 ExpectedErrorMsg string 2160 }{ 2161 { 2162 Name: "Success: ready state assignment when assignment is already in ready state", 2163 Context: ctxWithTenant, 2164 FormationAssignmentPairWithOperation: &formationassignment.AssignmentMappingPairWithOperation{ 2165 AssignmentMappingPair: &formationassignment.AssignmentMappingPair{ 2166 Assignment: &formationassignment.FormationAssignmentRequestMapping{ 2167 Request: nil, 2168 FormationAssignment: fixFormationAssignmentModelWithIDAndTenantID(readyStateAssignment), 2169 }, 2170 ReverseAssignment: nil, 2171 }, 2172 Operation: model.AssignFormation, 2173 }, 2174 }, 2175 { 2176 Name: "Success: ready state assignment with no request", 2177 Context: ctxWithTenant, 2178 FormationAssignmentPairWithOperation: &formationassignment.AssignmentMappingPairWithOperation{ 2179 AssignmentMappingPair: &formationassignment.AssignmentMappingPair{ 2180 Assignment: &formationassignment.FormationAssignmentRequestMapping{ 2181 Request: nil, 2182 FormationAssignment: initialStateAssignment.Clone(), 2183 }, 2184 ReverseAssignment: nil, 2185 }, 2186 Operation: model.AssignFormation, 2187 }, 2188 FormationAssignmentRepo: func() *automock.FormationAssignmentRepository { 2189 repo := &automock.FormationAssignmentRepository{} 2190 repo.On("Exists", ctxWithTenant, TestID, TestTenantID).Return(true, nil).Once() 2191 repo.On("Update", ctxWithTenant, readyStateAssignment).Return(nil).Once() 2192 return repo 2193 }, 2194 }, 2195 { 2196 Name: "Error when there is no request and update fails", 2197 Context: ctxWithTenant, 2198 FormationAssignmentPairWithOperation: &formationassignment.AssignmentMappingPairWithOperation{ 2199 AssignmentMappingPair: &formationassignment.AssignmentMappingPair{ 2200 Assignment: &formationassignment.FormationAssignmentRequestMapping{ 2201 Request: nil, 2202 FormationAssignment: initialStateAssignment.Clone(), 2203 }, 2204 ReverseAssignment: nil, 2205 }, 2206 Operation: model.AssignFormation, 2207 }, 2208 FormationAssignmentRepo: func() *automock.FormationAssignmentRepository { 2209 repo := &automock.FormationAssignmentRepository{} 2210 repo.On("Exists", ctxWithTenant, TestID, TestTenantID).Return(true, nil).Once() 2211 repo.On("Update", ctxWithTenant, readyStateAssignment).Return(testErr).Once() 2212 return repo 2213 }, 2214 ExpectedErrorMsg: testErr.Error(), 2215 }, 2216 { 2217 Name: "Success: state in response body", 2218 Context: ctxWithTenant, 2219 FormationAssignmentRepo: func() *automock.FormationAssignmentRepository { 2220 repo := &automock.FormationAssignmentRepository{} 2221 repo.On("Get", ctxWithTenant, TestID, TestTenantID).Return(initialStateAssignment.Clone(), nil).Once() 2222 return repo 2223 }, 2224 NotificationService: func() *automock.NotificationService { 2225 notificationSvc := &automock.NotificationService{} 2226 notificationSvc.On("SendNotification", ctxWithTenant, extendedFaNotificationInitialReq).Return(&webhook.Response{ 2227 SuccessStatusCode: &ok, 2228 IncompleteStatusCode: &incomplete, 2229 ActualStatusCode: &incomplete, 2230 State: &configPendingState, 2231 }, nil) 2232 return notificationSvc 2233 }, 2234 FANotificationSvc: func() *automock.FaNotificationService { 2235 faNotificationSvc := &automock.FaNotificationService{} 2236 assignmentMapping := fixAssignmentMappingPairWithAssignmentAndRequest(initialStateAssignment.Clone(), reqWebhook) 2237 faNotificationSvc.On("GenerateFormationAssignmentNotificationExt", ctxWithTenant, assignmentMapping.Assignment, assignmentMapping.ReverseAssignment, model.AssignFormation).Return(extendedFaNotificationInitialReq, nil).Once() 2238 return faNotificationSvc 2239 }, 2240 FAStatusService: func() *automock.StatusService { 2241 updater := &automock.StatusService{} 2242 updater.On("UpdateWithConstraints", ctxWithTenant, configPendingStateAssignment, assignOperation).Return(nil).Once() 2243 return updater 2244 }, 2245 FormationAssignmentPairWithOperation: fixAssignmentMappingPairWithAssignmentAndRequest(initialStateAssignment, reqWebhook), 2246 }, 2247 { 2248 Name: "Success: incomplete state assignment", 2249 Context: ctxWithTenant, 2250 FormationAssignmentRepo: func() *automock.FormationAssignmentRepository { 2251 repo := &automock.FormationAssignmentRepository{} 2252 repo.On("Get", ctxWithTenant, TestID, TestTenantID).Return(initialStateAssignment, nil).Once() 2253 return repo 2254 }, 2255 NotificationService: func() *automock.NotificationService { 2256 notificationSvc := &automock.NotificationService{} 2257 notificationSvc.On("SendNotification", ctxWithTenant, extendedFaNotificationInitialReq).Return(&webhook.Response{ 2258 SuccessStatusCode: &ok, 2259 IncompleteStatusCode: &incomplete, 2260 ActualStatusCode: &incomplete, 2261 }, nil) 2262 return notificationSvc 2263 }, 2264 FANotificationSvc: func() *automock.FaNotificationService { 2265 faNotificationSvc := &automock.FaNotificationService{} 2266 assignmentMapping := fixAssignmentMappingPairWithAssignmentAndRequest(initialStateAssignment, reqWebhook) 2267 faNotificationSvc.On("GenerateFormationAssignmentNotificationExt", ctxWithTenant, assignmentMapping.Assignment, assignmentMapping.ReverseAssignment, model.AssignFormation).Return(extendedFaNotificationInitialReq, nil).Once() 2268 return faNotificationSvc 2269 }, 2270 FAStatusService: func() *automock.StatusService { 2271 updater := &automock.StatusService{} 2272 updater.On("UpdateWithConstraints", ctxWithTenant, configPendingStateAssignment, assignOperation).Return(nil).Once() 2273 return updater 2274 }, 2275 FormationAssignmentPairWithOperation: fixAssignmentMappingPairWithAssignmentAndRequest(initialStateAssignment, reqWebhook), 2276 }, 2277 { 2278 Name: "Success: do not update assignment if already in ready state", 2279 Context: ctxWithTenant, 2280 FormationAssignmentRepo: func() *automock.FormationAssignmentRepository { 2281 repo := &automock.FormationAssignmentRepository{} 2282 repo.On("Get", ctxWithTenant, TestID, TestTenantID).Return(readyStateAssignment, nil).Once() 2283 return repo 2284 }, 2285 NotificationService: func() *automock.NotificationService { 2286 notificationSvc := &automock.NotificationService{} 2287 notificationSvc.On("SendNotification", ctxWithTenant, extendedFaNotificationInitialReq).Return(&webhook.Response{ 2288 SuccessStatusCode: &ok, 2289 IncompleteStatusCode: &incomplete, 2290 ActualStatusCode: &incomplete, 2291 }, nil) 2292 return notificationSvc 2293 }, 2294 FANotificationSvc: func() *automock.FaNotificationService { 2295 faNotificationSvc := &automock.FaNotificationService{} 2296 assignmentMapping := fixAssignmentMappingPairWithAssignmentAndRequest(initialStateAssignment, reqWebhook) 2297 faNotificationSvc.On("GenerateFormationAssignmentNotificationExt", ctxWithTenant, assignmentMapping.Assignment, assignmentMapping.ReverseAssignment, model.AssignFormation).Return(extendedFaNotificationInitialReq, nil).Once() 2298 return faNotificationSvc 2299 }, 2300 FormationAssignmentPairWithOperation: fixAssignmentMappingPairWithAssignmentAndRequest(initialStateAssignment, reqWebhook), 2301 }, 2302 { 2303 Name: "Success: update assignment to ready state if it is self-referenced formation assignment", 2304 Context: ctxWithTenant, 2305 FormationAssignmentRepo: func() *automock.FormationAssignmentRepository { 2306 repo := &automock.FormationAssignmentRepository{} 2307 repo.On("Exists", ctxWithTenant, TestID, TestTenantID).Return(true, nil).Once() 2308 repo.On("Update", ctxWithTenant, readyStateSelfReferencingAssignment).Return(nil).Once() 2309 return repo 2310 }, 2311 FormationAssignmentPairWithOperation: fixAssignmentMappingPairWithAssignmentAndRequest(initialStateSelfReferencingAssignment.Clone(), reqWebhook), 2312 }, 2313 { 2314 Name: "Error: update assignment to ready state if it is self-referenced formation assignment fails on update", 2315 Context: ctxWithTenant, 2316 FormationAssignmentRepo: func() *automock.FormationAssignmentRepository { 2317 repo := &automock.FormationAssignmentRepository{} 2318 repo.On("Exists", ctxWithTenant, TestID, TestTenantID).Return(true, nil).Once() 2319 repo.On("Update", ctxWithTenant, readyStateSelfReferencingAssignment).Return(testErr).Once() 2320 return repo 2321 }, 2322 FormationAssignmentPairWithOperation: fixAssignmentMappingPairWithAssignmentAndRequest(initialStateSelfReferencingAssignment.Clone(), reqWebhook), 2323 ExpectedErrorMsg: testErr.Error(), 2324 }, 2325 { 2326 Name: "Error: can't generate formation assignment extended notification", 2327 Context: ctxWithTenant, 2328 FANotificationSvc: func() *automock.FaNotificationService { 2329 faNotificationSvc := &automock.FaNotificationService{} 2330 assignmentMapping := fixAssignmentMappingPairWithAssignmentAndRequest(initialStateAssignment.Clone(), reqWebhook) 2331 faNotificationSvc.On("GenerateFormationAssignmentNotificationExt", ctxWithTenant, assignmentMapping.Assignment, assignmentMapping.ReverseAssignment, model.AssignFormation).Return(nil, testErr).Once() 2332 return faNotificationSvc 2333 }, 2334 FormationAssignmentPairWithOperation: fixAssignmentMappingPairWithAssignmentAndRequest(initialStateAssignment, reqWebhook), 2335 ExpectedErrorMsg: testErr.Error(), 2336 }, 2337 { 2338 Name: "Error: state in body is not valid", 2339 Context: ctxWithTenant, 2340 NotificationService: func() *automock.NotificationService { 2341 notificationSvc := &automock.NotificationService{} 2342 notificationSvc.On("SendNotification", ctxWithTenant, extendedFaNotificationInitialReq).Return(&webhook.Response{ 2343 SuccessStatusCode: &ok, 2344 IncompleteStatusCode: &incomplete, 2345 ActualStatusCode: &incomplete, 2346 State: &invalidState, 2347 }, nil) 2348 return notificationSvc 2349 }, 2350 FANotificationSvc: func() *automock.FaNotificationService { 2351 faNotificationSvc := &automock.FaNotificationService{} 2352 assignmentMapping := fixAssignmentMappingPairWithAssignmentAndRequest(initialStateAssignment, reqWebhook) 2353 faNotificationSvc.On("GenerateFormationAssignmentNotificationExt", ctxWithTenant, assignmentMapping.Assignment, assignmentMapping.ReverseAssignment, model.AssignFormation).Return(extendedFaNotificationInitialReq, nil).Once() 2354 return faNotificationSvc 2355 }, 2356 FormationAssignmentPairWithOperation: fixAssignmentMappingPairWithAssignmentAndRequest(initialStateAssignment, reqWebhook), 2357 ExpectedErrorMsg: fmt.Sprintf("The provided state in the response %q is not valid.", invalidState), 2358 }, 2359 { 2360 Name: "Error: state in body is INITIAL, but the previous assignment state is DELETING", 2361 Context: ctxWithTenant, 2362 NotificationService: func() *automock.NotificationService { 2363 notificationSvc := &automock.NotificationService{} 2364 notificationSvc.On("SendNotification", ctxWithTenant, fixExtendedFormationAssignmentNotificationReq(reqWebhook, deletingStateAssignment)).Return(&webhook.Response{ 2365 SuccessStatusCode: &ok, 2366 IncompleteStatusCode: &incomplete, 2367 ActualStatusCode: &incomplete, 2368 State: &initialState, 2369 }, nil) 2370 return notificationSvc 2371 }, 2372 FANotificationSvc: func() *automock.FaNotificationService { 2373 faNotificationSvc := &automock.FaNotificationService{} 2374 assignmentMapping := fixAssignmentMappingPairWithAssignmentAndRequest(deletingStateAssignment, reqWebhook) 2375 faNotificationSvc.On("GenerateFormationAssignmentNotificationExt", ctxWithTenant, assignmentMapping.Assignment, assignmentMapping.ReverseAssignment, model.AssignFormation).Return(fixExtendedFormationAssignmentNotificationReq(reqWebhook, deletingStateAssignment), nil).Once() 2376 return faNotificationSvc 2377 }, 2378 FormationAssignmentPairWithOperation: fixAssignmentMappingPairWithAssignmentAndRequest(deletingStateAssignment, reqWebhook), 2379 ExpectedErrorMsg: fmt.Sprintf("The provided state in the response %q is not valid.", initialState), 2380 }, 2381 { 2382 Name: "Error: state in body is DELETE_ERROR, but the previous assignment state is INITIAL", 2383 Context: ctxWithTenant, 2384 NotificationService: func() *automock.NotificationService { 2385 notificationSvc := &automock.NotificationService{} 2386 notificationSvc.On("SendNotification", ctxWithTenant, extendedFaNotificationInitialReq).Return(&webhook.Response{ 2387 SuccessStatusCode: &ok, 2388 IncompleteStatusCode: &incomplete, 2389 ActualStatusCode: &incomplete, 2390 State: &deleteErrorState, 2391 }, nil) 2392 return notificationSvc 2393 }, 2394 FANotificationSvc: func() *automock.FaNotificationService { 2395 faNotificationSvc := &automock.FaNotificationService{} 2396 assignmentMapping := fixAssignmentMappingPairWithAssignmentAndRequest(initialStateAssignment, reqWebhook) 2397 faNotificationSvc.On("GenerateFormationAssignmentNotificationExt", ctxWithTenant, assignmentMapping.Assignment, assignmentMapping.ReverseAssignment, model.AssignFormation).Return(extendedFaNotificationInitialReq, nil).Once() 2398 return faNotificationSvc 2399 }, 2400 FormationAssignmentPairWithOperation: fixAssignmentMappingPairWithAssignmentAndRequest(initialStateAssignment, reqWebhook), 2401 ExpectedErrorMsg: fmt.Sprintf("The provided state in the response %q is not valid.", deleteErrorState), 2402 }, 2403 { 2404 Name: "Error: fail to get assignment", 2405 Context: ctxWithTenant, 2406 FormationAssignmentRepo: func() *automock.FormationAssignmentRepository { 2407 repo := &automock.FormationAssignmentRepository{} 2408 repo.On("Get", ctxWithTenant, TestID, TestTenantID).Return(nil, testErr).Once() 2409 return repo 2410 }, 2411 NotificationService: func() *automock.NotificationService { 2412 notificationSvc := &automock.NotificationService{} 2413 notificationSvc.On("SendNotification", ctxWithTenant, extendedFaNotificationInitialReq).Return(&webhook.Response{ 2414 SuccessStatusCode: &ok, 2415 IncompleteStatusCode: &incomplete, 2416 ActualStatusCode: &incomplete, 2417 }, nil) 2418 return notificationSvc 2419 }, 2420 FANotificationSvc: func() *automock.FaNotificationService { 2421 faNotificationSvc := &automock.FaNotificationService{} 2422 assignmentMapping := fixAssignmentMappingPairWithAssignmentAndRequest(initialStateAssignment, reqWebhook) 2423 faNotificationSvc.On("GenerateFormationAssignmentNotificationExt", ctxWithTenant, assignmentMapping.Assignment, assignmentMapping.ReverseAssignment, model.AssignFormation).Return(extendedFaNotificationInitialReq, nil).Once() 2424 return faNotificationSvc 2425 }, 2426 FormationAssignmentPairWithOperation: fixAssignmentMappingPairWithAssignmentAndRequest(initialStateAssignment, reqWebhook), 2427 ExpectedErrorMsg: testErr.Error(), 2428 }, 2429 { 2430 Name: "Success: update assignment to ready state", 2431 Context: ctxWithTenant, 2432 FormationAssignmentRepo: func() *automock.FormationAssignmentRepository { 2433 repo := &automock.FormationAssignmentRepository{} 2434 repo.On("Get", ctxWithTenant, TestID, TestTenantID).Return(initialStateAssignment, nil).Once() 2435 return repo 2436 }, 2437 NotificationService: func() *automock.NotificationService { 2438 notificationSvc := &automock.NotificationService{} 2439 notificationSvc.On("SendNotification", ctxWithTenant, extendedFaNotificationInitialReq).Return(&webhook.Response{ 2440 SuccessStatusCode: &ok, 2441 IncompleteStatusCode: &incomplete, 2442 ActualStatusCode: &ok, 2443 }, nil) 2444 return notificationSvc 2445 }, 2446 FANotificationSvc: func() *automock.FaNotificationService { 2447 faNotificationSvc := &automock.FaNotificationService{} 2448 assignmentMapping := fixAssignmentMappingPairWithAssignmentAndRequest(initialStateAssignment, reqWebhook) 2449 faNotificationSvc.On("GenerateFormationAssignmentNotificationExt", ctxWithTenant, assignmentMapping.Assignment, assignmentMapping.ReverseAssignment, model.AssignFormation).Return(extendedFaNotificationInitialReq, nil).Once() 2450 return faNotificationSvc 2451 }, 2452 FormationAssignmentPairWithOperation: fixAssignmentMappingPairWithAssignmentAndRequest(initialStateAssignment, reqWebhook), 2453 FAStatusService: func() *automock.StatusService { 2454 updater := &automock.StatusService{} 2455 updater.On("UpdateWithConstraints", ctxWithTenant, readyStateAssignment, assignOperation).Return(nil).Once() 2456 return updater 2457 }, 2458 }, 2459 { 2460 Name: "Error: incomplete state assignment fails on update", 2461 Context: ctxWithTenant, 2462 FormationAssignmentRepo: func() *automock.FormationAssignmentRepository { 2463 repo := &automock.FormationAssignmentRepository{} 2464 repo.On("Get", ctxWithTenant, TestID, TestTenantID).Return(initialStateAssignment, nil).Once() 2465 return repo 2466 }, 2467 NotificationService: func() *automock.NotificationService { 2468 notificationSvc := &automock.NotificationService{} 2469 notificationSvc.On("SendNotification", ctxWithTenant, extendedFaNotificationInitialReq).Return(&webhook.Response{ 2470 SuccessStatusCode: &ok, 2471 IncompleteStatusCode: &incomplete, 2472 ActualStatusCode: &incomplete, 2473 }, nil) 2474 return notificationSvc 2475 }, 2476 FANotificationSvc: func() *automock.FaNotificationService { 2477 faNotificationSvc := &automock.FaNotificationService{} 2478 assignmentMapping := fixAssignmentMappingPairWithAssignmentAndRequest(initialStateAssignment, reqWebhook) 2479 faNotificationSvc.On("GenerateFormationAssignmentNotificationExt", ctxWithTenant, assignmentMapping.Assignment, assignmentMapping.ReverseAssignment, model.AssignFormation).Return(extendedFaNotificationInitialReq, nil).Once() 2480 return faNotificationSvc 2481 }, 2482 FAStatusService: func() *automock.StatusService { 2483 updater := &automock.StatusService{} 2484 updater.On("UpdateWithConstraints", ctxWithTenant, configPendingStateAssignment, assignOperation).Return(testErr).Once() 2485 return updater 2486 }, 2487 FormationAssignmentPairWithOperation: fixAssignmentMappingPairWithAssignmentAndRequest(initialStateAssignment, reqWebhook), 2488 ExpectedErrorMsg: testErr.Error(), 2489 }, 2490 { 2491 Name: "Success with error from response", 2492 Context: ctxWithTenant, 2493 NotificationService: func() *automock.NotificationService { 2494 notificationSvc := &automock.NotificationService{} 2495 notificationSvc.On("SendNotification", ctxWithTenant, extendedFaNotificationInitialReq).Return(&webhook.Response{ 2496 ActualStatusCode: &incomplete, 2497 Error: str.Ptr(testErr.Error()), 2498 }, nil) 2499 return notificationSvc 2500 }, 2501 FANotificationSvc: func() *automock.FaNotificationService { 2502 faNotificationSvc := &automock.FaNotificationService{} 2503 assignmentMapping := fixAssignmentMappingPairWithAssignmentAndRequest(initialStateAssignment, reqWebhook) 2504 faNotificationSvc.On("GenerateFormationAssignmentNotificationExt", ctxWithTenant, assignmentMapping.Assignment, assignmentMapping.ReverseAssignment, model.AssignFormation).Return(extendedFaNotificationInitialReq, nil).Once() 2505 return faNotificationSvc 2506 }, 2507 FAStatusService: func() *automock.StatusService { 2508 updater := &automock.StatusService{} 2509 updater.On("SetAssignmentToErrorStateWithConstraints", ctxWithTenant, initialStateAssignment, testErr.Error(), formationassignment.AssignmentErrorCode(2), model.CreateErrorAssignmentState, assignOperation).Return(nil).Once() 2510 return updater 2511 }, 2512 FormationAssignmentPairWithOperation: fixAssignmentMappingPairWithAssignmentAndRequest(initialStateAssignment, reqWebhook), 2513 }, 2514 { 2515 Name: "Error with error from response while updating formation assignment", 2516 Context: ctxWithTenant, 2517 NotificationService: func() *automock.NotificationService { 2518 notificationSvc := &automock.NotificationService{} 2519 notificationSvc.On("SendNotification", ctxWithTenant, extendedFaNotificationInitialReq).Return(&webhook.Response{ 2520 ActualStatusCode: &incomplete, 2521 Error: str.Ptr(testErr.Error()), 2522 }, nil) 2523 return notificationSvc 2524 }, 2525 FANotificationSvc: func() *automock.FaNotificationService { 2526 faNotificationSvc := &automock.FaNotificationService{} 2527 assignmentMapping := fixAssignmentMappingPairWithAssignmentAndRequest(initialStateAssignment, reqWebhook) 2528 faNotificationSvc.On("GenerateFormationAssignmentNotificationExt", ctxWithTenant, assignmentMapping.Assignment, assignmentMapping.ReverseAssignment, model.AssignFormation).Return(extendedFaNotificationInitialReq, nil).Once() 2529 return faNotificationSvc 2530 }, 2531 FAStatusService: func() *automock.StatusService { 2532 updater := &automock.StatusService{} 2533 updater.On("SetAssignmentToErrorStateWithConstraints", ctxWithTenant, initialStateAssignment, testErr.Error(), formationassignment.AssignmentErrorCode(2), model.CreateErrorAssignmentState, assignOperation).Return(testErr).Once() 2534 return updater 2535 }, 2536 FormationAssignmentPairWithOperation: fixAssignmentMappingPairWithAssignmentAndRequest(initialStateAssignment, reqWebhook), 2537 ExpectedErrorMsg: testErr.Error(), 2538 }, 2539 { 2540 Name: "Success while sending notification failing to update state to create error", 2541 Context: ctxWithTenant, 2542 FormationAssignmentRepo: func() *automock.FormationAssignmentRepository { 2543 repo := &automock.FormationAssignmentRepository{} 2544 repo.On("Exists", ctxWithTenant, TestID, TestTenantID).Return(true, nil).Once() 2545 repo.On("Update", ctxWithTenant, createErrorStateAssignment).Return(nil).Once() 2546 return repo 2547 }, 2548 NotificationService: func() *automock.NotificationService { 2549 notificationSvc := &automock.NotificationService{} 2550 notificationSvc.On("SendNotification", ctxWithTenant, extendedFaNotificationInitialReq).Return(nil, testErr).Once() 2551 return notificationSvc 2552 }, 2553 FANotificationSvc: func() *automock.FaNotificationService { 2554 faNotificationSvc := &automock.FaNotificationService{} 2555 assignmentMapping := fixAssignmentMappingPairWithAssignmentAndRequest(initialStateAssignment, reqWebhook) 2556 faNotificationSvc.On("GenerateFormationAssignmentNotificationExt", ctxWithTenant, assignmentMapping.Assignment, assignmentMapping.ReverseAssignment, model.AssignFormation).Return(extendedFaNotificationInitialReq, nil).Once() 2557 return faNotificationSvc 2558 }, 2559 FormationAssignmentPairWithOperation: fixAssignmentMappingPairWithAssignmentAndRequest(initialStateAssignment, reqWebhook), 2560 }, 2561 { 2562 Name: "Error while sending notification while updating state to create error", 2563 Context: ctxWithTenant, 2564 FormationAssignmentRepo: func() *automock.FormationAssignmentRepository { 2565 repo := &automock.FormationAssignmentRepository{} 2566 repo.On("Exists", ctxWithTenant, TestID, TestTenantID).Return(true, nil).Once() 2567 repo.On("Update", ctxWithTenant, createErrorStateAssignment).Return(testErr).Once() 2568 return repo 2569 }, 2570 NotificationService: func() *automock.NotificationService { 2571 notificationSvc := &automock.NotificationService{} 2572 notificationSvc.On("SendNotification", ctxWithTenant, extendedFaNotificationInitialReq).Return(nil, testErr).Once() 2573 return notificationSvc 2574 }, 2575 FANotificationSvc: func() *automock.FaNotificationService { 2576 faNotificationSvc := &automock.FaNotificationService{} 2577 assignmentMapping := fixAssignmentMappingPairWithAssignmentAndRequest(initialStateAssignment, reqWebhook) 2578 faNotificationSvc.On("GenerateFormationAssignmentNotificationExt", ctxWithTenant, assignmentMapping.Assignment, assignmentMapping.ReverseAssignment, model.AssignFormation).Return(extendedFaNotificationInitialReq, nil).Once() 2579 return faNotificationSvc 2580 }, 2581 FormationAssignmentPairWithOperation: fixAssignmentMappingPairWithAssignmentAndRequest(initialStateAssignment, reqWebhook), 2582 ExpectedErrorMsg: testErr.Error(), 2583 }, 2584 { 2585 Name: "Success: webhook has mode ASYNC_CALLBACK", 2586 Context: ctxWithTenant, 2587 FormationAssignmentRepo: func() *automock.FormationAssignmentRepository { 2588 repo := &automock.FormationAssignmentRepository{} 2589 repo.On("Exists", ctxWithTenant, TestID, TestTenantID).Return(true, nil).Once() 2590 repo.On("Update", ctxWithTenant, initialStateAssignment).Return(nil).Once() 2591 return repo 2592 }, 2593 NotificationService: func() *automock.NotificationService { 2594 notificationSvc := &automock.NotificationService{} 2595 notificationSvc.On("SendNotification", ctxWithTenant, extendedFaNotificationInitialReqAsync).Return(&webhook.Response{ 2596 SuccessStatusCode: &ok, 2597 IncompleteStatusCode: &incomplete, 2598 ActualStatusCode: &ok, 2599 }, nil) 2600 return notificationSvc 2601 }, 2602 FANotificationSvc: func() *automock.FaNotificationService { 2603 faNotificationSvc := &automock.FaNotificationService{} 2604 assignmentMapping := fixAssignmentMappingPairWithAssignmentAndRequest(initialStateAssignment, reqWebhookWithAsyncCallbackMode) 2605 faNotificationSvc.On("GenerateFormationAssignmentNotificationExt", ctxWithTenant, assignmentMapping.Assignment, assignmentMapping.ReverseAssignment, model.AssignFormation).Return(extendedFaNotificationInitialReqAsync, nil).Once() 2606 return faNotificationSvc 2607 }, 2608 FormationAssignmentPairWithOperation: fixAssignmentMappingPairWithAssignmentAndRequest(initialStateAssignment, reqWebhookWithAsyncCallbackMode), 2609 }, 2610 { 2611 Name: "ERROR: webhook has mode ASYNC_CALLBACK but fails on update", 2612 Context: ctxWithTenant, 2613 FormationAssignmentRepo: func() *automock.FormationAssignmentRepository { 2614 repo := &automock.FormationAssignmentRepository{} 2615 repo.On("Exists", ctxWithTenant, TestID, TestTenantID).Return(true, nil).Once() 2616 repo.On("Update", ctxWithTenant, initialStateAssignment).Return(testErr).Once() 2617 return repo 2618 }, 2619 NotificationService: func() *automock.NotificationService { 2620 notificationSvc := &automock.NotificationService{} 2621 notificationSvc.On("SendNotification", ctxWithTenant, extendedFaNotificationInitialReqAsync).Return(&webhook.Response{ 2622 SuccessStatusCode: &ok, 2623 IncompleteStatusCode: &incomplete, 2624 ActualStatusCode: &ok, 2625 }, nil) 2626 return notificationSvc 2627 }, 2628 FANotificationSvc: func() *automock.FaNotificationService { 2629 faNotificationSvc := &automock.FaNotificationService{} 2630 assignmentMapping := fixAssignmentMappingPairWithAssignmentAndRequest(initialStateAssignment, reqWebhookWithAsyncCallbackMode) 2631 faNotificationSvc.On("GenerateFormationAssignmentNotificationExt", ctxWithTenant, assignmentMapping.Assignment, assignmentMapping.ReverseAssignment, model.AssignFormation).Return(extendedFaNotificationInitialReqAsync, nil).Once() 2632 return faNotificationSvc 2633 }, 2634 FormationAssignmentPairWithOperation: fixAssignmentMappingPairWithAssignmentAndRequest(initialStateAssignment, reqWebhookWithAsyncCallbackMode), 2635 ExpectedErrorMsg: testErr.Error(), 2636 }, 2637 { 2638 Name: "Success: assignment with config", 2639 Context: ctxWithTenant, 2640 FormationAssignmentRepo: func() *automock.FormationAssignmentRepository { 2641 repo := &automock.FormationAssignmentRepository{} 2642 repo.On("Get", ctxWithTenant, TestID, TestTenantID).Return(initialStateAssignment, nil).Once() 2643 return repo 2644 }, 2645 NotificationService: func() *automock.NotificationService { 2646 notificationSvc := &automock.NotificationService{} 2647 notificationSvc.On("SendNotification", ctxWithTenant, extendedFaNotificationInitialReq).Return(&webhook.Response{ 2648 Config: &config, 2649 SuccessStatusCode: &ok, 2650 IncompleteStatusCode: &incomplete, 2651 ActualStatusCode: &ok, 2652 }, nil) 2653 return notificationSvc 2654 }, 2655 FANotificationSvc: func() *automock.FaNotificationService { 2656 faNotificationSvc := &automock.FaNotificationService{} 2657 assignmentMapping := fixAssignmentMappingPairWithAssignmentAndRequest(initialStateAssignment, reqWebhook) 2658 faNotificationSvc.On("GenerateFormationAssignmentNotificationExt", ctxWithTenant, assignmentMapping.Assignment, assignmentMapping.ReverseAssignment, model.AssignFormation).Return(extendedFaNotificationInitialReq, nil).Once() 2659 return faNotificationSvc 2660 }, 2661 FAStatusService: func() *automock.StatusService { 2662 updater := &automock.StatusService{} 2663 updater.On("UpdateWithConstraints", ctxWithTenant, configAssignment, assignOperation).Return(nil).Once() 2664 return updater 2665 }, 2666 FormationAssignmentPairWithOperation: fixAssignmentMappingPairWithAssignmentAndRequest(initialStateAssignment, reqWebhook), 2667 }, 2668 } 2669 2670 for _, testCase := range testCases { 2671 t.Run(testCase.Name, func(t *testing.T) { 2672 repo := &automock.FormationAssignmentRepository{} 2673 if testCase.FormationAssignmentRepo != nil { 2674 repo = testCase.FormationAssignmentRepo() 2675 } 2676 notificationSvc := &automock.NotificationService{} 2677 if testCase.NotificationService != nil { 2678 notificationSvc = testCase.NotificationService() 2679 } 2680 formationRepo := &automock.FormationRepository{} 2681 if testCase.FormationRepo != nil { 2682 formationRepo = testCase.FormationRepo() 2683 } 2684 faStatusService := &automock.StatusService{} 2685 if testCase.FAStatusService != nil { 2686 faStatusService = testCase.FAStatusService() 2687 } 2688 faNotificationSvc := &automock.FaNotificationService{} 2689 if testCase.FANotificationSvc != nil { 2690 faNotificationSvc = testCase.FANotificationSvc() 2691 } 2692 2693 svc := formationassignment.NewService(repo, nil, nil, nil, nil, notificationSvc, faNotificationSvc, nil, formationRepo, faStatusService, rtmTypeLabelKey, appTypeLabelKey) 2694 2695 ///WHEN 2696 isReverseProcessed, err := svc.ProcessFormationAssignmentPair(testCase.Context, testCase.FormationAssignmentPairWithOperation) 2697 2698 require.Equal(t, testCase.ExpectedIsReverseProcessed, isReverseProcessed) 2699 if testCase.ExpectedErrorMsg != "" { 2700 require.Error(t, err) 2701 require.Contains(t, err.Error(), testCase.ExpectedErrorMsg) 2702 } else { 2703 require.NoError(t, err) 2704 } 2705 2706 //// THEN 2707 mock.AssertExpectationsForObjects(t, repo, notificationSvc, formationRepo, faStatusService, faNotificationSvc) 2708 }) 2709 } 2710 2711 t.Run("success when propagating config to reverse assignment", func(t *testing.T) { 2712 mappingRequest := &webhookclient.FormationAssignmentNotificationRequest{ 2713 Webhook: graphql.Webhook{ 2714 ID: TestWebhookID, 2715 }, 2716 } 2717 inputMock := &automock.TemplateInput{} 2718 inputMock.On("Clone").Return(inputMock) 2719 mappingRequest.Object = inputMock 2720 2721 reverseMappingRequest := &webhookclient.FormationAssignmentNotificationRequest{ 2722 Webhook: graphql.Webhook{ 2723 ID: TestReverseWebhookID, 2724 }, 2725 } 2726 reverseInputMock := &automock.TemplateInput{} 2727 reverseInputMock.On("Clone").Return(reverseInputMock) 2728 reverseMappingRequest.Object = reverseInputMock 2729 2730 notificationSvc := &automock.NotificationService{} 2731 extendedReqWithReverseFA := fixExtendedFormationAssignmentNotificationReq(mappingRequest, initialStateAssignment) 2732 extendedReqWithReverseFA.ReverseFormationAssignment = reverseInitialStateAssignment 2733 notificationSvc.On("SendNotification", ctxWithTenant, extendedReqWithReverseFA).Return(&webhook.Response{ 2734 Config: &config, 2735 SuccessStatusCode: &ok, 2736 IncompleteStatusCode: &incomplete, 2737 ActualStatusCode: &ok, 2738 }, nil) 2739 2740 extendedReqWithReverseFAForReverseNotification := fixExtendedFormationAssignmentNotificationReq(reverseMappingRequest, reverseInitialStateAssignment) 2741 extendedReqWithReverseFAForReverseNotification.ReverseFormationAssignment = configAssignment 2742 2743 notificationSvc.On("SendNotification", ctxWithTenant, extendedReqWithReverseFAForReverseNotification).Return(&webhook.Response{ 2744 Config: &config, 2745 SuccessStatusCode: &ok, 2746 IncompleteStatusCode: &incomplete, 2747 ActualStatusCode: &ok, 2748 }, nil) 2749 2750 repo := &automock.FormationAssignmentRepository{} 2751 repo.On("Get", ctxWithTenant, TestID, TestTenantID).Return(initialStateAssignment, nil).Twice() 2752 2753 assignmentPair := &formationassignment.AssignmentMappingPairWithOperation{ 2754 AssignmentMappingPair: &formationassignment.AssignmentMappingPair{ 2755 Assignment: &formationassignment.FormationAssignmentRequestMapping{ 2756 Request: mappingRequest, 2757 FormationAssignment: initialStateAssignment, 2758 }, 2759 ReverseAssignment: &formationassignment.FormationAssignmentRequestMapping{ 2760 Request: reverseMappingRequest, 2761 FormationAssignment: fixFormationAssignmentModelWithIDAndTenantID(reverseInitialStateAssignment), 2762 }, 2763 }, 2764 Operation: model.AssignFormation, 2765 } 2766 reverseInputMock.On("SetAssignment", assignmentPair.ReverseAssignment.FormationAssignment).Once() 2767 2768 inputMock.On("SetReverseAssignment", assignmentPair.ReverseAssignment.FormationAssignment).Once() 2769 2770 // once while processing the assignment and once when processing in the recursion call 2771 inputMock.On("SetAssignment", fixFormationAssignmentModelWithIDAndTenantID(configAssignment)).Twice() 2772 inputMock.On("SetReverseAssignment", fixFormationAssignmentModelWithIDAndTenantID(reverseConfigAssignment)).Once() 2773 2774 reverseInputMock.On("SetAssignment", fixFormationAssignmentModelWithIDAndTenantID(reverseConfigAssignment)).Once() 2775 // once while processing the assignment and once when processing in the recursion call 2776 reverseInputMock.On("SetReverseAssignment", fixFormationAssignmentModelWithIDAndTenantID(configAssignment)).Twice() 2777 2778 lblSvc := &automock.LabelService{} 2779 lblSvc.On("GetLabel", ctxWithTenant, TestTenantID, &model.LabelInput{ 2780 Key: appTypeLabelKey, 2781 ObjectID: TestTarget, 2782 ObjectType: model.ApplicationLabelableObject, 2783 }).Return(appLbl, nil).Once() 2784 lblSvc.On("GetLabel", ctxWithTenant, TestTenantID, &model.LabelInput{ 2785 Key: appTypeLabelKey, 2786 ObjectID: TestSource, 2787 ObjectType: model.ApplicationLabelableObject, 2788 }).Return(appLbl, nil).Once() 2789 2790 formationRepo := &automock.FormationRepository{} 2791 formationRepo.On("Get", ctxWithTenant, TestFormationID, TestTenantID).Return(formation, nil).Times(2) 2792 2793 faStatusService := &automock.StatusService{} 2794 faStatusService.On("UpdateWithConstraints", ctxWithTenant, configAssignment, assignOperation).Return(nil).Once() 2795 faStatusService.On("UpdateWithConstraints", ctxWithTenant, reverseConfigAssignment, assignOperation).Return(nil).Once() 2796 2797 faNotificationSvc := &automock.FaNotificationService{} 2798 assignmentMapping := fixAssignmentMappingPairWithAssignmentAndRequestWithReverse(initialStateAssignment, reverseInitialStateAssignment, mappingRequest, reverseMappingRequest) 2799 faNotificationSvc.On("GenerateFormationAssignmentNotificationExt", ctxWithTenant, assignmentMapping.Assignment, assignmentMapping.ReverseAssignment, model.AssignFormation).Return(extendedReqWithReverseFA, nil).Once() 2800 2801 assignmentMapping = fixAssignmentMappingPairWithAssignmentAndRequestWithReverse(reverseInitialStateAssignment, configAssignment, reverseMappingRequest, mappingRequest) 2802 faNotificationSvc.On("GenerateFormationAssignmentNotificationExt", ctxWithTenant, assignmentMapping.Assignment, assignmentMapping.ReverseAssignment, model.AssignFormation).Return(extendedReqWithReverseFAForReverseNotification, nil).Once() 2803 2804 svc := formationassignment.NewService(repo, nil, nil, nil, nil, notificationSvc, faNotificationSvc, lblSvc, formationRepo, faStatusService, rtmTypeLabelKey, appTypeLabelKey) 2805 2806 ///WHEN 2807 isReverseProcessed, err := svc.ProcessFormationAssignmentPair(ctxWithTenant, assignmentPair) 2808 require.NoError(t, err) 2809 require.True(t, isReverseProcessed) 2810 2811 mock.AssertExpectationsForObjects(t, inputMock, reverseInputMock, notificationSvc, repo, faStatusService, faNotificationSvc) 2812 }) 2813 t.Run("error when updating to database in recursion call", func(t *testing.T) { 2814 mappingRequest := &webhookclient.FormationAssignmentNotificationRequest{ 2815 Webhook: graphql.Webhook{ 2816 ID: TestWebhookID, 2817 }, 2818 } 2819 inputMock := &automock.TemplateInput{} 2820 inputMock.On("Clone").Return(inputMock) 2821 mappingRequest.Object = inputMock 2822 2823 reverseMappingRequest := &webhookclient.FormationAssignmentNotificationRequest{ 2824 Webhook: graphql.Webhook{ 2825 ID: TestReverseWebhookID, 2826 }, 2827 } 2828 reverseInputMock := &automock.TemplateInput{} 2829 reverseInputMock.On("Clone").Return(reverseInputMock) 2830 reverseMappingRequest.Object = reverseInputMock 2831 2832 notificationSvc := &automock.NotificationService{} 2833 extendedReqWithReverseFA := fixExtendedFormationAssignmentNotificationReq(mappingRequest, initialStateAssignment) 2834 extendedReqWithReverseFA.ReverseFormationAssignment = reverseInitialStateAssignment 2835 notificationSvc.On("SendNotification", ctxWithTenant, extendedReqWithReverseFA).Return(&webhook.Response{ 2836 Config: &config, 2837 SuccessStatusCode: &ok, 2838 IncompleteStatusCode: &incomplete, 2839 ActualStatusCode: &ok, 2840 }, nil) 2841 2842 extendedReqWithReverseFAForReverseNotification := fixExtendedFormationAssignmentNotificationReq(reverseMappingRequest, reverseInitialStateAssignment) 2843 extendedReqWithReverseFAForReverseNotification.ReverseFormationAssignment = configAssignment 2844 2845 notificationSvc.On("SendNotification", ctxWithTenant, extendedReqWithReverseFAForReverseNotification).Return(&webhook.Response{ 2846 Config: &config, 2847 SuccessStatusCode: &ok, 2848 IncompleteStatusCode: &incomplete, 2849 ActualStatusCode: &ok, 2850 }, nil) 2851 2852 repo := &automock.FormationAssignmentRepository{} 2853 repo.On("Get", ctxWithTenant, TestID, TestTenantID).Return(initialStateAssignment, nil).Twice() 2854 2855 assignmentPair := &formationassignment.AssignmentMappingPairWithOperation{ 2856 AssignmentMappingPair: &formationassignment.AssignmentMappingPair{ 2857 Assignment: &formationassignment.FormationAssignmentRequestMapping{ 2858 Request: mappingRequest, 2859 FormationAssignment: initialStateAssignment, 2860 }, 2861 ReverseAssignment: &formationassignment.FormationAssignmentRequestMapping{ 2862 Request: reverseMappingRequest, 2863 FormationAssignment: fixFormationAssignmentModelWithIDAndTenantID(reverseInitialStateAssignment), 2864 }, 2865 }, 2866 Operation: model.AssignFormation, 2867 } 2868 inputMock.On("SetAssignment", fixFormationAssignmentModelWithIDAndTenantID(configAssignment)).Once() 2869 inputMock.On("SetReverseAssignment", fixFormationAssignmentModelWithIDAndTenantID(reverseInitialStateAssignment)).Once() 2870 2871 reverseInputMock.On("SetAssignment", assignmentPair.ReverseAssignment.FormationAssignment).Once() 2872 reverseInputMock.On("SetReverseAssignment", fixFormationAssignmentModelWithIDAndTenantID(configAssignment)).Once() 2873 2874 lblSvc := &automock.LabelService{} 2875 lblSvc.On("GetLabel", ctxWithTenant, TestTenantID, &model.LabelInput{ 2876 Key: appTypeLabelKey, 2877 ObjectID: TestTarget, 2878 ObjectType: model.ApplicationLabelableObject, 2879 }).Return(appLbl, nil).Once() 2880 lblSvc.On("GetLabel", ctxWithTenant, TestTenantID, &model.LabelInput{ 2881 Key: appTypeLabelKey, 2882 ObjectID: TestSource, 2883 ObjectType: model.ApplicationLabelableObject, 2884 }).Return(appLbl, nil).Once() 2885 2886 formationRepo := &automock.FormationRepository{} 2887 formationRepo.On("Get", ctxWithTenant, TestFormationID, TestTenantID).Return(formation, nil).Times(2) 2888 2889 faStatusService := &automock.StatusService{} 2890 faStatusService.On("UpdateWithConstraints", ctxWithTenant, configAssignment, assignOperation).Return(nil).Once() 2891 faStatusService.On("UpdateWithConstraints", ctxWithTenant, reverseConfigAssignment, assignOperation).Return(testErr).Once() 2892 2893 faNotificationSvc := &automock.FaNotificationService{} 2894 assignmentMapping := fixAssignmentMappingPairWithAssignmentAndRequestWithReverse(initialStateAssignment, reverseInitialStateAssignment, mappingRequest, reverseMappingRequest) 2895 faNotificationSvc.On("GenerateFormationAssignmentNotificationExt", ctxWithTenant, assignmentMapping.Assignment, assignmentMapping.ReverseAssignment, model.AssignFormation).Return(extendedReqWithReverseFA, nil).Once() 2896 2897 assignmentMapping = fixAssignmentMappingPairWithAssignmentAndRequestWithReverse(reverseInitialStateAssignment, configAssignment, reverseMappingRequest, mappingRequest) 2898 faNotificationSvc.On("GenerateFormationAssignmentNotificationExt", ctxWithTenant, assignmentMapping.Assignment, assignmentMapping.ReverseAssignment, model.AssignFormation).Return(extendedReqWithReverseFAForReverseNotification, nil).Once() 2899 2900 svc := formationassignment.NewService(repo, nil, nil, nil, nil, notificationSvc, faNotificationSvc, lblSvc, formationRepo, faStatusService, rtmTypeLabelKey, appTypeLabelKey) 2901 2902 ///WHEN 2903 isReverseProcessed, err := svc.ProcessFormationAssignmentPair(ctxWithTenant, assignmentPair) 2904 require.Error(t, err) 2905 require.Contains(t, err.Error(), testErr.Error()) 2906 require.True(t, isReverseProcessed) 2907 2908 mock.AssertExpectationsForObjects(t, inputMock, reverseInputMock, notificationSvc, repo, faStatusService, faNotificationSvc) 2909 }) 2910 t.Run("success when reaching the maximum depth limit with two config pending assignments that return unfinished configurations", func(t *testing.T) { 2911 mappingRequest := &webhookclient.FormationAssignmentNotificationRequest{ 2912 Webhook: graphql.Webhook{ 2913 ID: TestWebhookID, 2914 }, 2915 } 2916 inputMock := &automock.TemplateInput{} 2917 inputMock.On("Clone").Return(inputMock).Times(21) 2918 mappingRequest.Object = inputMock 2919 2920 reverseMappingRequest := &webhookclient.FormationAssignmentNotificationRequest{ 2921 Webhook: graphql.Webhook{ 2922 ID: TestReverseWebhookID, 2923 }, 2924 } 2925 reverseInputMock := &automock.TemplateInput{} 2926 reverseInputMock.On("Clone").Return(reverseInputMock).Times(21) 2927 reverseMappingRequest.Object = reverseInputMock 2928 2929 notificationSvc := &automock.NotificationService{} 2930 extendedReqWithReverseFA := fixExtendedFormationAssignmentNotificationReq(mappingRequest, initialStateAssignment) 2931 extendedReqWithReverseFA.ReverseFormationAssignment = reverseInitialStateAssignment 2932 notificationSvc.On("SendNotification", ctxWithTenant, extendedReqWithReverseFA).Return(&webhook.Response{ 2933 Config: &config, 2934 SuccessStatusCode: &ok, 2935 IncompleteStatusCode: &incomplete, 2936 ActualStatusCode: &incomplete, 2937 }, nil) 2938 2939 extendedReqWithReverseFAForReverseNotification := fixExtendedFormationAssignmentNotificationReq(reverseMappingRequest, reverseInitialStateAssignment) 2940 extendedReqWithReverseFAForReverseNotification.ReverseFormationAssignment = configPendingStateWithConfigAssignment 2941 2942 notificationSvc.On("SendNotification", ctxWithTenant, extendedReqWithReverseFAForReverseNotification).Return(&webhook.Response{ 2943 Config: &config, 2944 SuccessStatusCode: &ok, 2945 IncompleteStatusCode: &incomplete, 2946 ActualStatusCode: &incomplete, 2947 }, nil) 2948 2949 extendedReqWithReverseFAForReverseNotificationSecond := fixExtendedFormationAssignmentNotificationReq(mappingRequest, configPendingStateWithConfigAssignment) 2950 extendedReqWithReverseFAForReverseNotificationSecond.ReverseFormationAssignment = reverseConfigPendingAssignment 2951 2952 notificationSvc.On("SendNotification", ctxWithTenant, extendedReqWithReverseFAForReverseNotificationSecond).Return(&webhook.Response{ 2953 Config: &config, 2954 SuccessStatusCode: &ok, 2955 IncompleteStatusCode: &incomplete, 2956 ActualStatusCode: &incomplete, 2957 }, nil) 2958 2959 extendedReqWithReverseFAForReverseNotificationThird := fixExtendedFormationAssignmentNotificationReq(reverseMappingRequest, reverseConfigPendingAssignment) 2960 extendedReqWithReverseFAForReverseNotificationThird.ReverseFormationAssignment = configPendingStateWithConfigAssignment 2961 2962 notificationSvc.On("SendNotification", ctxWithTenant, extendedReqWithReverseFAForReverseNotificationThird).Return(&webhook.Response{ 2963 Config: &config, 2964 SuccessStatusCode: &ok, 2965 IncompleteStatusCode: &incomplete, 2966 ActualStatusCode: &incomplete, 2967 }, nil) 2968 2969 repo := &automock.FormationAssignmentRepository{} 2970 repo.On("Get", ctxWithTenant, TestID, TestTenantID).Return(initialStateAssignment, nil).Times(11) 2971 2972 assignmentPair := &formationassignment.AssignmentMappingPairWithOperation{ 2973 AssignmentMappingPair: &formationassignment.AssignmentMappingPair{ 2974 Assignment: &formationassignment.FormationAssignmentRequestMapping{ 2975 Request: mappingRequest, 2976 FormationAssignment: initialStateAssignment.Clone(), 2977 }, 2978 ReverseAssignment: &formationassignment.FormationAssignmentRequestMapping{ 2979 Request: reverseMappingRequest, 2980 FormationAssignment: fixFormationAssignmentModelWithIDAndTenantID(reverseInitialStateAssignment), 2981 }, 2982 }, 2983 Operation: model.AssignFormation, 2984 } 2985 reverseInputMock.On("SetAssignment", assignmentPair.ReverseAssignment.FormationAssignment) 2986 2987 inputMock.On("SetReverseAssignment", assignmentPair.ReverseAssignment.FormationAssignment) 2988 2989 inputMock.On("SetAssignment", fixFormationAssignmentModelWithIDAndTenantID(configPendingStateWithConfigAssignment)) 2990 inputMock.On("SetReverseAssignment", fixFormationAssignmentModelWithIDAndTenantID(reverseConfigPendingAssignment)) 2991 2992 reverseInputMock.On("SetAssignment", fixFormationAssignmentModelWithIDAndTenantID(reverseConfigPendingAssignment)) 2993 reverseInputMock.On("SetReverseAssignment", fixFormationAssignmentModelWithIDAndTenantID(configPendingStateWithConfigAssignment)) 2994 2995 lblSvc := &automock.LabelService{} 2996 lblSvc.On("GetLabel", ctxWithTenant, TestTenantID, &model.LabelInput{ 2997 Key: appTypeLabelKey, 2998 ObjectID: TestTarget, 2999 ObjectType: model.ApplicationLabelableObject, 3000 }).Return(appLbl, nil) 3001 lblSvc.On("GetLabel", ctxWithTenant, TestTenantID, &model.LabelInput{ 3002 Key: appTypeLabelKey, 3003 ObjectID: TestSource, 3004 ObjectType: model.ApplicationLabelableObject, 3005 }).Return(appLbl, nil) 3006 3007 formationRepo := &automock.FormationRepository{} 3008 formationRepo.On("Get", ctxWithTenant, TestFormationID, TestTenantID).Return(formation, nil) 3009 3010 faStatusService := &automock.StatusService{} 3011 faStatusService.On("UpdateWithConstraints", ctxWithTenant, configPendingStateWithConfigAssignment, assignOperation).Return(nil) 3012 faStatusService.On("UpdateWithConstraints", ctxWithTenant, reverseConfigPendingAssignment, assignOperation).Return(nil) 3013 3014 faNotificationSvc := &automock.FaNotificationService{} 3015 assignmentMapping := fixAssignmentMappingPairWithAssignmentAndRequestWithReverse(initialStateAssignment, reverseInitialStateAssignment, mappingRequest, reverseMappingRequest) 3016 faNotificationSvc.On("GenerateFormationAssignmentNotificationExt", ctxWithTenant, assignmentMapping.Assignment, assignmentMapping.ReverseAssignment, model.AssignFormation).Return(extendedReqWithReverseFA, nil) 3017 3018 assignmentMapping = fixAssignmentMappingPairWithAssignmentAndRequestWithReverse(reverseInitialStateAssignment, configAssignment, reverseMappingRequest, mappingRequest) 3019 faNotificationSvc.On("GenerateFormationAssignmentNotificationExt", ctxWithTenant, assignmentMapping.Assignment, assignmentMapping.ReverseAssignment, model.AssignFormation).Return(extendedReqWithReverseFAForReverseNotification, nil) 3020 3021 assignmentMapping = fixAssignmentMappingPairWithAssignmentAndRequestWithReverse(reverseInitialStateAssignment, configPendingStateWithConfigAssignment, reverseMappingRequest, mappingRequest) 3022 faNotificationSvc.On("GenerateFormationAssignmentNotificationExt", ctxWithTenant, assignmentMapping.Assignment, assignmentMapping.ReverseAssignment, model.AssignFormation).Return(extendedReqWithReverseFAForReverseNotification, nil) 3023 3024 assignmentMapping = fixAssignmentMappingPairWithAssignmentAndRequestWithReverse(configPendingStateWithConfigAssignment, reverseConfigPendingAssignment, mappingRequest, reverseMappingRequest) 3025 faNotificationSvc.On("GenerateFormationAssignmentNotificationExt", ctxWithTenant, assignmentMapping.Assignment, assignmentMapping.ReverseAssignment, model.AssignFormation).Return(extendedReqWithReverseFAForReverseNotificationSecond, nil) 3026 3027 assignmentMapping = fixAssignmentMappingPairWithAssignmentAndRequestWithReverse(reverseConfigPendingAssignment, configPendingStateWithConfigAssignment, reverseMappingRequest, mappingRequest) 3028 faNotificationSvc.On("GenerateFormationAssignmentNotificationExt", ctxWithTenant, assignmentMapping.Assignment, assignmentMapping.ReverseAssignment, model.AssignFormation).Return(extendedReqWithReverseFAForReverseNotificationThird, nil) 3029 3030 svc := formationassignment.NewService(repo, nil, nil, nil, nil, notificationSvc, faNotificationSvc, lblSvc, formationRepo, faStatusService, rtmTypeLabelKey, appTypeLabelKey) 3031 3032 ///WHEN 3033 isReverseProcessed, err := svc.ProcessFormationAssignmentPair(ctxWithTenant, assignmentPair) 3034 require.NoError(t, err) 3035 require.True(t, isReverseProcessed) 3036 3037 mock.AssertExpectationsForObjects(t, inputMock, reverseInputMock, notificationSvc, repo, faStatusService) 3038 }) 3039 } 3040 3041 func TestService_CleanupFormationAssignment(t *testing.T) { 3042 // GIVEN 3043 ok := http.StatusOK 3044 accepted := http.StatusNoContent 3045 notFound := http.StatusNotFound 3046 mode := graphql.WebhookModeAsyncCallback 3047 3048 req := &webhookclient.FormationAssignmentNotificationRequest{ 3049 Webhook: graphql.Webhook{}, 3050 Object: nil, 3051 CorrelationID: "", 3052 } 3053 3054 callbackReq := &webhookclient.FormationAssignmentNotificationRequest{ 3055 Webhook: graphql.Webhook{ 3056 Mode: &mode, 3057 }, 3058 Object: nil, 3059 CorrelationID: "", 3060 } 3061 3062 config := "{\"key\":\"value\"}" 3063 errMsg := "Test Error" 3064 3065 configAssignmentWithTenantAndID := fixFormationAssignmentModelWithParameters(TestID, formation.ID, TestTenantID, TestSource, TestTarget, model.FormationAssignmentTypeApplication, model.FormationAssignmentTypeApplication, string(model.ReadyAssignmentState), []byte(config)) 3066 3067 assignmentWithTenantAndIDInDeletingState := &model.FormationAssignment{ 3068 ID: TestID, 3069 FormationID: formation.ID, 3070 TenantID: TestTenantID, 3071 Source: TestSource, 3072 SourceType: model.FormationAssignmentTypeApplication, 3073 Target: TestTarget, 3074 TargetType: model.FormationAssignmentTypeApplication, 3075 State: string(model.DeletingAssignmentState), 3076 Value: nil, 3077 } 3078 3079 marshaledErrTechnicalError, err := json.Marshal(formationassignment.AssignmentErrorWrapper{ 3080 Error: formationassignment.AssignmentError{ 3081 Message: "Error while deleting assignment: config propagation is not supported on unassign notifications", 3082 ErrorCode: 2, 3083 }, 3084 }) 3085 require.NoError(t, err) 3086 3087 assignmentWithTenantAndIDInDeleteError := fixFormationAssignmentModelWithParameters(TestID, formation.ID, TestTenantID, TestSource, TestTarget, model.FormationAssignmentTypeApplication, model.FormationAssignmentTypeApplication, string(model.DeleteErrorFormationState), marshaledErrTechnicalError) 3088 3089 marshaledDeleteError, err := json.Marshal(formationassignment.AssignmentErrorWrapper{ 3090 Error: formationassignment.AssignmentError{ 3091 Message: "while deleting formation assignment with ID: \"c861c3db-1265-4143-a05c-1ced1291d816\": Test Error", 3092 ErrorCode: 1, 3093 }, 3094 }) 3095 require.NoError(t, err) 3096 3097 deleteErrorStateAssignmentDeleteErr := fixFormationAssignmentModelWithParameters(TestID, formation.ID, TestTenantID, TestSource, TestTarget, model.FormationAssignmentTypeApplication, model.FormationAssignmentTypeApplication, string(model.DeleteErrorFormationState), marshaledDeleteError) 3098 3099 marshaledFailedRequestTechnicalErr, err := json.Marshal(formationassignment.AssignmentErrorWrapper{ 3100 Error: formationassignment.AssignmentError{ 3101 Message: errMsg, 3102 ErrorCode: 1, 3103 }, 3104 }) 3105 require.NoError(t, err) 3106 3107 deleteErrorStateAssignmentTechnicalErr := fixFormationAssignmentModelWithParameters(TestID, formation.ID, TestTenantID, TestSource, TestTarget, model.FormationAssignmentTypeApplication, model.FormationAssignmentTypeApplication, string(model.DeleteErrorFormationState), marshaledFailedRequestTechnicalErr) 3108 3109 marshaledWhileDeletingError, err := json.Marshal(formationassignment.AssignmentErrorWrapper{ 3110 Error: formationassignment.AssignmentError{ 3111 Message: "error while deleting assignment", 3112 ErrorCode: 1, 3113 }, 3114 }) 3115 require.NoError(t, err) 3116 deleteErrorStateAssignmentWhileDeletingErr := fixFormationAssignmentModelWithParameters(TestID, formation.ID, TestTenantID, TestSource, TestTarget, model.FormationAssignmentTypeApplication, model.FormationAssignmentTypeApplication, string(model.DeleteErrorFormationState), marshaledWhileDeletingError) 3117 3118 successResponse := &webhook.Response{ActualStatusCode: &ok, SuccessStatusCode: &ok, IncompleteStatusCode: &accepted} 3119 incompleteResponse := &webhook.Response{ActualStatusCode: &accepted, SuccessStatusCode: &ok, IncompleteStatusCode: &accepted} 3120 errorResponse := &webhook.Response{ActualStatusCode: ¬Found, SuccessStatusCode: &ok, IncompleteStatusCode: &accepted, Error: &errMsg} 3121 3122 successResponseWithStateInBody := &webhook.Response{ActualStatusCode: &ok, SuccessStatusCode: &ok, IncompleteStatusCode: &accepted, State: &readyState} 3123 deleteErrorResponseWithStateInBody := &webhook.Response{ActualStatusCode: &ok, SuccessStatusCode: &ok, IncompleteStatusCode: &accepted, State: &deleteErrorState} 3124 responseWithInvalidStateInBody := &webhook.Response{ActualStatusCode: &ok, SuccessStatusCode: &ok, IncompleteStatusCode: &accepted, State: &invalidState} 3125 3126 testCases := []struct { 3127 Name string 3128 Context context.Context 3129 FormationAssignmentRepo func() *automock.FormationAssignmentRepository 3130 NotificationService func() *automock.NotificationService 3131 LabelService func() *automock.LabelService 3132 FormationRepo func() *automock.FormationRepository 3133 RuntimeContextRepo func() *automock.RuntimeContextRepository 3134 FANotificationSvc func() *automock.FaNotificationService 3135 FAStatusService func() *automock.StatusService 3136 FormationAssignmentMappingPairWithOperation *formationassignment.AssignmentMappingPairWithOperation 3137 ExpectedErrorMsg string 3138 }{ 3139 { 3140 Name: "success delete assignment when there is no request", 3141 Context: ctxWithTenant, 3142 FormationAssignmentRepo: func() *automock.FormationAssignmentRepository { 3143 repo := &automock.FormationAssignmentRepository{} 3144 repo.On("Delete", ctxWithTenant, TestID, TestTenantID).Return(nil).Once() 3145 return repo 3146 }, 3147 FormationAssignmentMappingPairWithOperation: fixAssignmentMappingPairWithID(TestID), 3148 }, 3149 { 3150 Name: "success delete assignment when response code matches success status code", 3151 Context: ctxWithTenant, 3152 NotificationService: func() *automock.NotificationService { 3153 svc := &automock.NotificationService{} 3154 svc.On("SendNotification", ctxWithTenant, fixExtendedFormationAssignmentNotificationReq(req, configAssignmentWithTenantAndID)).Return(successResponse, nil).Once() 3155 return svc 3156 }, 3157 FANotificationSvc: func() *automock.FaNotificationService { 3158 faNotificationSvc := &automock.FaNotificationService{} 3159 assignmentMapping := fixAssignmentMappingPairWithAssignmentAndRequest(configAssignmentWithTenantAndID.Clone(), req) 3160 faNotificationSvc.On("GenerateFormationAssignmentNotificationExt", ctxWithTenant, assignmentMapping.Assignment, assignmentMapping.ReverseAssignment, model.AssignFormation).Return(fixExtendedFormationAssignmentNotificationReq(req, configAssignmentWithTenantAndID), nil).Once() 3161 return faNotificationSvc 3162 }, 3163 FAStatusService: func() *automock.StatusService { 3164 svc := &automock.StatusService{} 3165 svc.On("DeleteWithConstraints", ctxWithTenant, TestID).Return(nil).Once() 3166 return svc 3167 }, 3168 FormationAssignmentMappingPairWithOperation: fixAssignmentMappingPairWithAssignmentAndRequest(configAssignmentWithTenantAndID, req), 3169 }, 3170 { 3171 Name: "success delete assignment when there is a READY state in response", 3172 Context: ctxWithTenant, 3173 NotificationService: func() *automock.NotificationService { 3174 svc := &automock.NotificationService{} 3175 svc.On("SendNotification", ctxWithTenant, fixExtendedFormationAssignmentNotificationReq(req, configAssignmentWithTenantAndID)).Return(successResponseWithStateInBody, nil).Once() 3176 return svc 3177 }, 3178 FAStatusService: func() *automock.StatusService { 3179 svc := &automock.StatusService{} 3180 svc.On("DeleteWithConstraints", ctxWithTenant, TestID).Return(nil).Once() 3181 return svc 3182 }, 3183 FANotificationSvc: func() *automock.FaNotificationService { 3184 faNotificationSvc := &automock.FaNotificationService{} 3185 assignmentMapping := fixAssignmentMappingPairWithAssignmentAndRequest(configAssignmentWithTenantAndID.Clone(), req) 3186 faNotificationSvc.On("GenerateFormationAssignmentNotificationExt", ctxWithTenant, assignmentMapping.Assignment, assignmentMapping.ReverseAssignment, model.AssignFormation).Return(fixExtendedFormationAssignmentNotificationReq(req, configAssignmentWithTenantAndID), nil).Once() 3187 return faNotificationSvc 3188 }, 3189 FormationAssignmentMappingPairWithOperation: fixAssignmentMappingPairWithAssignmentAndRequest(configAssignmentWithTenantAndID.Clone(), req), 3190 }, 3191 { 3192 Name: "sets assignment in deleting state when webhook is async callback", 3193 Context: ctxWithTenant, 3194 FormationAssignmentRepo: func() *automock.FormationAssignmentRepository { 3195 repo := &automock.FormationAssignmentRepository{} 3196 repo.On("Exists", ctxWithTenant, TestID, TestTenantID).Return(true, nil).Once() 3197 repo.On("Update", ctxWithTenant, assignmentWithTenantAndIDInDeletingState).Return(nil).Once() 3198 return repo 3199 }, 3200 NotificationService: func() *automock.NotificationService { 3201 svc := &automock.NotificationService{} 3202 svc.On("SendNotification", ctxWithTenant, fixExtendedFormationAssignmentNotificationReq(callbackReq, configAssignmentWithTenantAndID)).Return(successResponse, nil).Once() 3203 return svc 3204 }, 3205 FANotificationSvc: func() *automock.FaNotificationService { 3206 faNotificationSvc := &automock.FaNotificationService{} 3207 assignmentMapping := fixAssignmentMappingPairWithAssignmentAndRequest(configAssignmentWithTenantAndID.Clone(), callbackReq) 3208 faNotificationSvc.On("GenerateFormationAssignmentNotificationExt", ctxWithTenant, assignmentMapping.Assignment, assignmentMapping.ReverseAssignment, model.AssignFormation).Return(fixExtendedFormationAssignmentNotificationReq(callbackReq, configAssignmentWithTenantAndID), nil).Once() 3209 return faNotificationSvc 3210 }, 3211 FormationAssignmentMappingPairWithOperation: fixAssignmentMappingPairWithAssignmentAndRequest(configAssignmentWithTenantAndID.Clone(), callbackReq), 3212 }, 3213 { 3214 Name: "incomplete response code matches actual response code", 3215 Context: ctxWithTenant, 3216 FormationAssignmentRepo: func() *automock.FormationAssignmentRepository { 3217 repo := &automock.FormationAssignmentRepository{} 3218 repo.On("Exists", ctxWithTenant, TestID, TestTenantID).Return(true, nil).Once() 3219 repo.On("Update", ctxWithTenant, assignmentWithTenantAndIDInDeleteError).Return(nil).Once() 3220 return repo 3221 }, 3222 NotificationService: func() *automock.NotificationService { 3223 svc := &automock.NotificationService{} 3224 svc.On("SendNotification", ctxWithTenant, fixExtendedFormationAssignmentNotificationReq(req, configAssignmentWithTenantAndID)).Return(incompleteResponse, nil).Once() 3225 return svc 3226 }, 3227 FANotificationSvc: func() *automock.FaNotificationService { 3228 faNotificationSvc := &automock.FaNotificationService{} 3229 assignmentMapping := fixAssignmentMappingPairWithAssignmentAndRequest(configAssignmentWithTenantAndID.Clone(), req) 3230 faNotificationSvc.On("GenerateFormationAssignmentNotificationExt", ctxWithTenant, assignmentMapping.Assignment, assignmentMapping.ReverseAssignment, model.AssignFormation).Return(fixExtendedFormationAssignmentNotificationReq(req, configAssignmentWithTenantAndID), nil).Once() 3231 return faNotificationSvc 3232 }, 3233 FormationAssignmentMappingPairWithOperation: fixAssignmentMappingPairWithAssignmentAndRequest(configAssignmentWithTenantAndID.Clone(), req), 3234 ExpectedErrorMsg: "Error while deleting assignment: config propagation is not supported on unassign notifications", 3235 }, 3236 { 3237 Name: "error when can't generate extended formation assignment notification", 3238 Context: ctxWithTenant, 3239 FANotificationSvc: func() *automock.FaNotificationService { 3240 faNotificationSvc := &automock.FaNotificationService{} 3241 assignmentMapping := fixAssignmentMappingPairWithAssignmentAndRequest(configAssignmentWithTenantAndID.Clone(), req) 3242 faNotificationSvc.On("GenerateFormationAssignmentNotificationExt", ctxWithTenant, assignmentMapping.Assignment, assignmentMapping.ReverseAssignment, model.AssignFormation).Return(nil, testErr).Once() 3243 return faNotificationSvc 3244 }, 3245 FormationAssignmentMappingPairWithOperation: fixAssignmentMappingPairWithAssignmentAndRequest(configAssignmentWithTenantAndID.Clone(), req), 3246 ExpectedErrorMsg: testErr.Error(), 3247 }, 3248 { 3249 Name: "error when update assignment to deleting state when webhook is async callback", 3250 Context: ctxWithTenant, 3251 FormationAssignmentRepo: func() *automock.FormationAssignmentRepository { 3252 repo := &automock.FormationAssignmentRepository{} 3253 repo.On("Exists", ctxWithTenant, TestID, TestTenantID).Return(true, nil).Once() 3254 repo.On("Update", ctxWithTenant, assignmentWithTenantAndIDInDeletingState).Return(testErr).Once() 3255 return repo 3256 }, 3257 NotificationService: func() *automock.NotificationService { 3258 svc := &automock.NotificationService{} 3259 svc.On("SendNotification", ctxWithTenant, fixExtendedFormationAssignmentNotificationReq(callbackReq, configAssignmentWithTenantAndID)).Return(successResponse, nil).Once() 3260 return svc 3261 }, 3262 FANotificationSvc: func() *automock.FaNotificationService { 3263 faNotificationSvc := &automock.FaNotificationService{} 3264 assignmentMapping := fixAssignmentMappingPairWithAssignmentAndRequest(configAssignmentWithTenantAndID.Clone(), callbackReq) 3265 faNotificationSvc.On("GenerateFormationAssignmentNotificationExt", ctxWithTenant, assignmentMapping.Assignment, assignmentMapping.ReverseAssignment, model.AssignFormation).Return(fixExtendedFormationAssignmentNotificationReq(callbackReq, configAssignmentWithTenantAndID), nil).Once() 3266 return faNotificationSvc 3267 }, 3268 FormationAssignmentMappingPairWithOperation: fixAssignmentMappingPairWithAssignmentAndRequest(configAssignmentWithTenantAndID.Clone(), callbackReq), 3269 ExpectedErrorMsg: testErr.Error(), 3270 }, 3271 { 3272 Name: "response contains error", 3273 Context: ctxWithTenant, 3274 NotificationService: func() *automock.NotificationService { 3275 svc := &automock.NotificationService{} 3276 svc.On("SendNotification", ctxWithTenant, fixExtendedFormationAssignmentNotificationReq(req, configAssignmentWithTenantAndID)).Return(errorResponse, nil).Once() 3277 return svc 3278 }, 3279 FANotificationSvc: func() *automock.FaNotificationService { 3280 faNotificationSvc := &automock.FaNotificationService{} 3281 assignmentMapping := fixAssignmentMappingPairWithAssignmentAndRequest(configAssignmentWithTenantAndID.Clone(), req) 3282 faNotificationSvc.On("GenerateFormationAssignmentNotificationExt", ctxWithTenant, assignmentMapping.Assignment, assignmentMapping.ReverseAssignment, model.AssignFormation).Return(fixExtendedFormationAssignmentNotificationReq(req, configAssignmentWithTenantAndID), nil).Once() 3283 return faNotificationSvc 3284 }, 3285 FAStatusService: func() *automock.StatusService { 3286 updater := &automock.StatusService{} 3287 updater.On("SetAssignmentToErrorStateWithConstraints", ctxWithTenant, configAssignmentWithTenantAndID.Clone(), testErr.Error(), formationassignment.AssignmentErrorCode(2), model.DeleteErrorAssignmentState, assignOperation).Return(nil).Once() 3288 return updater 3289 }, 3290 FormationAssignmentMappingPairWithOperation: fixAssignmentMappingPairWithAssignmentAndRequest(configAssignmentWithTenantAndID.Clone(), req), 3291 ExpectedErrorMsg: "Received error from response: Test Error", 3292 }, 3293 { 3294 Name: "error when update assignment to deleting state when webhook is async callback", 3295 Context: ctxWithTenant, 3296 FormationAssignmentRepo: func() *automock.FormationAssignmentRepository { 3297 repo := &automock.FormationAssignmentRepository{} 3298 repo.On("Exists", ctxWithTenant, TestID, TestTenantID).Return(true, nil).Once() 3299 repo.On("Update", ctxWithTenant, assignmentWithTenantAndIDInDeletingState).Return(notFoundError).Once() 3300 return repo 3301 }, 3302 NotificationService: func() *automock.NotificationService { 3303 svc := &automock.NotificationService{} 3304 svc.On("SendNotification", ctxWithTenant, fixExtendedFormationAssignmentNotificationReq(callbackReq, configAssignmentWithTenantAndID)).Return(successResponse, nil).Once() 3305 return svc 3306 }, 3307 FANotificationSvc: func() *automock.FaNotificationService { 3308 faNotificationSvc := &automock.FaNotificationService{} 3309 assignmentMapping := fixAssignmentMappingPairWithAssignmentAndRequest(configAssignmentWithTenantAndID.Clone(), callbackReq) 3310 faNotificationSvc.On("GenerateFormationAssignmentNotificationExt", ctxWithTenant, assignmentMapping.Assignment, assignmentMapping.ReverseAssignment, model.AssignFormation).Return(fixExtendedFormationAssignmentNotificationReq(callbackReq, configAssignmentWithTenantAndID), nil).Once() 3311 return faNotificationSvc 3312 }, 3313 FormationAssignmentMappingPairWithOperation: fixAssignmentMappingPairWithAssignmentAndRequest(configAssignmentWithTenantAndID.Clone(), callbackReq), 3314 }, 3315 { 3316 Name: "error while delete assignment when there is no request succeed in updating", 3317 Context: ctxWithTenant, 3318 FormationAssignmentRepo: func() *automock.FormationAssignmentRepository { 3319 repo := &automock.FormationAssignmentRepository{} 3320 repo.On("Exists", ctxWithTenant, TestID, TestTenantID).Return(true, nil).Once() 3321 repo.On("Update", ctxWithTenant, deleteErrorStateAssignmentDeleteErr).Return(nil).Once() 3322 repo.On("Delete", ctxWithTenant, TestID, TestTenantID).Return(testErr).Once() 3323 return repo 3324 }, 3325 FormationAssignmentMappingPairWithOperation: fixAssignmentMappingPairWithAssignmentAndRequest(configAssignmentWithTenantAndID.Clone(), nil), 3326 ExpectedErrorMsg: "while deleting formation assignment with id", 3327 }, 3328 { 3329 Name: "error while delete assignment when there is no request then error while updating", 3330 Context: ctxWithTenant, 3331 FormationAssignmentRepo: func() *automock.FormationAssignmentRepository { 3332 repo := &automock.FormationAssignmentRepository{} 3333 repo.On("Exists", ctxWithTenant, TestID, TestTenantID).Return(true, nil).Once() 3334 repo.On("Update", ctxWithTenant, deleteErrorStateAssignmentDeleteErr).Return(testErr).Once() 3335 repo.On("Delete", ctxWithTenant, TestID, TestTenantID).Return(testErr).Once() 3336 return repo 3337 }, 3338 FormationAssignmentMappingPairWithOperation: fixAssignmentMappingPairWithAssignmentAndRequest(configAssignmentWithTenantAndID.Clone(), nil), 3339 ExpectedErrorMsg: "while updating error state:", 3340 }, 3341 { 3342 Name: "error while delete assignment when there is no request then error while updating", 3343 Context: ctxWithTenant, 3344 FormationAssignmentRepo: func() *automock.FormationAssignmentRepository { 3345 repo := &automock.FormationAssignmentRepository{} 3346 repo.On("Delete", ctxWithTenant, TestID, TestTenantID).Return(notFoundError).Once() 3347 return repo 3348 }, 3349 FormationAssignmentMappingPairWithOperation: fixAssignmentMappingPairWithAssignmentAndRequest(configAssignmentWithTenantAndID.Clone(), nil), 3350 }, 3351 { 3352 Name: "error while delete assignment when there is no request succeed in updating", 3353 Context: ctxWithTenant, 3354 FormationAssignmentRepo: func() *automock.FormationAssignmentRepository { 3355 repo := &automock.FormationAssignmentRepository{} 3356 repo.On("Exists", ctxWithTenant, TestID, TestTenantID).Return(true, nil).Once() 3357 repo.On("Update", ctxWithTenant, deleteErrorStateAssignmentTechnicalErr).Return(nil).Once() 3358 return repo 3359 }, 3360 NotificationService: func() *automock.NotificationService { 3361 svc := &automock.NotificationService{} 3362 svc.On("SendNotification", ctxWithTenant, fixExtendedFormationAssignmentNotificationReq(req, configAssignmentWithTenantAndID)).Return(nil, testErr).Once() 3363 return svc 3364 }, 3365 FANotificationSvc: func() *automock.FaNotificationService { 3366 faNotificationSvc := &automock.FaNotificationService{} 3367 assignmentMapping := fixAssignmentMappingPairWithAssignmentAndRequest(configAssignmentWithTenantAndID.Clone(), req) 3368 faNotificationSvc.On("GenerateFormationAssignmentNotificationExt", ctxWithTenant, assignmentMapping.Assignment, assignmentMapping.ReverseAssignment, model.AssignFormation).Return(fixExtendedFormationAssignmentNotificationReq(req, configAssignmentWithTenantAndID), nil).Once() 3369 return faNotificationSvc 3370 }, 3371 FormationAssignmentMappingPairWithOperation: fixAssignmentMappingPairWithAssignmentAndRequest(configAssignmentWithTenantAndID.Clone(), req), 3372 ExpectedErrorMsg: "while sending notification for formation assignment with ID", 3373 }, 3374 { 3375 Name: "error while delete assignment when there is no request then error while updating", 3376 Context: ctxWithTenant, 3377 FormationAssignmentRepo: func() *automock.FormationAssignmentRepository { 3378 repo := &automock.FormationAssignmentRepository{} 3379 repo.On("Exists", ctxWithTenant, TestID, TestTenantID).Return(true, nil).Once() 3380 repo.On("Update", ctxWithTenant, deleteErrorStateAssignmentTechnicalErr).Return(testErr).Once() 3381 return repo 3382 }, 3383 NotificationService: func() *automock.NotificationService { 3384 svc := &automock.NotificationService{} 3385 svc.On("SendNotification", ctxWithTenant, fixExtendedFormationAssignmentNotificationReq(req, configAssignmentWithTenantAndID)).Return(nil, testErr).Once() 3386 return svc 3387 }, 3388 FANotificationSvc: func() *automock.FaNotificationService { 3389 faNotificationSvc := &automock.FaNotificationService{} 3390 assignmentMapping := fixAssignmentMappingPairWithAssignmentAndRequest(configAssignmentWithTenantAndID.Clone(), req) 3391 faNotificationSvc.On("GenerateFormationAssignmentNotificationExt", ctxWithTenant, assignmentMapping.Assignment, assignmentMapping.ReverseAssignment, model.AssignFormation).Return(fixExtendedFormationAssignmentNotificationReq(req, configAssignmentWithTenantAndID), nil).Once() 3392 return faNotificationSvc 3393 }, 3394 FormationAssignmentMappingPairWithOperation: fixAssignmentMappingPairWithAssignmentAndRequest(configAssignmentWithTenantAndID.Clone(), req), 3395 ExpectedErrorMsg: "while sending notification for formation assignment with ID", 3396 }, 3397 { 3398 Name: "error incomplete response code matches actual response code fails on update", 3399 Context: ctxWithTenant, 3400 FormationAssignmentRepo: func() *automock.FormationAssignmentRepository { 3401 repo := &automock.FormationAssignmentRepository{} 3402 repo.On("Exists", ctxWithTenant, TestID, TestTenantID).Return(true, nil).Once() 3403 repo.On("Update", ctxWithTenant, assignmentWithTenantAndIDInDeleteError).Return(testErr).Once() 3404 return repo 3405 }, 3406 NotificationService: func() *automock.NotificationService { 3407 svc := &automock.NotificationService{} 3408 svc.On("SendNotification", ctxWithTenant, fixExtendedFormationAssignmentNotificationReq(req, configAssignmentWithTenantAndID)).Return(incompleteResponse, nil).Once() 3409 return svc 3410 }, 3411 FANotificationSvc: func() *automock.FaNotificationService { 3412 faNotificationSvc := &automock.FaNotificationService{} 3413 assignmentMapping := fixAssignmentMappingPairWithAssignmentAndRequest(configAssignmentWithTenantAndID.Clone(), req) 3414 faNotificationSvc.On("GenerateFormationAssignmentNotificationExt", ctxWithTenant, assignmentMapping.Assignment, assignmentMapping.ReverseAssignment, model.AssignFormation).Return(fixExtendedFormationAssignmentNotificationReq(req, configAssignmentWithTenantAndID), nil).Once() 3415 return faNotificationSvc 3416 }, 3417 FormationAssignmentMappingPairWithOperation: fixAssignmentMappingPairWithAssignmentAndRequest(configAssignmentWithTenantAndID.Clone(), req), 3418 ExpectedErrorMsg: "while updating error state for formation with ID", 3419 }, 3420 { 3421 Name: "response contains error fails on update", 3422 Context: ctxWithTenant, 3423 NotificationService: func() *automock.NotificationService { 3424 svc := &automock.NotificationService{} 3425 svc.On("SendNotification", ctxWithTenant, fixExtendedFormationAssignmentNotificationReq(req, configAssignmentWithTenantAndID)).Return(errorResponse, nil).Once() 3426 return svc 3427 }, 3428 FANotificationSvc: func() *automock.FaNotificationService { 3429 faNotificationSvc := &automock.FaNotificationService{} 3430 assignmentMapping := fixAssignmentMappingPairWithAssignmentAndRequest(configAssignmentWithTenantAndID.Clone(), req) 3431 faNotificationSvc.On("GenerateFormationAssignmentNotificationExt", ctxWithTenant, assignmentMapping.Assignment, assignmentMapping.ReverseAssignment, model.AssignFormation).Return(fixExtendedFormationAssignmentNotificationReq(req, configAssignmentWithTenantAndID), nil).Once() 3432 return faNotificationSvc 3433 }, 3434 FAStatusService: func() *automock.StatusService { 3435 updater := &automock.StatusService{} 3436 updater.On("SetAssignmentToErrorStateWithConstraints", ctxWithTenant, configAssignmentWithTenantAndID.Clone(), testErr.Error(), formationassignment.AssignmentErrorCode(2), model.DeleteErrorAssignmentState, assignOperation).Return(testErr).Once() 3437 return updater 3438 }, 3439 FormationAssignmentMappingPairWithOperation: fixAssignmentMappingPairWithAssignmentAndRequest(configAssignmentWithTenantAndID.Clone(), req), 3440 ExpectedErrorMsg: testErr.Error(), 3441 }, 3442 { 3443 Name: "error when fails on delete when success response", 3444 Context: ctxWithTenant, 3445 FormationAssignmentRepo: func() *automock.FormationAssignmentRepository { 3446 repo := &automock.FormationAssignmentRepository{} 3447 repo.On("Exists", ctxWithTenant, TestID, TestTenantID).Return(true, nil).Once() 3448 repo.On("Update", ctxWithTenant, deleteErrorStateAssignmentWhileDeletingErr).Return(nil).Once() 3449 return repo 3450 }, 3451 NotificationService: func() *automock.NotificationService { 3452 svc := &automock.NotificationService{} 3453 svc.On("SendNotification", ctxWithTenant, fixExtendedFormationAssignmentNotificationReq(req, configAssignmentWithTenantAndID)).Return(successResponse, nil).Once() 3454 return svc 3455 }, 3456 FANotificationSvc: func() *automock.FaNotificationService { 3457 faNotificationSvc := &automock.FaNotificationService{} 3458 assignmentMapping := fixAssignmentMappingPairWithAssignmentAndRequest(configAssignmentWithTenantAndID.Clone(), req) 3459 faNotificationSvc.On("GenerateFormationAssignmentNotificationExt", ctxWithTenant, assignmentMapping.Assignment, assignmentMapping.ReverseAssignment, model.AssignFormation).Return(fixExtendedFormationAssignmentNotificationReq(req, configAssignmentWithTenantAndID), nil).Once() 3460 return faNotificationSvc 3461 }, 3462 FAStatusService: func() *automock.StatusService { 3463 updater := &automock.StatusService{} 3464 updater.On("DeleteWithConstraints", ctxWithTenant, TestID).Return(testErr).Once() 3465 return updater 3466 }, 3467 FormationAssignmentMappingPairWithOperation: fixAssignmentMappingPairWithAssignmentAndRequest(configAssignmentWithTenantAndID.Clone(), req), 3468 ExpectedErrorMsg: testErr.Error(), 3469 }, 3470 { 3471 Name: "error when fails on delete when success response", 3472 Context: ctxWithTenant, 3473 NotificationService: func() *automock.NotificationService { 3474 svc := &automock.NotificationService{} 3475 svc.On("SendNotification", ctxWithTenant, fixExtendedFormationAssignmentNotificationReq(req, configAssignmentWithTenantAndID)).Return(successResponse, nil).Once() 3476 return svc 3477 }, 3478 FANotificationSvc: func() *automock.FaNotificationService { 3479 faNotificationSvc := &automock.FaNotificationService{} 3480 assignmentMapping := fixAssignmentMappingPairWithAssignmentAndRequest(configAssignmentWithTenantAndID.Clone(), req) 3481 faNotificationSvc.On("GenerateFormationAssignmentNotificationExt", ctxWithTenant, assignmentMapping.Assignment, assignmentMapping.ReverseAssignment, model.AssignFormation).Return(fixExtendedFormationAssignmentNotificationReq(req, configAssignmentWithTenantAndID), nil).Once() 3482 return faNotificationSvc 3483 }, 3484 FAStatusService: func() *automock.StatusService { 3485 updater := &automock.StatusService{} 3486 updater.On("DeleteWithConstraints", ctxWithTenant, TestID).Return(notFoundError).Once() 3487 return updater 3488 }, 3489 FormationAssignmentMappingPairWithOperation: fixAssignmentMappingPairWithAssignmentAndRequest(configAssignmentWithTenantAndID.Clone(), req), 3490 }, 3491 { 3492 Name: "error when fails on delete when success response then fail on update", 3493 Context: ctxWithTenant, 3494 FormationAssignmentRepo: func() *automock.FormationAssignmentRepository { 3495 repo := &automock.FormationAssignmentRepository{} 3496 repo.On("Exists", ctxWithTenant, TestID, TestTenantID).Return(true, nil).Once() 3497 repo.On("Update", ctxWithTenant, deleteErrorStateAssignmentWhileDeletingErr).Return(testErr).Once() 3498 return repo 3499 }, 3500 NotificationService: func() *automock.NotificationService { 3501 svc := &automock.NotificationService{} 3502 svc.On("SendNotification", ctxWithTenant, fixExtendedFormationAssignmentNotificationReq(req, configAssignmentWithTenantAndID)).Return(successResponse, nil).Once() 3503 return svc 3504 }, 3505 FANotificationSvc: func() *automock.FaNotificationService { 3506 faNotificationSvc := &automock.FaNotificationService{} 3507 assignmentMapping := fixAssignmentMappingPairWithAssignmentAndRequest(configAssignmentWithTenantAndID.Clone(), req) 3508 faNotificationSvc.On("GenerateFormationAssignmentNotificationExt", ctxWithTenant, assignmentMapping.Assignment, assignmentMapping.ReverseAssignment, model.AssignFormation).Return(fixExtendedFormationAssignmentNotificationReq(req, configAssignmentWithTenantAndID), nil).Once() 3509 return faNotificationSvc 3510 }, 3511 FAStatusService: func() *automock.StatusService { 3512 updater := &automock.StatusService{} 3513 updater.On("DeleteWithConstraints", ctxWithTenant, TestID).Return(testErr).Once() 3514 return updater 3515 }, 3516 FormationAssignmentMappingPairWithOperation: fixAssignmentMappingPairWithAssignmentAndRequest(configAssignmentWithTenantAndID.Clone(), req), 3517 ExpectedErrorMsg: testErr.Error(), 3518 }, 3519 { 3520 Name: "error when fails on delete when success response then fail on update with not found", 3521 Context: ctxWithTenant, 3522 FormationAssignmentRepo: func() *automock.FormationAssignmentRepository { 3523 repo := &automock.FormationAssignmentRepository{} 3524 repo.On("Exists", ctxWithTenant, TestID, TestTenantID).Return(true, nil).Once() 3525 repo.On("Update", ctxWithTenant, deleteErrorStateAssignmentWhileDeletingErr).Return(notFoundError).Once() 3526 return repo 3527 }, 3528 NotificationService: func() *automock.NotificationService { 3529 svc := &automock.NotificationService{} 3530 svc.On("SendNotification", ctxWithTenant, fixExtendedFormationAssignmentNotificationReq(req, configAssignmentWithTenantAndID)).Return(successResponse, nil).Once() 3531 return svc 3532 }, 3533 FANotificationSvc: func() *automock.FaNotificationService { 3534 faNotificationSvc := &automock.FaNotificationService{} 3535 assignmentMapping := fixAssignmentMappingPairWithAssignmentAndRequest(configAssignmentWithTenantAndID.Clone(), req) 3536 faNotificationSvc.On("GenerateFormationAssignmentNotificationExt", ctxWithTenant, assignmentMapping.Assignment, assignmentMapping.ReverseAssignment, model.AssignFormation).Return(fixExtendedFormationAssignmentNotificationReq(req, configAssignmentWithTenantAndID), nil).Once() 3537 return faNotificationSvc 3538 }, 3539 FAStatusService: func() *automock.StatusService { 3540 updater := &automock.StatusService{} 3541 updater.On("DeleteWithConstraints", ctxWithTenant, TestID).Return(testErr).Once() 3542 return updater 3543 }, 3544 FormationAssignmentMappingPairWithOperation: fixAssignmentMappingPairWithAssignmentAndRequest(configAssignmentWithTenantAndID.Clone(), req), 3545 }, 3546 { 3547 Name: "error when state in body is invalid", 3548 Context: ctxWithTenant, 3549 NotificationService: func() *automock.NotificationService { 3550 svc := &automock.NotificationService{} 3551 svc.On("SendNotification", ctxWithTenant, fixExtendedFormationAssignmentNotificationReq(req, configAssignmentWithTenantAndID)).Return(responseWithInvalidStateInBody, nil).Once() 3552 return svc 3553 }, 3554 FANotificationSvc: func() *automock.FaNotificationService { 3555 faNotificationSvc := &automock.FaNotificationService{} 3556 assignmentMapping := fixAssignmentMappingPairWithAssignmentAndRequest(configAssignmentWithTenantAndID.Clone(), req) 3557 faNotificationSvc.On("GenerateFormationAssignmentNotificationExt", ctxWithTenant, assignmentMapping.Assignment, assignmentMapping.ReverseAssignment, model.AssignFormation).Return(fixExtendedFormationAssignmentNotificationReq(req, configAssignmentWithTenantAndID), nil).Once() 3558 return faNotificationSvc 3559 }, 3560 FormationAssignmentMappingPairWithOperation: fixAssignmentMappingPairWithAssignmentAndRequest(configAssignmentWithTenantAndID.Clone(), req), 3561 ExpectedErrorMsg: fmt.Sprintf("The provided state in the response %q is not valid.", invalidState), 3562 }, 3563 { 3564 Name: "error state in body is DELETE_ERROR and fails on update with not found", 3565 Context: ctxWithTenant, 3566 NotificationService: func() *automock.NotificationService { 3567 svc := &automock.NotificationService{} 3568 svc.On("SendNotification", ctxWithTenant, fixExtendedFormationAssignmentNotificationReq(req, configAssignmentWithTenantAndID)).Return(deleteErrorResponseWithStateInBody, nil).Once() 3569 return svc 3570 }, 3571 FANotificationSvc: func() *automock.FaNotificationService { 3572 faNotificationSvc := &automock.FaNotificationService{} 3573 assignmentMapping := fixAssignmentMappingPairWithAssignmentAndRequest(configAssignmentWithTenantAndID.Clone(), req) 3574 faNotificationSvc.On("GenerateFormationAssignmentNotificationExt", ctxWithTenant, assignmentMapping.Assignment, assignmentMapping.ReverseAssignment, model.AssignFormation).Return(fixExtendedFormationAssignmentNotificationReq(req, configAssignmentWithTenantAndID), nil).Once() 3575 return faNotificationSvc 3576 }, 3577 FAStatusService: func() *automock.StatusService { 3578 updater := &automock.StatusService{} 3579 updater.On("SetAssignmentToErrorStateWithConstraints", ctxWithTenant, configAssignmentWithTenantAndID.Clone(), "", formationassignment.AssignmentErrorCode(2), model.DeleteErrorAssignmentState, assignOperation).Return(notFoundError).Once() 3580 return updater 3581 }, 3582 FormationAssignmentMappingPairWithOperation: fixAssignmentMappingPairWithAssignmentAndRequest(configAssignmentWithTenantAndID.Clone(), req), 3583 }, 3584 { 3585 Name: "error state in body is DELETE_ERROR and fails on update", 3586 Context: ctxWithTenant, 3587 NotificationService: func() *automock.NotificationService { 3588 svc := &automock.NotificationService{} 3589 svc.On("SendNotification", ctxWithTenant, fixExtendedFormationAssignmentNotificationReq(req, configAssignmentWithTenantAndID)).Return(deleteErrorResponseWithStateInBody, nil).Once() 3590 return svc 3591 }, 3592 FANotificationSvc: func() *automock.FaNotificationService { 3593 faNotificationSvc := &automock.FaNotificationService{} 3594 assignmentMapping := fixAssignmentMappingPairWithAssignmentAndRequest(configAssignmentWithTenantAndID.Clone(), req) 3595 faNotificationSvc.On("GenerateFormationAssignmentNotificationExt", ctxWithTenant, assignmentMapping.Assignment, assignmentMapping.ReverseAssignment, model.AssignFormation).Return(fixExtendedFormationAssignmentNotificationReq(req, configAssignmentWithTenantAndID), nil).Once() 3596 return faNotificationSvc 3597 }, 3598 FAStatusService: func() *automock.StatusService { 3599 updater := &automock.StatusService{} 3600 updater.On("SetAssignmentToErrorStateWithConstraints", ctxWithTenant, configAssignmentWithTenantAndID.Clone(), "", formationassignment.AssignmentErrorCode(2), model.DeleteErrorAssignmentState, assignOperation).Return(testErr).Once() 3601 return updater 3602 }, 3603 FormationAssignmentMappingPairWithOperation: fixAssignmentMappingPairWithAssignmentAndRequest(configAssignmentWithTenantAndID.Clone(), req), 3604 ExpectedErrorMsg: "while updating error state for formation with ID", 3605 }, 3606 } 3607 3608 for _, testCase := range testCases { 3609 t.Run(testCase.Name, func(t *testing.T) { 3610 repo := &automock.FormationAssignmentRepository{} 3611 if testCase.FormationAssignmentRepo != nil { 3612 repo = testCase.FormationAssignmentRepo() 3613 } 3614 notificationSvc := &automock.NotificationService{} 3615 if testCase.NotificationService != nil { 3616 notificationSvc = testCase.NotificationService() 3617 } 3618 lblSvc := &automock.LabelService{} 3619 if testCase.LabelService != nil { 3620 lblSvc = testCase.LabelService() 3621 } 3622 formationRepo := &automock.FormationRepository{} 3623 if testCase.FormationRepo != nil { 3624 formationRepo = testCase.FormationRepo() 3625 } 3626 rtmCtxRepo := &automock.RuntimeContextRepository{} 3627 if testCase.RuntimeContextRepo != nil { 3628 rtmCtxRepo = testCase.RuntimeContextRepo() 3629 } 3630 updater := &automock.StatusService{} 3631 if testCase.FAStatusService != nil { 3632 updater = testCase.FAStatusService() 3633 } 3634 faNotificationSvc := &automock.FaNotificationService{} 3635 if testCase.FANotificationSvc != nil { 3636 faNotificationSvc = testCase.FANotificationSvc() 3637 } 3638 3639 svc := formationassignment.NewService(repo, nil, nil, nil, rtmCtxRepo, notificationSvc, faNotificationSvc, lblSvc, formationRepo, updater, rtmTypeLabelKey, appTypeLabelKey) 3640 3641 // WHEN 3642 isReverseProcessed, err := svc.CleanupFormationAssignment(testCase.Context, testCase.FormationAssignmentMappingPairWithOperation) 3643 3644 require.False(t, isReverseProcessed) 3645 if testCase.ExpectedErrorMsg != "" { 3646 require.Error(t, err) 3647 require.Contains(t, err.Error(), testCase.ExpectedErrorMsg) 3648 } else { 3649 require.NoError(t, err) 3650 } 3651 3652 // THEN 3653 mock.AssertExpectationsForObjects(t, repo, notificationSvc, lblSvc, formationRepo, rtmCtxRepo, updater, faNotificationSvc) 3654 }) 3655 } 3656 } 3657 3658 type operationContainer struct { 3659 content []*formationassignment.AssignmentMappingPairWithOperation 3660 err error 3661 } 3662 3663 func (o *operationContainer) appendThatProcessedReverse(_ context.Context, a *formationassignment.AssignmentMappingPairWithOperation) (bool, error) { 3664 o.content = append(o.content, a) 3665 return true, nil 3666 } 3667 3668 func (o *operationContainer) appendThatDoesNotProcessedReverse(_ context.Context, a *formationassignment.AssignmentMappingPairWithOperation) (bool, error) { 3669 o.content = append(o.content, a) 3670 return false, nil 3671 } 3672 3673 func (o *operationContainer) fail(context.Context, *formationassignment.AssignmentMappingPairWithOperation) (bool, error) { 3674 return false, o.err 3675 } 3676 3677 func (o *operationContainer) clear() { 3678 o.content = []*formationassignment.AssignmentMappingPairWithOperation{} 3679 }