github.com/kyma-incubator/compass/components/director@v0.0.0-20230623144113-d764f56ff805/internal/domain/formation/service_test.go (about) 1 package formation_test 2 3 import ( 4 "context" 5 6 "github.com/kyma-incubator/compass/components/director/internal/domain/formationassignment" 7 "github.com/kyma-incubator/compass/components/director/pkg/graphql" 8 webhookclient "github.com/kyma-incubator/compass/components/director/pkg/webhook_client" 9 10 "github.com/kyma-incubator/compass/components/director/internal/domain/labeldef" 11 "github.com/kyma-incubator/compass/components/director/pkg/apperrors" 12 "github.com/kyma-incubator/compass/components/director/pkg/pagination" 13 "github.com/kyma-incubator/compass/components/director/pkg/resource" 14 "github.com/pkg/errors" 15 16 "fmt" 17 "testing" 18 19 "github.com/kyma-incubator/compass/components/director/internal/domain/formation" 20 "github.com/kyma-incubator/compass/components/director/internal/domain/formation/automock" 21 "github.com/kyma-incubator/compass/components/director/internal/domain/tenant" 22 "github.com/kyma-incubator/compass/components/director/internal/model" 23 "github.com/stretchr/testify/assert" 24 "github.com/stretchr/testify/mock" 25 "github.com/stretchr/testify/require" 26 ) 27 28 func TestServiceList(t *testing.T) { 29 ctx := context.TODO() 30 ctx = tenant.SaveToContext(ctx, TntInternalID, TntExternalID) 31 32 pageSize := 100 33 cursor := "start" 34 35 expectedFormationPage := &model.FormationPage{ 36 Data: []*model.Formation{&modelFormation}, 37 PageInfo: &pagination.Page{ 38 StartCursor: cursor, 39 EndCursor: "", 40 HasNextPage: false, 41 }, 42 TotalCount: 1, 43 } 44 45 testCases := []struct { 46 Name string 47 FormationRepoFn func() *automock.FormationRepository 48 InputID string 49 InputPageSize int 50 ExpectedFormationPage *model.FormationPage 51 ExpectedErrMessage string 52 }{ 53 { 54 Name: "Success", 55 FormationRepoFn: func() *automock.FormationRepository { 56 repo := &automock.FormationRepository{} 57 repo.On("List", ctx, TntInternalID, pageSize, cursor).Return(expectedFormationPage, nil).Once() 58 return repo 59 }, 60 InputID: FormationID, 61 InputPageSize: pageSize, 62 ExpectedFormationPage: expectedFormationPage, 63 ExpectedErrMessage: "", 64 }, 65 { 66 Name: "Returns error when can't list formations", 67 FormationRepoFn: func() *automock.FormationRepository { 68 repo := &automock.FormationRepository{} 69 repo.On("List", ctx, TntInternalID, pageSize, cursor).Return(nil, testErr).Once() 70 return repo 71 }, 72 InputID: FormationID, 73 InputPageSize: pageSize, 74 ExpectedFormationPage: nil, 75 ExpectedErrMessage: testErr.Error(), 76 }, 77 { 78 Name: "Returns error when page size is not between 1 and 200", 79 InputID: FormationID, 80 InputPageSize: 300, 81 ExpectedFormationPage: nil, 82 ExpectedErrMessage: "page size must be between 1 and 200", 83 }, 84 } 85 86 for _, testCase := range testCases { 87 t.Run(testCase.Name, func(t *testing.T) { 88 // GIVEN 89 formationRepo := unusedFormationRepo() 90 if testCase.FormationRepoFn != nil { 91 formationRepo = testCase.FormationRepoFn() 92 } 93 94 svc := formation.NewService(nil, nil, nil, nil, formationRepo, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, runtimeType, applicationType) 95 96 // WHEN 97 actual, err := svc.List(ctx, testCase.InputPageSize, cursor) 98 99 // THEN 100 if testCase.ExpectedErrMessage == "" { 101 require.NoError(t, err) 102 assert.Equal(t, testCase.ExpectedFormationPage, actual) 103 } else { 104 require.Error(t, err) 105 require.Contains(t, err.Error(), testCase.ExpectedErrMessage) 106 require.Nil(t, actual) 107 } 108 109 formationRepo.AssertExpectations(t) 110 }) 111 } 112 } 113 114 func TestServiceGet(t *testing.T) { 115 ctx := context.TODO() 116 ctx = tenant.SaveToContext(ctx, TntInternalID, TntExternalID) 117 118 testCases := []struct { 119 Name string 120 FormationRepoFn func() *automock.FormationRepository 121 InputID string 122 ExpectedFormation *model.Formation 123 ExpectedErrMessage string 124 }{ 125 { 126 Name: "Success", 127 FormationRepoFn: func() *automock.FormationRepository { 128 repo := &automock.FormationRepository{} 129 repo.On("Get", ctx, FormationID, TntInternalID).Return(&modelFormation, nil).Once() 130 return repo 131 }, 132 InputID: FormationID, 133 ExpectedFormation: &modelFormation, 134 }, 135 { 136 Name: "Returns error when can't get the formation", 137 FormationRepoFn: func() *automock.FormationRepository { 138 repo := &automock.FormationRepository{} 139 repo.On("Get", ctx, FormationID, TntInternalID).Return(nil, testErr).Once() 140 return repo 141 }, 142 InputID: FormationID, 143 ExpectedFormation: nil, 144 ExpectedErrMessage: testErr.Error(), 145 }, 146 } 147 148 for _, testCase := range testCases { 149 t.Run(testCase.Name, func(t *testing.T) { 150 // GIVEN 151 formationRepo := testCase.FormationRepoFn() 152 153 svc := formation.NewService(nil, nil, nil, nil, formationRepo, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, runtimeType, applicationType) 154 155 // WHEN 156 actual, err := svc.Get(ctx, testCase.InputID) 157 158 // THEN 159 if testCase.ExpectedErrMessage == "" { 160 require.NoError(t, err) 161 assert.Equal(t, testCase.ExpectedFormation, actual) 162 } else { 163 require.Error(t, err) 164 require.Contains(t, err.Error(), testCase.ExpectedErrMessage) 165 require.Nil(t, actual) 166 } 167 168 formationRepo.AssertExpectations(t) 169 }) 170 } 171 } 172 173 func TestService_GetFormationByName(t *testing.T) { 174 ctx := context.TODO() 175 ctx = tenant.SaveToContext(ctx, TntInternalID, TntExternalID) 176 177 testCases := []struct { 178 Name string 179 FormationRepoFn func() *automock.FormationRepository 180 Input string 181 ExpectedFormation *model.Formation 182 ExpectedErrMessage string 183 }{ 184 { 185 Name: "Success", 186 FormationRepoFn: func() *automock.FormationRepository { 187 repo := &automock.FormationRepository{} 188 repo.On("GetByName", ctx, testFormationName, TntInternalID).Return(&modelFormation, nil).Once() 189 return repo 190 }, 191 Input: testFormationName, 192 ExpectedFormation: &modelFormation, 193 }, 194 { 195 Name: "Returns error when can't get the formation", 196 FormationRepoFn: func() *automock.FormationRepository { 197 repo := &automock.FormationRepository{} 198 repo.On("GetByName", ctx, testFormationName, TntInternalID).Return(nil, testErr).Once() 199 return repo 200 }, 201 Input: testFormationName, 202 ExpectedFormation: nil, 203 ExpectedErrMessage: "An error occurred while getting formation by name", 204 }, 205 } 206 207 for _, testCase := range testCases { 208 t.Run(testCase.Name, func(t *testing.T) { 209 // GIVEN 210 formationRepo := testCase.FormationRepoFn() 211 212 svc := formation.NewService(nil, nil, nil, nil, formationRepo, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, runtimeType, applicationType) 213 214 // WHEN 215 actual, err := svc.GetFormationByName(ctx, testCase.Input, TntInternalID) 216 217 // THEN 218 if testCase.ExpectedErrMessage == "" { 219 require.NoError(t, err) 220 assert.Equal(t, testCase.ExpectedFormation, actual) 221 } else { 222 require.Error(t, err) 223 require.Contains(t, err.Error(), testCase.ExpectedErrMessage) 224 require.Nil(t, actual) 225 } 226 227 formationRepo.AssertExpectations(t) 228 }) 229 } 230 } 231 232 func TestService_GetGlobalByID(t *testing.T) { 233 ctx := context.TODO() 234 ctxWithTenant := tenant.SaveToContext(ctx, TntInternalID, TntExternalID) 235 236 testCases := []struct { 237 Name string 238 FormationRepoFn func() *automock.FormationRepository 239 ExpectedFormation *model.Formation 240 ExpectedErrMessage string 241 }{ 242 { 243 Name: "Success", 244 FormationRepoFn: func() *automock.FormationRepository { 245 repo := &automock.FormationRepository{} 246 repo.On("GetGlobalByID", ctxWithTenant, FormationID).Return(&modelFormation, nil).Once() 247 return repo 248 }, 249 ExpectedFormation: &modelFormation, 250 }, 251 { 252 Name: "Error when getting formation globally fails", 253 FormationRepoFn: func() *automock.FormationRepository { 254 repo := &automock.FormationRepository{} 255 repo.On("GetGlobalByID", ctxWithTenant, FormationID).Return(nil, testErr).Once() 256 return repo 257 }, 258 ExpectedFormation: nil, 259 ExpectedErrMessage: fmt.Sprintf("An error occurred while getting formation by ID: %q globally", FormationID), 260 }, 261 } 262 263 for _, testCase := range testCases { 264 t.Run(testCase.Name, func(t *testing.T) { 265 // GIVEN 266 formationRepo := testCase.FormationRepoFn() 267 defer formationRepo.AssertExpectations(t) 268 269 svc := formation.NewService(nil, nil, nil, nil, formationRepo, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, runtimeType, applicationType) 270 271 // WHEN 272 actual, err := svc.GetGlobalByID(ctxWithTenant, FormationID) 273 274 // THEN 275 if testCase.ExpectedErrMessage == "" { 276 require.NoError(t, err) 277 assert.Equal(t, testCase.ExpectedFormation, actual) 278 } else { 279 require.Error(t, err) 280 require.Contains(t, err.Error(), testCase.ExpectedErrMessage) 281 require.Nil(t, actual) 282 } 283 }) 284 } 285 } 286 287 func TestService_Update(t *testing.T) { 288 ctx := context.TODO() 289 ctxWithTenant := tenant.SaveToContext(ctx, TntInternalID, TntExternalID) 290 291 testCases := []struct { 292 Name string 293 FormationRepoFn func() *automock.FormationRepository 294 ExpectedErrMessage string 295 }{ 296 { 297 Name: "Success", 298 FormationRepoFn: func() *automock.FormationRepository { 299 repo := &automock.FormationRepository{} 300 repo.On("Update", ctxWithTenant, &modelFormation).Return(nil).Once() 301 return repo 302 }, 303 }, 304 { 305 Name: "Error when updating formation fails", 306 FormationRepoFn: func() *automock.FormationRepository { 307 repo := &automock.FormationRepository{} 308 repo.On("Update", ctxWithTenant, &modelFormation).Return(testErr).Once() 309 return repo 310 }, 311 ExpectedErrMessage: fmt.Sprintf("An error occurred while updating formation with ID: %q", FormationID), 312 }, 313 } 314 315 for _, testCase := range testCases { 316 t.Run(testCase.Name, func(t *testing.T) { 317 // GIVEN 318 formationRepo := testCase.FormationRepoFn() 319 defer formationRepo.AssertExpectations(t) 320 321 svc := formation.NewService(nil, nil, nil, nil, formationRepo, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, runtimeType, applicationType) 322 323 // WHEN 324 err := svc.Update(ctxWithTenant, &modelFormation) 325 326 // THEN 327 if testCase.ExpectedErrMessage == "" { 328 require.NoError(t, err) 329 } else { 330 require.Error(t, err) 331 require.Contains(t, err.Error(), testCase.ExpectedErrMessage) 332 } 333 }) 334 } 335 } 336 337 func TestServiceCreateFormation(t *testing.T) { 338 ctx := context.TODO() 339 ctx = tenant.SaveToContext(ctx, TntInternalID, TntExternalID) 340 341 in := model.Formation{ 342 Name: testFormationName, 343 } 344 345 expectedFormation := fixFormationModelWithState(model.ReadyFormationState) 346 expectedFormationInInitialState := fixFormationModelWithState(model.InitialFormationState) 347 348 formationWithReadyState := fixFormationModelWithState(model.ReadyFormationState) 349 formationWithCreateErrorStateAndTechnicalAssignmentError := fixFormationModelWithStateAndAssignmentError(t, model.CreateErrorFormationState, testErr.Error(), formationassignment.TechnicalError) 350 351 testSchema, err := labeldef.NewSchemaForFormations([]string{testScenario}) 352 assert.NoError(t, err) 353 testSchemaLblDef := fixScenariosLabelDefinition(TntInternalID, testSchema) 354 355 newSchema, err := labeldef.NewSchemaForFormations([]string{testScenario, testFormationName}) 356 assert.NoError(t, err) 357 newSchemaLblDef := fixScenariosLabelDefinition(TntInternalID, newSchema) 358 359 emptySchemaLblDef := fixScenariosLabelDefinition(TntInternalID, testSchemaLblDef) 360 emptySchemaLblDef.Schema = nil 361 362 testCases := []struct { 363 Name string 364 FormationInput *model.Formation 365 UUIDServiceFn func() *automock.UuidService 366 LabelDefRepositoryFn func() *automock.LabelDefRepository 367 LabelDefServiceFn func() *automock.LabelDefService 368 NotificationsSvcFn func() *automock.NotificationsService 369 FormationTemplateRepoFn func() *automock.FormationTemplateRepository 370 FormationRepoFn func() *automock.FormationRepository 371 ConstraintEngineFn func() *automock.ConstraintEngine 372 webhookRepoFn func() *automock.WebhookRepository 373 StatusServiceFn func() *automock.StatusService 374 TemplateName string 375 ExpectedFormation *model.Formation 376 ExpectedErrMessage string 377 }{ 378 { 379 Name: "success when no labeldef exists", 380 UUIDServiceFn: func() *automock.UuidService { 381 uuidService := &automock.UuidService{} 382 uuidService.On("Generate").Return(fixUUID()) 383 return uuidService 384 }, 385 LabelDefRepositoryFn: func() *automock.LabelDefRepository { 386 labelDefRepo := &automock.LabelDefRepository{} 387 labelDefRepo.On("GetByKey", ctx, TntInternalID, model.ScenariosKey).Return(nil, apperrors.NewNotFoundError(resource.LabelDefinition, "")) 388 return labelDefRepo 389 }, 390 LabelDefServiceFn: func() *automock.LabelDefService { 391 labelDefService := &automock.LabelDefService{} 392 labelDefService.On("CreateWithFormations", ctx, TntInternalID, []string{testFormationName}).Return(nil) 393 return labelDefService 394 }, 395 NotificationsSvcFn: func() *automock.NotificationsService { 396 notificationSvc := &automock.NotificationsService{} 397 notificationSvc.On("GenerateFormationNotifications", ctx, emptyFormationLifecycleWebhooks, TntInternalID, formationWithReadyState, testFormationTemplateName, FormationTemplateID, model.CreateFormation).Return(emptyFormationNotificationRequests, nil).Once() 398 return notificationSvc 399 }, 400 FormationTemplateRepoFn: func() *automock.FormationTemplateRepository { 401 formationTemplateRepoMock := &automock.FormationTemplateRepository{} 402 formationTemplateRepoMock.On("GetByNameAndTenant", ctx, testFormationTemplateName, TntInternalID).Return(fixFormationTemplateModel(), nil).Once() 403 return formationTemplateRepoMock 404 }, 405 FormationRepoFn: func() *automock.FormationRepository { 406 formationRepoMock := &automock.FormationRepository{} 407 formationRepoMock.On("Create", ctx, formationWithReadyState).Return(nil).Once() 408 return formationRepoMock 409 }, 410 ConstraintEngineFn: func() *automock.ConstraintEngine { 411 engine := &automock.ConstraintEngine{} 412 engine.On("EnforceConstraints", ctx, preCreateLocation, createFormationDetails, FormationTemplateID).Return(nil).Once() 413 engine.On("EnforceConstraints", ctx, postCreateLocation, createFormationDetails, FormationTemplateID).Return(nil).Once() 414 return engine 415 }, 416 webhookRepoFn: func() *automock.WebhookRepository { 417 webhookRepo := &automock.WebhookRepository{} 418 webhookRepo.On("ListByReferenceObjectIDGlobal", ctx, FormationTemplateID, model.FormationTemplateWebhookReference).Return(emptyFormationLifecycleWebhooks, nil).Once() 419 return webhookRepo 420 }, 421 TemplateName: testFormationTemplateName, 422 ExpectedFormation: expectedFormation, 423 }, 424 { 425 Name: "success when labeldef exists", 426 UUIDServiceFn: func() *automock.UuidService { 427 uuidService := &automock.UuidService{} 428 uuidService.On("Generate").Return(fixUUID()) 429 return uuidService 430 }, 431 LabelDefRepositoryFn: func() *automock.LabelDefRepository { 432 labelDefRepo := &automock.LabelDefRepository{} 433 labelDefRepo.On("GetByKey", ctx, TntInternalID, model.ScenariosKey).Return(&testSchemaLblDef, nil) 434 labelDefRepo.On("UpdateWithVersion", ctx, newSchemaLblDef).Return(nil) 435 return labelDefRepo 436 }, 437 LabelDefServiceFn: func() *automock.LabelDefService { 438 labelDefService := &automock.LabelDefService{} 439 labelDefService.On("ValidateExistingLabelsAgainstSchema", ctx, newSchema, TntInternalID, model.ScenariosKey).Return(nil) 440 labelDefService.On("ValidateAutomaticScenarioAssignmentAgainstSchema", ctx, newSchema, TntInternalID, model.ScenariosKey).Return(nil) 441 return labelDefService 442 }, 443 NotificationsSvcFn: func() *automock.NotificationsService { 444 notificationSvc := &automock.NotificationsService{} 445 notificationSvc.On("GenerateFormationNotifications", ctx, emptyFormationLifecycleWebhooks, TntInternalID, formationWithReadyState, testFormationTemplateName, FormationTemplateID, model.CreateFormation).Return(emptyFormationNotificationRequests, nil).Once() 446 return notificationSvc 447 }, 448 FormationTemplateRepoFn: func() *automock.FormationTemplateRepository { 449 formationTemplateRepoMock := &automock.FormationTemplateRepository{} 450 formationTemplateRepoMock.On("GetByNameAndTenant", ctx, testFormationTemplateName, TntInternalID).Return(fixFormationTemplateModel(), nil).Once() 451 return formationTemplateRepoMock 452 }, 453 FormationRepoFn: func() *automock.FormationRepository { 454 formationRepoMock := &automock.FormationRepository{} 455 formationRepoMock.On("Create", ctx, formationWithReadyState).Return(nil).Once() 456 return formationRepoMock 457 }, 458 ConstraintEngineFn: func() *automock.ConstraintEngine { 459 engine := &automock.ConstraintEngine{} 460 engine.On("EnforceConstraints", ctx, preCreateLocation, createFormationDetails, FormationTemplateID).Return(nil).Once() 461 engine.On("EnforceConstraints", ctx, postCreateLocation, createFormationDetails, FormationTemplateID).Return(nil).Once() 462 return engine 463 }, 464 webhookRepoFn: func() *automock.WebhookRepository { 465 webhookRepo := &automock.WebhookRepository{} 466 webhookRepo.On("ListByReferenceObjectIDGlobal", ctx, FormationTemplateID, model.FormationTemplateWebhookReference).Return(emptyFormationLifecycleWebhooks, nil).Once() 467 return webhookRepo 468 }, 469 TemplateName: testFormationTemplateName, 470 ExpectedFormation: expectedFormation, 471 }, 472 { 473 Name: "success when state is provided externally", 474 UUIDServiceFn: func() *automock.UuidService { 475 uuidService := &automock.UuidService{} 476 uuidService.On("Generate").Return(fixUUID()) 477 return uuidService 478 }, 479 LabelDefRepositoryFn: func() *automock.LabelDefRepository { 480 labelDefRepo := &automock.LabelDefRepository{} 481 labelDefRepo.On("GetByKey", ctx, TntInternalID, model.ScenariosKey).Return(&testSchemaLblDef, nil) 482 labelDefRepo.On("UpdateWithVersion", ctx, newSchemaLblDef).Return(nil) 483 return labelDefRepo 484 }, 485 LabelDefServiceFn: func() *automock.LabelDefService { 486 labelDefService := &automock.LabelDefService{} 487 labelDefService.On("ValidateExistingLabelsAgainstSchema", ctx, newSchema, TntInternalID, model.ScenariosKey).Return(nil) 488 labelDefService.On("ValidateAutomaticScenarioAssignmentAgainstSchema", ctx, newSchema, TntInternalID, model.ScenariosKey).Return(nil) 489 return labelDefService 490 }, 491 NotificationsSvcFn: func() *automock.NotificationsService { 492 notificationSvc := &automock.NotificationsService{} 493 notificationSvc.On("GenerateFormationNotifications", ctx, emptyFormationLifecycleWebhooks, TntInternalID, formationWithInitialState, testFormationTemplateName, FormationTemplateID, model.CreateFormation).Return(emptyFormationNotificationRequests, nil).Once() 494 return notificationSvc 495 }, 496 FormationTemplateRepoFn: func() *automock.FormationTemplateRepository { 497 formationTemplateRepoMock := &automock.FormationTemplateRepository{} 498 formationTemplateRepoMock.On("GetByNameAndTenant", ctx, testFormationTemplateName, TntInternalID).Return(fixFormationTemplateModel(), nil).Once() 499 return formationTemplateRepoMock 500 }, 501 FormationRepoFn: func() *automock.FormationRepository { 502 formationRepoMock := &automock.FormationRepository{} 503 formationRepoMock.On("Create", ctx, formationWithInitialState).Return(nil).Once() 504 return formationRepoMock 505 }, 506 ConstraintEngineFn: func() *automock.ConstraintEngine { 507 engine := &automock.ConstraintEngine{} 508 engine.On("EnforceConstraints", ctx, preCreateLocation, createFormationDetails, FormationTemplateID).Return(nil).Once() 509 engine.On("EnforceConstraints", ctx, postCreateLocation, createFormationDetails, FormationTemplateID).Return(nil).Once() 510 return engine 511 }, 512 webhookRepoFn: func() *automock.WebhookRepository { 513 webhookRepo := &automock.WebhookRepository{} 514 webhookRepo.On("ListByReferenceObjectIDGlobal", ctx, FormationTemplateID, model.FormationTemplateWebhookReference).Return(emptyFormationLifecycleWebhooks, nil).Once() 515 return webhookRepo 516 }, 517 FormationInput: &model.Formation{ 518 Name: testFormationName, 519 State: model.InitialFormationState, 520 }, 521 TemplateName: testFormationTemplateName, 522 ExpectedFormation: expectedFormationInInitialState, 523 }, 524 { 525 Name: "error when labeldef is missing and can not create it", 526 LabelDefRepositoryFn: func() *automock.LabelDefRepository { 527 labelDefRepo := &automock.LabelDefRepository{} 528 labelDefRepo.On("GetByKey", ctx, TntInternalID, model.ScenariosKey).Return(nil, apperrors.NewNotFoundError(resource.LabelDefinition, "")) 529 return labelDefRepo 530 }, 531 LabelDefServiceFn: func() *automock.LabelDefService { 532 labelDefService := &automock.LabelDefService{} 533 labelDefService.On("CreateWithFormations", ctx, TntInternalID, []string{testFormationName}).Return(testErr) 534 return labelDefService 535 }, 536 FormationTemplateRepoFn: func() *automock.FormationTemplateRepository { 537 formationTemplateRepoMock := &automock.FormationTemplateRepository{} 538 formationTemplateRepoMock.On("GetByNameAndTenant", ctx, testFormationTemplateName, TntInternalID).Return(fixFormationTemplateModel(), nil).Once() 539 return formationTemplateRepoMock 540 }, 541 ConstraintEngineFn: func() *automock.ConstraintEngine { 542 engine := &automock.ConstraintEngine{} 543 engine.On("EnforceConstraints", ctx, preCreateLocation, createFormationDetails, FormationTemplateID).Return(nil).Once() 544 return engine 545 }, 546 TemplateName: testFormationTemplateName, 547 ExpectedErrMessage: testErr.Error(), 548 }, 549 { 550 Name: "error when labeldef is missing and create formation fails", 551 UUIDServiceFn: func() *automock.UuidService { 552 uuidService := &automock.UuidService{} 553 uuidService.On("Generate").Return(fixUUID()) 554 return uuidService 555 }, 556 LabelDefRepositoryFn: func() *automock.LabelDefRepository { 557 labelDefRepo := &automock.LabelDefRepository{} 558 labelDefRepo.On("GetByKey", ctx, TntInternalID, model.ScenariosKey).Return(nil, apperrors.NewNotFoundError(resource.LabelDefinition, "")) 559 return labelDefRepo 560 }, 561 LabelDefServiceFn: func() *automock.LabelDefService { 562 labelDefService := &automock.LabelDefService{} 563 labelDefService.On("CreateWithFormations", ctx, TntInternalID, []string{testFormationName}).Return(nil) 564 return labelDefService 565 }, 566 FormationRepoFn: func() *automock.FormationRepository { 567 formationRepoMock := &automock.FormationRepository{} 568 formationRepoMock.On("Create", ctx, formationWithReadyState).Return(testErr).Once() 569 return formationRepoMock 570 }, 571 FormationTemplateRepoFn: func() *automock.FormationTemplateRepository { 572 formationTemplateRepoMock := &automock.FormationTemplateRepository{} 573 formationTemplateRepoMock.On("GetByNameAndTenant", ctx, testFormationTemplateName, TntInternalID).Return(fixFormationTemplateModel(), nil).Once() 574 return formationTemplateRepoMock 575 }, 576 ConstraintEngineFn: func() *automock.ConstraintEngine { 577 engine := &automock.ConstraintEngine{} 578 engine.On("EnforceConstraints", ctx, preCreateLocation, createFormationDetails, FormationTemplateID).Return(nil).Once() 579 return engine 580 }, 581 webhookRepoFn: func() *automock.WebhookRepository { 582 webhookRepo := &automock.WebhookRepository{} 583 webhookRepo.On("ListByReferenceObjectIDGlobal", ctx, FormationTemplateID, model.FormationTemplateWebhookReference).Return(emptyFormationLifecycleWebhooks, nil).Once() 584 return webhookRepo 585 }, 586 TemplateName: testFormationTemplateName, 587 ExpectedErrMessage: "An error occurred while creating formation with name", 588 }, 589 { 590 Name: "error when can not get labeldef", 591 LabelDefRepositoryFn: func() *automock.LabelDefRepository { 592 labelDefRepo := &automock.LabelDefRepository{} 593 labelDefRepo.On("GetByKey", ctx, TntInternalID, model.ScenariosKey).Return(nil, testErr) 594 return labelDefRepo 595 }, 596 ConstraintEngineFn: func() *automock.ConstraintEngine { 597 engine := &automock.ConstraintEngine{} 598 engine.On("EnforceConstraints", ctx, preCreateLocation, createFormationDetails, FormationTemplateID).Return(nil).Once() 599 return engine 600 }, 601 FormationTemplateRepoFn: func() *automock.FormationTemplateRepository { 602 formationTemplateRepoMock := &automock.FormationTemplateRepository{} 603 formationTemplateRepoMock.On("GetByNameAndTenant", ctx, testFormationTemplateName, TntInternalID).Return(fixFormationTemplateModel(), nil).Once() 604 return formationTemplateRepoMock 605 }, 606 LabelDefServiceFn: unusedLabelDefService, 607 TemplateName: testFormationTemplateName, 608 ExpectedErrMessage: "while getting `scenarios` label definition: Test error", 609 }, 610 { 611 Name: "error when labeldef's schema is missing", 612 LabelDefRepositoryFn: func() *automock.LabelDefRepository { 613 labelDefRepo := &automock.LabelDefRepository{} 614 labelDefRepo.On("GetByKey", ctx, TntInternalID, model.ScenariosKey).Return(&emptySchemaLblDef, nil) 615 return labelDefRepo 616 }, 617 FormationTemplateRepoFn: func() *automock.FormationTemplateRepository { 618 formationTemplateRepoMock := &automock.FormationTemplateRepository{} 619 formationTemplateRepoMock.On("GetByNameAndTenant", ctx, testFormationTemplateName, TntInternalID).Return(fixFormationTemplateModel(), nil).Once() 620 return formationTemplateRepoMock 621 }, 622 ConstraintEngineFn: func() *automock.ConstraintEngine { 623 engine := &automock.ConstraintEngine{} 624 engine.On("EnforceConstraints", ctx, preCreateLocation, createFormationDetails, FormationTemplateID).Return(nil).Once() 625 return engine 626 }, 627 LabelDefServiceFn: unusedLabelDefService, 628 TemplateName: testFormationTemplateName, 629 ExpectedErrMessage: "missing schema", 630 }, 631 { 632 Name: "error when validating existing labels against the schema", 633 LabelDefRepositoryFn: func() *automock.LabelDefRepository { 634 labelDefRepo := &automock.LabelDefRepository{} 635 labelDefRepo.On("GetByKey", ctx, TntInternalID, model.ScenariosKey).Return(&testSchemaLblDef, nil) 636 return labelDefRepo 637 }, 638 LabelDefServiceFn: func() *automock.LabelDefService { 639 labelDefService := &automock.LabelDefService{} 640 labelDefService.On("ValidateExistingLabelsAgainstSchema", ctx, newSchema, TntInternalID, testSchemaLblDef.Key).Return(testErr) 641 return labelDefService 642 }, 643 FormationTemplateRepoFn: func() *automock.FormationTemplateRepository { 644 formationTemplateRepoMock := &automock.FormationTemplateRepository{} 645 formationTemplateRepoMock.On("GetByNameAndTenant", ctx, testFormationTemplateName, TntInternalID).Return(fixFormationTemplateModel(), nil).Once() 646 return formationTemplateRepoMock 647 }, 648 ConstraintEngineFn: func() *automock.ConstraintEngine { 649 engine := &automock.ConstraintEngine{} 650 engine.On("EnforceConstraints", ctx, preCreateLocation, createFormationDetails, FormationTemplateID).Return(nil).Once() 651 return engine 652 }, 653 TemplateName: testFormationTemplateName, 654 ExpectedErrMessage: testErr.Error(), 655 }, 656 { 657 Name: "error when validating automatic scenario assignment against the schema", 658 LabelDefRepositoryFn: func() *automock.LabelDefRepository { 659 labelDefRepo := &automock.LabelDefRepository{} 660 labelDefRepo.On("GetByKey", ctx, TntInternalID, model.ScenariosKey).Return(&testSchemaLblDef, nil) 661 return labelDefRepo 662 }, 663 LabelDefServiceFn: func() *automock.LabelDefService { 664 labelDefService := &automock.LabelDefService{} 665 labelDefService.On("ValidateExistingLabelsAgainstSchema", ctx, newSchema, TntInternalID, testSchemaLblDef.Key).Return(nil) 666 labelDefService.On("ValidateAutomaticScenarioAssignmentAgainstSchema", ctx, newSchema, TntInternalID, testSchemaLblDef.Key).Return(testErr) 667 return labelDefService 668 }, 669 FormationTemplateRepoFn: func() *automock.FormationTemplateRepository { 670 formationTemplateRepoMock := &automock.FormationTemplateRepository{} 671 formationTemplateRepoMock.On("GetByNameAndTenant", ctx, testFormationTemplateName, TntInternalID).Return(fixFormationTemplateModel(), nil).Once() 672 return formationTemplateRepoMock 673 }, 674 ConstraintEngineFn: func() *automock.ConstraintEngine { 675 engine := &automock.ConstraintEngine{} 676 engine.On("EnforceConstraints", ctx, preCreateLocation, createFormationDetails, FormationTemplateID).Return(nil).Once() 677 return engine 678 }, 679 TemplateName: testFormationTemplateName, 680 ExpectedErrMessage: "while validating Scenario Assignments against a new schema", 681 }, 682 { 683 Name: "error when update with version fails", 684 LabelDefRepositoryFn: func() *automock.LabelDefRepository { 685 labelDefRepo := &automock.LabelDefRepository{} 686 labelDefRepo.On("GetByKey", ctx, TntInternalID, model.ScenariosKey).Return(&testSchemaLblDef, nil) 687 labelDefRepo.On("UpdateWithVersion", ctx, newSchemaLblDef).Return(testErr) 688 return labelDefRepo 689 }, 690 LabelDefServiceFn: func() *automock.LabelDefService { 691 labelDefService := &automock.LabelDefService{} 692 labelDefService.On("ValidateExistingLabelsAgainstSchema", ctx, newSchema, TntInternalID, testSchemaLblDef.Key).Return(nil) 693 labelDefService.On("ValidateAutomaticScenarioAssignmentAgainstSchema", ctx, newSchema, TntInternalID, testSchemaLblDef.Key).Return(nil) 694 return labelDefService 695 }, 696 FormationTemplateRepoFn: func() *automock.FormationTemplateRepository { 697 formationTemplateRepoMock := &automock.FormationTemplateRepository{} 698 formationTemplateRepoMock.On("GetByNameAndTenant", ctx, testFormationTemplateName, TntInternalID).Return(fixFormationTemplateModel(), nil).Once() 699 return formationTemplateRepoMock 700 }, 701 ConstraintEngineFn: func() *automock.ConstraintEngine { 702 engine := &automock.ConstraintEngine{} 703 engine.On("EnforceConstraints", ctx, preCreateLocation, createFormationDetails, FormationTemplateID).Return(nil).Once() 704 return engine 705 }, 706 TemplateName: testFormationTemplateName, 707 ExpectedErrMessage: testErr.Error(), 708 }, 709 { 710 Name: "error when getting formation template by name fails", 711 FormationTemplateRepoFn: func() *automock.FormationTemplateRepository { 712 formationTemplateRepoMock := &automock.FormationTemplateRepository{} 713 formationTemplateRepoMock.On("GetByNameAndTenant", ctx, testFormationTemplateName, TntInternalID).Return(nil, testErr).Once() 714 return formationTemplateRepoMock 715 }, 716 TemplateName: testFormationTemplateName, 717 ExpectedErrMessage: "An error occurred while getting formation template by name", 718 }, 719 { 720 Name: "error when creating formation fails", 721 UUIDServiceFn: func() *automock.UuidService { 722 uuidService := &automock.UuidService{} 723 uuidService.On("Generate").Return(fixUUID()) 724 return uuidService 725 }, 726 LabelDefRepositoryFn: func() *automock.LabelDefRepository { 727 labelDefRepo := &automock.LabelDefRepository{} 728 labelDefRepo.On("GetByKey", ctx, TntInternalID, model.ScenariosKey).Return(&testSchemaLblDef, nil) 729 labelDefRepo.On("UpdateWithVersion", ctx, newSchemaLblDef).Return(nil) 730 return labelDefRepo 731 }, 732 LabelDefServiceFn: func() *automock.LabelDefService { 733 labelDefService := &automock.LabelDefService{} 734 labelDefService.On("ValidateExistingLabelsAgainstSchema", ctx, newSchema, TntInternalID, model.ScenariosKey).Return(nil) 735 labelDefService.On("ValidateAutomaticScenarioAssignmentAgainstSchema", ctx, newSchema, TntInternalID, model.ScenariosKey).Return(nil) 736 return labelDefService 737 }, 738 FormationTemplateRepoFn: func() *automock.FormationTemplateRepository { 739 formationTemplateRepoMock := &automock.FormationTemplateRepository{} 740 formationTemplateRepoMock.On("GetByNameAndTenant", ctx, testFormationTemplateName, TntInternalID).Return(fixFormationTemplateModel(), nil).Once() 741 return formationTemplateRepoMock 742 }, 743 FormationRepoFn: func() *automock.FormationRepository { 744 formationRepoMock := &automock.FormationRepository{} 745 formationRepoMock.On("Create", ctx, formationWithReadyState).Return(testErr).Once() 746 return formationRepoMock 747 }, 748 ConstraintEngineFn: func() *automock.ConstraintEngine { 749 engine := &automock.ConstraintEngine{} 750 engine.On("EnforceConstraints", ctx, preCreateLocation, createFormationDetails, FormationTemplateID).Return(nil).Once() 751 return engine 752 }, 753 webhookRepoFn: func() *automock.WebhookRepository { 754 webhookRepo := &automock.WebhookRepository{} 755 webhookRepo.On("ListByReferenceObjectIDGlobal", ctx, FormationTemplateID, model.FormationTemplateWebhookReference).Return(emptyFormationLifecycleWebhooks, nil).Once() 756 return webhookRepo 757 }, 758 TemplateName: testFormationTemplateName, 759 ExpectedErrMessage: "An error occurred while creating formation with name: \"test-formation\"", 760 }, 761 { 762 Name: "error while enforcing constraint pre operation", 763 FormationTemplateRepoFn: func() *automock.FormationTemplateRepository { 764 formationTemplateRepoMock := &automock.FormationTemplateRepository{} 765 formationTemplateRepoMock.On("GetByNameAndTenant", ctx, testFormationTemplateName, TntInternalID).Return(fixFormationTemplateModel(), nil).Once() 766 return formationTemplateRepoMock 767 }, 768 ConstraintEngineFn: func() *automock.ConstraintEngine { 769 engine := &automock.ConstraintEngine{} 770 engine.On("EnforceConstraints", ctx, preCreateLocation, createFormationDetails, FormationTemplateID).Return(testErr).Once() 771 return engine 772 }, 773 TemplateName: testFormationTemplateName, 774 ExpectedErrMessage: "while enforcing constraints for target operation \"CREATE_FORMATION\" and constraint type \"PRE\": Test error", 775 }, 776 { 777 Name: "error when listing formation template's webhooks fails", 778 LabelDefRepositoryFn: func() *automock.LabelDefRepository { 779 labelDefRepo := &automock.LabelDefRepository{} 780 labelDefRepo.On("GetByKey", ctx, TntInternalID, model.ScenariosKey).Return(&testSchemaLblDef, nil) 781 labelDefRepo.On("UpdateWithVersion", ctx, newSchemaLblDef).Return(nil) 782 return labelDefRepo 783 }, 784 LabelDefServiceFn: func() *automock.LabelDefService { 785 labelDefService := &automock.LabelDefService{} 786 labelDefService.On("ValidateExistingLabelsAgainstSchema", ctx, newSchema, TntInternalID, model.ScenariosKey).Return(nil) 787 labelDefService.On("ValidateAutomaticScenarioAssignmentAgainstSchema", ctx, newSchema, TntInternalID, model.ScenariosKey).Return(nil) 788 return labelDefService 789 }, 790 FormationTemplateRepoFn: func() *automock.FormationTemplateRepository { 791 formationTemplateRepoMock := &automock.FormationTemplateRepository{} 792 formationTemplateRepoMock.On("GetByNameAndTenant", ctx, testFormationTemplateName, TntInternalID).Return(fixFormationTemplateModel(), nil).Once() 793 return formationTemplateRepoMock 794 }, 795 ConstraintEngineFn: func() *automock.ConstraintEngine { 796 engine := &automock.ConstraintEngine{} 797 engine.On("EnforceConstraints", ctx, preCreateLocation, createFormationDetails, FormationTemplateID).Return(nil).Once() 798 return engine 799 }, 800 webhookRepoFn: func() *automock.WebhookRepository { 801 webhookRepo := &automock.WebhookRepository{} 802 webhookRepo.On("ListByReferenceObjectIDGlobal", ctx, FormationTemplateID, model.FormationTemplateWebhookReference).Return(nil, testErr).Once() 803 return webhookRepo 804 }, 805 TemplateName: testFormationTemplateName, 806 ExpectedErrMessage: "when listing formation lifecycle webhooks for formation template with ID", 807 }, 808 { 809 Name: "error while enforcing constraints post operation", 810 UUIDServiceFn: func() *automock.UuidService { 811 uuidService := &automock.UuidService{} 812 uuidService.On("Generate").Return(fixUUID()) 813 return uuidService 814 }, 815 LabelDefRepositoryFn: func() *automock.LabelDefRepository { 816 labelDefRepo := &automock.LabelDefRepository{} 817 labelDefRepo.On("GetByKey", ctx, TntInternalID, model.ScenariosKey).Return(nil, apperrors.NewNotFoundError(resource.LabelDefinition, "")) 818 return labelDefRepo 819 }, 820 LabelDefServiceFn: func() *automock.LabelDefService { 821 labelDefService := &automock.LabelDefService{} 822 labelDefService.On("CreateWithFormations", ctx, TntInternalID, []string{testFormationName}).Return(nil) 823 return labelDefService 824 }, 825 NotificationsSvcFn: func() *automock.NotificationsService { 826 notificationSvc := &automock.NotificationsService{} 827 notificationSvc.On("GenerateFormationNotifications", ctx, emptyFormationLifecycleWebhooks, TntInternalID, formationWithReadyState, testFormationTemplateName, FormationTemplateID, model.CreateFormation).Return(emptyFormationNotificationRequests, nil).Once() 828 return notificationSvc 829 }, 830 FormationTemplateRepoFn: func() *automock.FormationTemplateRepository { 831 formationTemplateRepoMock := &automock.FormationTemplateRepository{} 832 formationTemplateRepoMock.On("GetByNameAndTenant", ctx, testFormationTemplateName, TntInternalID).Return(fixFormationTemplateModel(), nil).Once() 833 return formationTemplateRepoMock 834 }, 835 FormationRepoFn: func() *automock.FormationRepository { 836 formationRepoMock := &automock.FormationRepository{} 837 formationRepoMock.On("Create", ctx, formationWithReadyState).Return(nil).Once() 838 return formationRepoMock 839 }, 840 ConstraintEngineFn: func() *automock.ConstraintEngine { 841 engine := &automock.ConstraintEngine{} 842 engine.On("EnforceConstraints", ctx, preCreateLocation, createFormationDetails, FormationTemplateID).Return(nil).Once() 843 engine.On("EnforceConstraints", ctx, postCreateLocation, createFormationDetails, FormationTemplateID).Return(testErr).Once() 844 return engine 845 }, 846 webhookRepoFn: func() *automock.WebhookRepository { 847 webhookRepo := &automock.WebhookRepository{} 848 webhookRepo.On("ListByReferenceObjectIDGlobal", ctx, FormationTemplateID, model.FormationTemplateWebhookReference).Return(emptyFormationLifecycleWebhooks, nil).Once() 849 return webhookRepo 850 }, 851 TemplateName: testFormationTemplateName, 852 ExpectedErrMessage: "while enforcing constraints for target operation \"CREATE_FORMATION\" and constraint type \"POST\": Test error", 853 }, 854 { 855 Name: "Success when there is formation notifications", 856 UUIDServiceFn: func() *automock.UuidService { 857 uuidService := &automock.UuidService{} 858 uuidService.On("Generate").Return(fixUUID()) 859 return uuidService 860 }, 861 LabelDefRepositoryFn: func() *automock.LabelDefRepository { 862 labelDefRepo := &automock.LabelDefRepository{} 863 labelDefRepo.On("GetByKey", ctx, TntInternalID, model.ScenariosKey).Return(&testSchemaLblDef, nil) 864 labelDefRepo.On("UpdateWithVersion", ctx, newSchemaLblDef).Return(nil) 865 return labelDefRepo 866 }, 867 LabelDefServiceFn: func() *automock.LabelDefService { 868 labelDefService := &automock.LabelDefService{} 869 labelDefService.On("ValidateExistingLabelsAgainstSchema", ctx, newSchema, TntInternalID, model.ScenariosKey).Return(nil) 870 labelDefService.On("ValidateAutomaticScenarioAssignmentAgainstSchema", ctx, newSchema, TntInternalID, model.ScenariosKey).Return(nil) 871 return labelDefService 872 }, 873 NotificationsSvcFn: func() *automock.NotificationsService { 874 notificationSvc := &automock.NotificationsService{} 875 notificationSvc.On("GenerateFormationNotifications", ctx, formationLifecycleSyncWebhooks, TntInternalID, formationWithInitialState, testFormationTemplateName, FormationTemplateID, model.CreateFormation).Return(formationNotificationSyncCreateRequests, nil).Once() 876 notificationSvc.On("SendNotification", ctx, formationNotificationSyncCreateRequest).Return(formationNotificationWebhookSuccessResponse, nil).Once() 877 return notificationSvc 878 }, 879 FormationTemplateRepoFn: func() *automock.FormationTemplateRepository { 880 formationTemplateRepoMock := &automock.FormationTemplateRepository{} 881 formationTemplateRepoMock.On("GetByNameAndTenant", ctx, testFormationTemplateName, TntInternalID).Return(fixFormationTemplateModel(), nil).Once() 882 return formationTemplateRepoMock 883 }, 884 FormationRepoFn: func() *automock.FormationRepository { 885 formationRepoMock := &automock.FormationRepository{} 886 formationRepoMock.On("Create", ctx, formationWithInitialState).Return(nil).Once() 887 return formationRepoMock 888 }, 889 ConstraintEngineFn: func() *automock.ConstraintEngine { 890 engine := &automock.ConstraintEngine{} 891 engine.On("EnforceConstraints", ctx, preCreateLocation, createFormationDetails, FormationTemplateID).Return(nil).Once() 892 engine.On("EnforceConstraints", ctx, postCreateLocation, createFormationDetails, FormationTemplateID).Return(nil).Once() 893 return engine 894 }, 895 webhookRepoFn: func() *automock.WebhookRepository { 896 webhookRepo := &automock.WebhookRepository{} 897 webhookRepo.On("ListByReferenceObjectIDGlobal", ctx, FormationTemplateID, model.FormationTemplateWebhookReference).Return(formationLifecycleSyncWebhooks, nil).Once() 898 return webhookRepo 899 }, 900 StatusServiceFn: func() *automock.StatusService { 901 svc := &automock.StatusService{} 902 svc.On("UpdateWithConstraints", ctx, formationWithReadyState, model.CreateFormation).Return(nil).Once() 903 return svc 904 }, 905 TemplateName: testFormationTemplateName, 906 ExpectedFormation: formationWithReadyState, 907 }, 908 { 909 Name: "Success when there is formation notifications with ASYNC_CALLBACK", 910 UUIDServiceFn: func() *automock.UuidService { 911 uuidService := &automock.UuidService{} 912 uuidService.On("Generate").Return(fixUUID()) 913 return uuidService 914 }, 915 LabelDefRepositoryFn: func() *automock.LabelDefRepository { 916 labelDefRepo := &automock.LabelDefRepository{} 917 labelDefRepo.On("GetByKey", ctx, TntInternalID, model.ScenariosKey).Return(&testSchemaLblDef, nil) 918 labelDefRepo.On("UpdateWithVersion", ctx, newSchemaLblDef).Return(nil) 919 return labelDefRepo 920 }, 921 LabelDefServiceFn: func() *automock.LabelDefService { 922 labelDefService := &automock.LabelDefService{} 923 labelDefService.On("ValidateExistingLabelsAgainstSchema", ctx, newSchema, TntInternalID, model.ScenariosKey).Return(nil) 924 labelDefService.On("ValidateAutomaticScenarioAssignmentAgainstSchema", ctx, newSchema, TntInternalID, model.ScenariosKey).Return(nil) 925 return labelDefService 926 }, 927 NotificationsSvcFn: func() *automock.NotificationsService { 928 notificationSvc := &automock.NotificationsService{} 929 notificationSvc.On("GenerateFormationNotifications", ctx, formationLifecycleAsyncWebhooks, TntInternalID, formationWithInitialState, testFormationTemplateName, FormationTemplateID, model.CreateFormation).Return(formationNotificationAsyncCreateRequests, nil).Once() 930 notificationSvc.On("SendNotification", ctx, formationNotificationAsyncCreateRequest).Return(formationNotificationWebhookSuccessResponse, nil).Once() 931 return notificationSvc 932 }, 933 FormationTemplateRepoFn: func() *automock.FormationTemplateRepository { 934 formationTemplateRepoMock := &automock.FormationTemplateRepository{} 935 formationTemplateRepoMock.On("GetByNameAndTenant", ctx, testFormationTemplateName, TntInternalID).Return(fixFormationTemplateModel(), nil).Once() 936 return formationTemplateRepoMock 937 }, 938 FormationRepoFn: func() *automock.FormationRepository { 939 formationRepoMock := &automock.FormationRepository{} 940 formationRepoMock.On("Create", ctx, formationWithInitialState).Return(nil).Once() 941 formationRepoMock.On("Update", ctx, formationWithInitialState).Return(nil).Once() 942 return formationRepoMock 943 }, 944 ConstraintEngineFn: func() *automock.ConstraintEngine { 945 engine := &automock.ConstraintEngine{} 946 engine.On("EnforceConstraints", ctx, preCreateLocation, createFormationDetails, FormationTemplateID).Return(nil).Once() 947 engine.On("EnforceConstraints", ctx, postCreateLocation, createFormationDetails, FormationTemplateID).Return(nil).Once() 948 return engine 949 }, 950 webhookRepoFn: func() *automock.WebhookRepository { 951 webhookRepo := &automock.WebhookRepository{} 952 webhookRepo.On("ListByReferenceObjectIDGlobal", ctx, FormationTemplateID, model.FormationTemplateWebhookReference).Return(formationLifecycleAsyncWebhooks, nil).Once() 953 return webhookRepo 954 }, 955 TemplateName: testFormationTemplateName, 956 ExpectedFormation: formationWithInitialState, 957 }, 958 { 959 Name: "Error when there is formation notifications but webhook response status is incorrect", 960 UUIDServiceFn: func() *automock.UuidService { 961 uuidService := &automock.UuidService{} 962 uuidService.On("Generate").Return(fixUUID()) 963 return uuidService 964 }, 965 LabelDefRepositoryFn: func() *automock.LabelDefRepository { 966 labelDefRepo := &automock.LabelDefRepository{} 967 labelDefRepo.On("GetByKey", ctx, TntInternalID, model.ScenariosKey).Return(&testSchemaLblDef, nil) 968 labelDefRepo.On("UpdateWithVersion", ctx, newSchemaLblDef).Return(nil) 969 return labelDefRepo 970 }, 971 LabelDefServiceFn: func() *automock.LabelDefService { 972 labelDefService := &automock.LabelDefService{} 973 labelDefService.On("ValidateExistingLabelsAgainstSchema", ctx, newSchema, TntInternalID, model.ScenariosKey).Return(nil) 974 labelDefService.On("ValidateAutomaticScenarioAssignmentAgainstSchema", ctx, newSchema, TntInternalID, model.ScenariosKey).Return(nil) 975 return labelDefService 976 }, 977 NotificationsSvcFn: func() *automock.NotificationsService { 978 notificationSvc := &automock.NotificationsService{} 979 notificationSvc.On("GenerateFormationNotifications", ctx, formationLifecycleSyncWebhooks, TntInternalID, formationWithInitialState, testFormationTemplateName, FormationTemplateID, model.CreateFormation).Return(formationNotificationSyncCreateRequests, nil).Once() 980 notificationSvc.On("SendNotification", ctx, formationNotificationSyncCreateRequest).Return(formationNotificationWebhookErrorResponse, nil).Once() 981 return notificationSvc 982 }, 983 FormationTemplateRepoFn: func() *automock.FormationTemplateRepository { 984 formationTemplateRepoMock := &automock.FormationTemplateRepository{} 985 formationTemplateRepoMock.On("GetByNameAndTenant", ctx, testFormationTemplateName, TntInternalID).Return(fixFormationTemplateModel(), nil).Once() 986 return formationTemplateRepoMock 987 }, 988 FormationRepoFn: func() *automock.FormationRepository { 989 formationRepoMock := &automock.FormationRepository{} 990 formationRepoMock.On("Create", ctx, formationWithInitialState).Return(nil).Once() 991 return formationRepoMock 992 }, 993 ConstraintEngineFn: func() *automock.ConstraintEngine { 994 engine := &automock.ConstraintEngine{} 995 engine.On("EnforceConstraints", ctx, preCreateLocation, createFormationDetails, FormationTemplateID).Return(nil).Once() 996 engine.On("EnforceConstraints", ctx, postCreateLocation, createFormationDetails, FormationTemplateID).Return(nil).Once() 997 return engine 998 }, 999 webhookRepoFn: func() *automock.WebhookRepository { 1000 webhookRepo := &automock.WebhookRepository{} 1001 webhookRepo.On("ListByReferenceObjectIDGlobal", ctx, FormationTemplateID, model.FormationTemplateWebhookReference).Return(formationLifecycleSyncWebhooks, nil).Once() 1002 return webhookRepo 1003 }, 1004 StatusServiceFn: func() *automock.StatusService { 1005 svc := &automock.StatusService{} 1006 svc.On("SetFormationToErrorStateWithConstraints", ctx, formationWithInitialState, testErr.Error(), formationassignment.AssignmentErrorCode(formationassignment.ClientError), model.CreateErrorFormationState, model.CreateFormation).Return(nil).Once() 1007 return svc 1008 }, 1009 TemplateName: testFormationTemplateName, 1010 ExpectedFormation: formationWithInitialState, 1011 }, 1012 { 1013 Name: "Error when generating formation notification fails", 1014 UUIDServiceFn: func() *automock.UuidService { 1015 uuidService := &automock.UuidService{} 1016 uuidService.On("Generate").Return(fixUUID()) 1017 return uuidService 1018 }, 1019 LabelDefRepositoryFn: func() *automock.LabelDefRepository { 1020 labelDefRepo := &automock.LabelDefRepository{} 1021 labelDefRepo.On("GetByKey", ctx, TntInternalID, model.ScenariosKey).Return(&testSchemaLblDef, nil) 1022 labelDefRepo.On("UpdateWithVersion", ctx, newSchemaLblDef).Return(nil) 1023 return labelDefRepo 1024 }, 1025 LabelDefServiceFn: func() *automock.LabelDefService { 1026 labelDefService := &automock.LabelDefService{} 1027 labelDefService.On("ValidateExistingLabelsAgainstSchema", ctx, newSchema, TntInternalID, model.ScenariosKey).Return(nil) 1028 labelDefService.On("ValidateAutomaticScenarioAssignmentAgainstSchema", ctx, newSchema, TntInternalID, model.ScenariosKey).Return(nil) 1029 return labelDefService 1030 }, 1031 NotificationsSvcFn: func() *automock.NotificationsService { 1032 notificationSvc := &automock.NotificationsService{} 1033 notificationSvc.On("GenerateFormationNotifications", ctx, formationLifecycleSyncWebhooks, TntInternalID, formationWithInitialState, testFormationTemplateName, FormationTemplateID, model.CreateFormation).Return(nil, testErr).Once() 1034 return notificationSvc 1035 }, 1036 FormationTemplateRepoFn: func() *automock.FormationTemplateRepository { 1037 formationTemplateRepoMock := &automock.FormationTemplateRepository{} 1038 formationTemplateRepoMock.On("GetByNameAndTenant", ctx, testFormationTemplateName, TntInternalID).Return(fixFormationTemplateModel(), nil).Once() 1039 return formationTemplateRepoMock 1040 }, 1041 FormationRepoFn: func() *automock.FormationRepository { 1042 formationRepoMock := &automock.FormationRepository{} 1043 formationRepoMock.On("Create", ctx, formationWithInitialState).Return(nil).Once() 1044 return formationRepoMock 1045 }, 1046 ConstraintEngineFn: func() *automock.ConstraintEngine { 1047 engine := &automock.ConstraintEngine{} 1048 engine.On("EnforceConstraints", ctx, preCreateLocation, createFormationDetails, FormationTemplateID).Return(nil).Once() 1049 return engine 1050 }, 1051 webhookRepoFn: func() *automock.WebhookRepository { 1052 webhookRepo := &automock.WebhookRepository{} 1053 webhookRepo.On("ListByReferenceObjectIDGlobal", ctx, FormationTemplateID, model.FormationTemplateWebhookReference).Return(formationLifecycleSyncWebhooks, nil).Once() 1054 return webhookRepo 1055 }, 1056 TemplateName: testFormationTemplateName, 1057 ExpectedFormation: nil, 1058 ExpectedErrMessage: "while generating notifications for formation with ID", 1059 }, 1060 { 1061 Name: "Error when sending formation notifications fails", 1062 UUIDServiceFn: func() *automock.UuidService { 1063 uuidService := &automock.UuidService{} 1064 uuidService.On("Generate").Return(fixUUID()) 1065 return uuidService 1066 }, 1067 LabelDefRepositoryFn: func() *automock.LabelDefRepository { 1068 labelDefRepo := &automock.LabelDefRepository{} 1069 labelDefRepo.On("GetByKey", ctx, TntInternalID, model.ScenariosKey).Return(&testSchemaLblDef, nil) 1070 labelDefRepo.On("UpdateWithVersion", ctx, newSchemaLblDef).Return(nil) 1071 return labelDefRepo 1072 }, 1073 LabelDefServiceFn: func() *automock.LabelDefService { 1074 labelDefService := &automock.LabelDefService{} 1075 labelDefService.On("ValidateExistingLabelsAgainstSchema", ctx, newSchema, TntInternalID, model.ScenariosKey).Return(nil) 1076 labelDefService.On("ValidateAutomaticScenarioAssignmentAgainstSchema", ctx, newSchema, TntInternalID, model.ScenariosKey).Return(nil) 1077 return labelDefService 1078 }, 1079 NotificationsSvcFn: func() *automock.NotificationsService { 1080 notificationSvc := &automock.NotificationsService{} 1081 notificationSvc.On("GenerateFormationNotifications", ctx, formationLifecycleSyncWebhooks, TntInternalID, formationWithInitialState, testFormationTemplateName, FormationTemplateID, model.CreateFormation).Return(formationNotificationSyncCreateRequests, nil).Once() 1082 notificationSvc.On("SendNotification", ctx, formationNotificationSyncCreateRequest).Return(nil, testErr).Once() 1083 return notificationSvc 1084 }, 1085 FormationTemplateRepoFn: func() *automock.FormationTemplateRepository { 1086 formationTemplateRepoMock := &automock.FormationTemplateRepository{} 1087 formationTemplateRepoMock.On("GetByNameAndTenant", ctx, testFormationTemplateName, TntInternalID).Return(fixFormationTemplateModel(), nil).Once() 1088 return formationTemplateRepoMock 1089 }, 1090 FormationRepoFn: func() *automock.FormationRepository { 1091 formationRepoMock := &automock.FormationRepository{} 1092 formationRepoMock.On("Create", ctx, formationWithInitialState).Return(nil).Once() 1093 formationRepoMock.On("Update", ctx, formationWithCreateErrorStateAndTechnicalAssignmentError).Return(nil).Once() 1094 return formationRepoMock 1095 }, 1096 ConstraintEngineFn: func() *automock.ConstraintEngine { 1097 engine := &automock.ConstraintEngine{} 1098 engine.On("EnforceConstraints", ctx, preCreateLocation, createFormationDetails, FormationTemplateID).Return(nil).Once() 1099 return engine 1100 }, 1101 webhookRepoFn: func() *automock.WebhookRepository { 1102 webhookRepo := &automock.WebhookRepository{} 1103 webhookRepo.On("ListByReferenceObjectIDGlobal", ctx, FormationTemplateID, model.FormationTemplateWebhookReference).Return(formationLifecycleSyncWebhooks, nil).Once() 1104 return webhookRepo 1105 }, 1106 TemplateName: testFormationTemplateName, 1107 ExpectedFormation: nil, 1108 ExpectedErrMessage: "while sending notification for formation with ID", 1109 }, 1110 { 1111 Name: "Error when sending formation notifications fails and subsequently formation update fails", 1112 UUIDServiceFn: func() *automock.UuidService { 1113 uuidService := &automock.UuidService{} 1114 uuidService.On("Generate").Return(fixUUID()) 1115 return uuidService 1116 }, 1117 LabelDefRepositoryFn: func() *automock.LabelDefRepository { 1118 labelDefRepo := &automock.LabelDefRepository{} 1119 labelDefRepo.On("GetByKey", ctx, TntInternalID, model.ScenariosKey).Return(&testSchemaLblDef, nil) 1120 labelDefRepo.On("UpdateWithVersion", ctx, newSchemaLblDef).Return(nil) 1121 return labelDefRepo 1122 }, 1123 LabelDefServiceFn: func() *automock.LabelDefService { 1124 labelDefService := &automock.LabelDefService{} 1125 labelDefService.On("ValidateExistingLabelsAgainstSchema", ctx, newSchema, TntInternalID, model.ScenariosKey).Return(nil) 1126 labelDefService.On("ValidateAutomaticScenarioAssignmentAgainstSchema", ctx, newSchema, TntInternalID, model.ScenariosKey).Return(nil) 1127 return labelDefService 1128 }, 1129 NotificationsSvcFn: func() *automock.NotificationsService { 1130 notificationSvc := &automock.NotificationsService{} 1131 notificationSvc.On("GenerateFormationNotifications", ctx, formationLifecycleSyncWebhooks, TntInternalID, formationWithInitialState, testFormationTemplateName, FormationTemplateID, model.CreateFormation).Return(formationNotificationSyncCreateRequests, nil).Once() 1132 notificationSvc.On("SendNotification", ctx, formationNotificationSyncCreateRequest).Return(nil, testErr).Once() 1133 return notificationSvc 1134 }, 1135 FormationTemplateRepoFn: func() *automock.FormationTemplateRepository { 1136 formationTemplateRepoMock := &automock.FormationTemplateRepository{} 1137 formationTemplateRepoMock.On("GetByNameAndTenant", ctx, testFormationTemplateName, TntInternalID).Return(fixFormationTemplateModel(), nil).Once() 1138 return formationTemplateRepoMock 1139 }, 1140 FormationRepoFn: func() *automock.FormationRepository { 1141 formationRepoMock := &automock.FormationRepository{} 1142 formationRepoMock.On("Create", ctx, formationWithInitialState).Return(nil).Once() 1143 formationRepoMock.On("Update", ctx, formationWithCreateErrorStateAndTechnicalAssignmentError).Return(testErr).Once() 1144 return formationRepoMock 1145 }, 1146 ConstraintEngineFn: func() *automock.ConstraintEngine { 1147 engine := &automock.ConstraintEngine{} 1148 engine.On("EnforceConstraints", ctx, preCreateLocation, createFormationDetails, FormationTemplateID).Return(nil).Once() 1149 return engine 1150 }, 1151 webhookRepoFn: func() *automock.WebhookRepository { 1152 webhookRepo := &automock.WebhookRepository{} 1153 webhookRepo.On("ListByReferenceObjectIDGlobal", ctx, FormationTemplateID, model.FormationTemplateWebhookReference).Return(formationLifecycleSyncWebhooks, nil).Once() 1154 return webhookRepo 1155 }, 1156 TemplateName: testFormationTemplateName, 1157 ExpectedFormation: nil, 1158 ExpectedErrMessage: "while updating error state", 1159 }, 1160 { 1161 Name: "Error when there is formation notifications, webhook response status is incorrect and setting formation to error state with constraints fails", 1162 UUIDServiceFn: func() *automock.UuidService { 1163 uuidService := &automock.UuidService{} 1164 uuidService.On("Generate").Return(fixUUID()) 1165 return uuidService 1166 }, 1167 LabelDefRepositoryFn: func() *automock.LabelDefRepository { 1168 labelDefRepo := &automock.LabelDefRepository{} 1169 labelDefRepo.On("GetByKey", ctx, TntInternalID, model.ScenariosKey).Return(&testSchemaLblDef, nil) 1170 labelDefRepo.On("UpdateWithVersion", ctx, newSchemaLblDef).Return(nil) 1171 return labelDefRepo 1172 }, 1173 LabelDefServiceFn: func() *automock.LabelDefService { 1174 labelDefService := &automock.LabelDefService{} 1175 labelDefService.On("ValidateExistingLabelsAgainstSchema", ctx, newSchema, TntInternalID, model.ScenariosKey).Return(nil) 1176 labelDefService.On("ValidateAutomaticScenarioAssignmentAgainstSchema", ctx, newSchema, TntInternalID, model.ScenariosKey).Return(nil) 1177 return labelDefService 1178 }, 1179 NotificationsSvcFn: func() *automock.NotificationsService { 1180 notificationSvc := &automock.NotificationsService{} 1181 notificationSvc.On("GenerateFormationNotifications", ctx, formationLifecycleSyncWebhooks, TntInternalID, formationWithInitialState, testFormationTemplateName, FormationTemplateID, model.CreateFormation).Return(formationNotificationSyncCreateRequests, nil).Once() 1182 notificationSvc.On("SendNotification", ctx, formationNotificationSyncCreateRequest).Return(formationNotificationWebhookErrorResponse, nil).Once() 1183 return notificationSvc 1184 }, 1185 FormationTemplateRepoFn: func() *automock.FormationTemplateRepository { 1186 formationTemplateRepoMock := &automock.FormationTemplateRepository{} 1187 formationTemplateRepoMock.On("GetByNameAndTenant", ctx, testFormationTemplateName, TntInternalID).Return(fixFormationTemplateModel(), nil).Once() 1188 return formationTemplateRepoMock 1189 }, 1190 FormationRepoFn: func() *automock.FormationRepository { 1191 formationRepoMock := &automock.FormationRepository{} 1192 formationRepoMock.On("Create", ctx, formationWithInitialState).Return(nil).Once() 1193 return formationRepoMock 1194 }, 1195 ConstraintEngineFn: func() *automock.ConstraintEngine { 1196 engine := &automock.ConstraintEngine{} 1197 engine.On("EnforceConstraints", ctx, preCreateLocation, createFormationDetails, FormationTemplateID).Return(nil).Once() 1198 return engine 1199 }, 1200 webhookRepoFn: func() *automock.WebhookRepository { 1201 webhookRepo := &automock.WebhookRepository{} 1202 webhookRepo.On("ListByReferenceObjectIDGlobal", ctx, FormationTemplateID, model.FormationTemplateWebhookReference).Return(formationLifecycleSyncWebhooks, nil).Once() 1203 return webhookRepo 1204 }, 1205 StatusServiceFn: func() *automock.StatusService { 1206 svc := &automock.StatusService{} 1207 svc.On("SetFormationToErrorStateWithConstraints", ctx, formationWithInitialState, testErr.Error(), formationassignment.AssignmentErrorCode(formationassignment.ClientError), model.CreateErrorFormationState, model.CreateFormation).Return(testErr).Once() 1208 return svc 1209 }, 1210 TemplateName: testFormationTemplateName, 1211 ExpectedFormation: nil, 1212 ExpectedErrMessage: "while updating error state for formation with ID", 1213 }, 1214 { 1215 Name: "Error when there is formation notifications, webhook response status is correct but the formation update with constraints fails", 1216 UUIDServiceFn: func() *automock.UuidService { 1217 uuidService := &automock.UuidService{} 1218 uuidService.On("Generate").Return(fixUUID()) 1219 return uuidService 1220 }, 1221 LabelDefRepositoryFn: func() *automock.LabelDefRepository { 1222 labelDefRepo := &automock.LabelDefRepository{} 1223 labelDefRepo.On("GetByKey", ctx, TntInternalID, model.ScenariosKey).Return(&testSchemaLblDef, nil) 1224 labelDefRepo.On("UpdateWithVersion", ctx, newSchemaLblDef).Return(nil) 1225 return labelDefRepo 1226 }, 1227 LabelDefServiceFn: func() *automock.LabelDefService { 1228 labelDefService := &automock.LabelDefService{} 1229 labelDefService.On("ValidateExistingLabelsAgainstSchema", ctx, newSchema, TntInternalID, model.ScenariosKey).Return(nil) 1230 labelDefService.On("ValidateAutomaticScenarioAssignmentAgainstSchema", ctx, newSchema, TntInternalID, model.ScenariosKey).Return(nil) 1231 return labelDefService 1232 }, 1233 NotificationsSvcFn: func() *automock.NotificationsService { 1234 notificationSvc := &automock.NotificationsService{} 1235 notificationSvc.On("GenerateFormationNotifications", ctx, formationLifecycleSyncWebhooks, TntInternalID, formationWithInitialState, testFormationTemplateName, FormationTemplateID, model.CreateFormation).Return(formationNotificationSyncCreateRequests, nil).Once() 1236 notificationSvc.On("SendNotification", ctx, formationNotificationSyncCreateRequest).Return(formationNotificationWebhookSuccessResponse, nil).Once() 1237 return notificationSvc 1238 }, 1239 FormationTemplateRepoFn: func() *automock.FormationTemplateRepository { 1240 formationTemplateRepoMock := &automock.FormationTemplateRepository{} 1241 formationTemplateRepoMock.On("GetByNameAndTenant", ctx, testFormationTemplateName, TntInternalID).Return(fixFormationTemplateModel(), nil).Once() 1242 return formationTemplateRepoMock 1243 }, 1244 FormationRepoFn: func() *automock.FormationRepository { 1245 formationRepoMock := &automock.FormationRepository{} 1246 formationRepoMock.On("Create", ctx, formationWithInitialState).Return(nil).Once() 1247 return formationRepoMock 1248 }, 1249 ConstraintEngineFn: func() *automock.ConstraintEngine { 1250 engine := &automock.ConstraintEngine{} 1251 engine.On("EnforceConstraints", ctx, preCreateLocation, createFormationDetails, FormationTemplateID).Return(nil).Once() 1252 return engine 1253 }, 1254 webhookRepoFn: func() *automock.WebhookRepository { 1255 webhookRepo := &automock.WebhookRepository{} 1256 webhookRepo.On("ListByReferenceObjectIDGlobal", ctx, FormationTemplateID, model.FormationTemplateWebhookReference).Return(formationLifecycleSyncWebhooks, nil).Once() 1257 return webhookRepo 1258 }, 1259 StatusServiceFn: func() *automock.StatusService { 1260 svc := &automock.StatusService{} 1261 svc.On("UpdateWithConstraints", ctx, formationWithReadyState, model.CreateFormation).Return(testErr).Once() 1262 return svc 1263 }, 1264 TemplateName: testFormationTemplateName, 1265 ExpectedErrMessage: testErr.Error(), 1266 }, 1267 } 1268 1269 for _, testCase := range testCases { 1270 t.Run(testCase.Name, func(t *testing.T) { 1271 // GIVEN 1272 input := testCase.FormationInput 1273 if input == nil { 1274 input = &in 1275 } 1276 1277 uidService := unusedUUIDService() 1278 if testCase.UUIDServiceFn != nil { 1279 uidService = testCase.UUIDServiceFn() 1280 } 1281 labelDefRepo := unusedLabelDefRepository() 1282 if testCase.LabelDefServiceFn != nil { 1283 labelDefRepo = testCase.LabelDefRepositoryFn() 1284 } 1285 labelDefService := unusedLabelDefService() 1286 if testCase.LabelDefServiceFn != nil { 1287 labelDefService = testCase.LabelDefServiceFn() 1288 } 1289 1290 notificationsService := unusedNotificationsService() 1291 if testCase.NotificationsSvcFn != nil { 1292 notificationsService = testCase.NotificationsSvcFn() 1293 } 1294 1295 formationRepo := unusedFormationRepo() 1296 if testCase.FormationRepoFn != nil { 1297 formationRepo = testCase.FormationRepoFn() 1298 } 1299 formationTemplateRepo := unusedFormationTemplateRepo() 1300 if testCase.FormationTemplateRepoFn != nil { 1301 formationTemplateRepo = testCase.FormationTemplateRepoFn() 1302 } 1303 constraintEngine := unusedConstraintEngine() 1304 if testCase.ConstraintEngineFn != nil { 1305 constraintEngine = testCase.ConstraintEngineFn() 1306 } 1307 webhookRepo := unusedWebhookRepository() 1308 if testCase.webhookRepoFn != nil { 1309 webhookRepo = testCase.webhookRepoFn() 1310 } 1311 1312 statusSvc := &automock.StatusService{} 1313 if testCase.StatusServiceFn != nil { 1314 statusSvc = testCase.StatusServiceFn() 1315 } 1316 1317 svc := formation.NewService(nil, nil, labelDefRepo, nil, formationRepo, formationTemplateRepo, nil, uidService, labelDefService, nil, nil, nil, nil, nil, nil, nil, notificationsService, constraintEngine, webhookRepo, statusSvc, runtimeType, applicationType) 1318 1319 // WHEN 1320 actual, err := svc.CreateFormation(ctx, TntInternalID, *input, testCase.TemplateName) 1321 1322 // THEN 1323 if testCase.ExpectedErrMessage == "" { 1324 require.NoError(t, err) 1325 assert.Equal(t, testCase.ExpectedFormation, actual) 1326 } else { 1327 require.Error(t, err) 1328 require.Contains(t, err.Error(), testCase.ExpectedErrMessage) 1329 require.Nil(t, actual) 1330 } 1331 1332 mock.AssertExpectationsForObjects(t, uidService, labelDefRepo, labelDefService, notificationsService, formationRepo, formationTemplateRepo, constraintEngine, webhookRepo, statusSvc) 1333 }) 1334 } 1335 } 1336 1337 func TestServiceDeleteFormation(t *testing.T) { 1338 ctx := context.TODO() 1339 ctx = tenant.SaveToContext(ctx, TntInternalID, TntExternalID) 1340 1341 in := model.Formation{ 1342 Name: testFormationName, 1343 } 1344 1345 formationWithCreateErrorStateAndTechnicalAssignmentError := fixFormationModelWithStateAndAssignmentError(t, model.DeleteErrorFormationState, testErr.Error(), formationassignment.TechnicalError) 1346 1347 expectedFormation := fixFormationModelWithState(model.ReadyFormationState) 1348 1349 formationWithReadyState := fixFormationModelWithState(model.ReadyFormationState) 1350 1351 testSchema, err := labeldef.NewSchemaForFormations([]string{testScenario, testFormationName}) 1352 assert.NoError(t, err) 1353 testSchemaLblDef := fixScenariosLabelDefinition(TntInternalID, testSchema) 1354 1355 newSchema, err := labeldef.NewSchemaForFormations([]string{testScenario}) 1356 assert.NoError(t, err) 1357 newSchemaLblDef := fixScenariosLabelDefinition(TntInternalID, newSchema) 1358 1359 nilSchemaLblDef := fixScenariosLabelDefinition(TntInternalID, testSchema) 1360 nilSchemaLblDef.Schema = nil 1361 1362 testCases := []struct { 1363 Name string 1364 LabelDefRepositoryFn func() *automock.LabelDefRepository 1365 LabelDefServiceFn func() *automock.LabelDefService 1366 NotificationsSvcFn func() *automock.NotificationsService 1367 FormationRepoFn func() *automock.FormationRepository 1368 FormationTemplateRepoFn func() *automock.FormationTemplateRepository 1369 ConstraintEngineFn func() *automock.ConstraintEngine 1370 webhookRepoFn func() *automock.WebhookRepository 1371 InputFormation model.Formation 1372 ExpectedFormation *model.Formation 1373 ExpectedErrMessage string 1374 }{ 1375 { 1376 Name: "success", 1377 LabelDefRepositoryFn: func() *automock.LabelDefRepository { 1378 labelDefRepo := &automock.LabelDefRepository{} 1379 labelDefRepo.On("GetByKey", ctx, TntInternalID, model.ScenariosKey).Return(&testSchemaLblDef, nil) 1380 labelDefRepo.On("UpdateWithVersion", ctx, newSchemaLblDef).Return(nil) 1381 return labelDefRepo 1382 }, 1383 LabelDefServiceFn: func() *automock.LabelDefService { 1384 labelDefService := &automock.LabelDefService{} 1385 labelDefService.On("ValidateExistingLabelsAgainstSchema", ctx, newSchema, TntInternalID, model.ScenariosKey).Return(nil) 1386 labelDefService.On("ValidateAutomaticScenarioAssignmentAgainstSchema", ctx, newSchema, TntInternalID, model.ScenariosKey).Return(nil) 1387 return labelDefService 1388 }, 1389 NotificationsSvcFn: func() *automock.NotificationsService { 1390 notificationSvc := &automock.NotificationsService{} 1391 notificationSvc.On("GenerateFormationNotifications", ctx, emptyFormationLifecycleWebhooks, TntInternalID, formationWithReadyState, testFormationTemplateName, FormationTemplateID, model.DeleteFormation).Return(emptyFormationNotificationRequests, nil).Once() 1392 return notificationSvc 1393 }, 1394 FormationRepoFn: func() *automock.FormationRepository { 1395 formationRepoMock := &automock.FormationRepository{} 1396 formationRepoMock.On("DeleteByName", ctx, TntInternalID, testFormationName).Return(nil).Once() 1397 formationRepoMock.On("GetByName", ctx, testFormationName, TntInternalID).Return(expectedFormation, nil).Once() 1398 return formationRepoMock 1399 }, 1400 FormationTemplateRepoFn: func() *automock.FormationTemplateRepository { 1401 repo := &automock.FormationTemplateRepository{} 1402 repo.On("Get", ctx, FormationTemplateID).Return(fixFormationTemplateModel(), nil).Once() 1403 return repo 1404 }, 1405 ConstraintEngineFn: func() *automock.ConstraintEngine { 1406 engine := &automock.ConstraintEngine{} 1407 engine.On("EnforceConstraints", ctx, preDeleteLocation, deleteFormationDetails, FormationTemplateID).Return(nil).Once() 1408 engine.On("EnforceConstraints", ctx, postDeleteLocation, deleteFormationDetails, FormationTemplateID).Return(nil).Once() 1409 return engine 1410 }, 1411 webhookRepoFn: func() *automock.WebhookRepository { 1412 webhookRepo := &automock.WebhookRepository{} 1413 webhookRepo.On("ListByReferenceObjectIDGlobal", ctx, FormationTemplateID, model.FormationTemplateWebhookReference).Return(emptyFormationLifecycleWebhooks, nil).Once() 1414 return webhookRepo 1415 }, 1416 InputFormation: in, 1417 ExpectedFormation: expectedFormation, 1418 }, 1419 { 1420 Name: "success when formation has async webhook", 1421 NotificationsSvcFn: func() *automock.NotificationsService { 1422 notificationSvc := &automock.NotificationsService{} 1423 notificationSvc.On("GenerateFormationNotifications", ctx, formationLifecycleAsyncWebhooks, TntInternalID, formationWithReadyState, testFormationTemplateName, FormationTemplateID, model.DeleteFormation).Return(formationNotificationAsyncDeleteRequests, nil).Once() 1424 notificationSvc.On("SendNotification", ctx, formationNotificationAsyncDeleteRequest).Return(fixFormationNotificationWebhookResponse(200, 200, nil), nil).Once() 1425 return notificationSvc 1426 }, 1427 FormationRepoFn: func() *automock.FormationRepository { 1428 formationRepoMock := &automock.FormationRepository{} 1429 formationRepoMock.On("GetByName", ctx, testFormationName, TntInternalID).Return(fixFormationModelWithState(model.ReadyFormationState), nil).Once() 1430 formationRepoMock.On("Update", ctx, fixFormationModelWithState(model.DeletingFormationState)).Return(nil).Once() 1431 return formationRepoMock 1432 }, 1433 FormationTemplateRepoFn: func() *automock.FormationTemplateRepository { 1434 repo := &automock.FormationTemplateRepository{} 1435 repo.On("Get", ctx, FormationTemplateID).Return(fixFormationTemplateModel(), nil).Once() 1436 return repo 1437 }, 1438 ConstraintEngineFn: func() *automock.ConstraintEngine { 1439 engine := &automock.ConstraintEngine{} 1440 engine.On("EnforceConstraints", ctx, preDeleteLocation, deleteFormationDetails, FormationTemplateID).Return(nil).Once() 1441 return engine 1442 }, 1443 webhookRepoFn: func() *automock.WebhookRepository { 1444 webhookRepo := &automock.WebhookRepository{} 1445 webhookRepo.On("ListByReferenceObjectIDGlobal", ctx, FormationTemplateID, model.FormationTemplateWebhookReference).Return(formationLifecycleAsyncWebhooks, nil).Once() 1446 return webhookRepo 1447 }, 1448 InputFormation: in, 1449 ExpectedFormation: fixFormationModelWithState(model.DeletingFormationState), 1450 }, 1451 { 1452 Name: "error when formation has async webhook and updating to deleting state fails", 1453 NotificationsSvcFn: func() *automock.NotificationsService { 1454 notificationSvc := &automock.NotificationsService{} 1455 notificationSvc.On("GenerateFormationNotifications", ctx, formationLifecycleAsyncWebhooks, TntInternalID, formationWithReadyState, testFormationTemplateName, FormationTemplateID, model.DeleteFormation).Return(formationNotificationAsyncDeleteRequests, nil).Once() 1456 notificationSvc.On("SendNotification", ctx, formationNotificationAsyncDeleteRequest).Return(fixFormationNotificationWebhookResponse(200, 200, nil), nil).Once() 1457 return notificationSvc 1458 }, 1459 FormationRepoFn: func() *automock.FormationRepository { 1460 formationRepoMock := &automock.FormationRepository{} 1461 formationRepoMock.On("GetByName", ctx, testFormationName, TntInternalID).Return(fixFormationModelWithState(model.ReadyFormationState), nil).Once() 1462 formationRepoMock.On("Update", ctx, fixFormationModelWithState(model.DeletingFormationState)).Return(testErr).Once() 1463 return formationRepoMock 1464 }, 1465 FormationTemplateRepoFn: func() *automock.FormationTemplateRepository { 1466 repo := &automock.FormationTemplateRepository{} 1467 repo.On("Get", ctx, FormationTemplateID).Return(fixFormationTemplateModel(), nil).Once() 1468 return repo 1469 }, 1470 ConstraintEngineFn: func() *automock.ConstraintEngine { 1471 engine := &automock.ConstraintEngine{} 1472 engine.On("EnforceConstraints", ctx, preDeleteLocation, deleteFormationDetails, FormationTemplateID).Return(nil).Once() 1473 return engine 1474 }, 1475 webhookRepoFn: func() *automock.WebhookRepository { 1476 webhookRepo := &automock.WebhookRepository{} 1477 webhookRepo.On("ListByReferenceObjectIDGlobal", ctx, FormationTemplateID, model.FormationTemplateWebhookReference).Return(formationLifecycleAsyncWebhooks, nil).Once() 1478 return webhookRepo 1479 }, 1480 InputFormation: in, 1481 ExpectedErrMessage: testErr.Error(), 1482 }, 1483 { 1484 Name: "error when can not get labeldef", 1485 LabelDefRepositoryFn: func() *automock.LabelDefRepository { 1486 labelDefRepo := &automock.LabelDefRepository{} 1487 labelDefRepo.On("GetByKey", ctx, TntInternalID, model.ScenariosKey).Return(nil, testErr).Once() 1488 return labelDefRepo 1489 }, 1490 NotificationsSvcFn: func() *automock.NotificationsService { 1491 notificationSvc := &automock.NotificationsService{} 1492 notificationSvc.On("GenerateFormationNotifications", ctx, emptyFormationLifecycleWebhooks, TntInternalID, formationWithReadyState, testFormationTemplateName, FormationTemplateID, model.DeleteFormation).Return(emptyFormationNotificationRequests, nil).Once() 1493 return notificationSvc 1494 }, 1495 FormationRepoFn: func() *automock.FormationRepository { 1496 formationRepoMock := &automock.FormationRepository{} 1497 formationRepoMock.On("GetByName", ctx, testFormationName, TntInternalID).Return(expectedFormation, nil).Once() 1498 return formationRepoMock 1499 }, 1500 FormationTemplateRepoFn: func() *automock.FormationTemplateRepository { 1501 repo := &automock.FormationTemplateRepository{} 1502 repo.On("Get", ctx, FormationTemplateID).Return(fixFormationTemplateModel(), nil).Once() 1503 return repo 1504 }, 1505 ConstraintEngineFn: func() *automock.ConstraintEngine { 1506 engine := &automock.ConstraintEngine{} 1507 engine.On("EnforceConstraints", ctx, preDeleteLocation, deleteFormationDetails, FormationTemplateID).Return(nil).Once() 1508 return engine 1509 }, 1510 webhookRepoFn: func() *automock.WebhookRepository { 1511 webhookRepo := &automock.WebhookRepository{} 1512 webhookRepo.On("ListByReferenceObjectIDGlobal", ctx, FormationTemplateID, model.FormationTemplateWebhookReference).Return(emptyFormationLifecycleWebhooks, nil).Once() 1513 return webhookRepo 1514 }, 1515 InputFormation: in, 1516 ExpectedErrMessage: "while getting `scenarios` label definition: Test error", 1517 }, 1518 { 1519 Name: "error when labeldef's schema is missing", 1520 LabelDefRepositoryFn: func() *automock.LabelDefRepository { 1521 labelDefRepo := &automock.LabelDefRepository{} 1522 labelDefRepo.On("GetByKey", ctx, TntInternalID, model.ScenariosKey).Return(&nilSchemaLblDef, nil) 1523 return labelDefRepo 1524 }, 1525 NotificationsSvcFn: func() *automock.NotificationsService { 1526 notificationSvc := &automock.NotificationsService{} 1527 notificationSvc.On("GenerateFormationNotifications", ctx, emptyFormationLifecycleWebhooks, TntInternalID, formationWithReadyState, testFormationTemplateName, FormationTemplateID, model.DeleteFormation).Return(emptyFormationNotificationRequests, nil).Once() 1528 return notificationSvc 1529 }, 1530 FormationRepoFn: func() *automock.FormationRepository { 1531 formationRepoMock := &automock.FormationRepository{} 1532 formationRepoMock.On("GetByName", ctx, testFormationName, TntInternalID).Return(expectedFormation, nil).Once() 1533 return formationRepoMock 1534 }, 1535 FormationTemplateRepoFn: func() *automock.FormationTemplateRepository { 1536 repo := &automock.FormationTemplateRepository{} 1537 repo.On("Get", ctx, FormationTemplateID).Return(fixFormationTemplateModel(), nil).Once() 1538 return repo 1539 }, 1540 ConstraintEngineFn: func() *automock.ConstraintEngine { 1541 engine := &automock.ConstraintEngine{} 1542 engine.On("EnforceConstraints", ctx, preDeleteLocation, deleteFormationDetails, FormationTemplateID).Return(nil).Once() 1543 return engine 1544 }, 1545 webhookRepoFn: func() *automock.WebhookRepository { 1546 webhookRepo := &automock.WebhookRepository{} 1547 webhookRepo.On("ListByReferenceObjectIDGlobal", ctx, FormationTemplateID, model.FormationTemplateWebhookReference).Return(emptyFormationLifecycleWebhooks, nil).Once() 1548 return webhookRepo 1549 }, 1550 InputFormation: in, 1551 ExpectedErrMessage: "missing schema", 1552 }, 1553 { 1554 Name: "error when validating existing labels against the schema", 1555 LabelDefRepositoryFn: func() *automock.LabelDefRepository { 1556 labelDefRepo := &automock.LabelDefRepository{} 1557 labelDefRepo.On("GetByKey", ctx, TntInternalID, model.ScenariosKey).Return(&testSchemaLblDef, nil) 1558 return labelDefRepo 1559 }, 1560 LabelDefServiceFn: func() *automock.LabelDefService { 1561 labelDefService := &automock.LabelDefService{} 1562 labelDefService.On("ValidateExistingLabelsAgainstSchema", ctx, newSchema, TntInternalID, model.ScenariosKey).Return(testErr) 1563 return labelDefService 1564 }, 1565 NotificationsSvcFn: func() *automock.NotificationsService { 1566 notificationSvc := &automock.NotificationsService{} 1567 notificationSvc.On("GenerateFormationNotifications", ctx, emptyFormationLifecycleWebhooks, TntInternalID, formationWithReadyState, testFormationTemplateName, FormationTemplateID, model.DeleteFormation).Return(emptyFormationNotificationRequests, nil).Once() 1568 return notificationSvc 1569 }, 1570 FormationRepoFn: func() *automock.FormationRepository { 1571 formationRepoMock := &automock.FormationRepository{} 1572 formationRepoMock.On("GetByName", ctx, testFormationName, TntInternalID).Return(expectedFormation, nil).Once() 1573 return formationRepoMock 1574 }, 1575 FormationTemplateRepoFn: func() *automock.FormationTemplateRepository { 1576 repo := &automock.FormationTemplateRepository{} 1577 repo.On("Get", ctx, FormationTemplateID).Return(fixFormationTemplateModel(), nil).Once() 1578 return repo 1579 }, 1580 ConstraintEngineFn: func() *automock.ConstraintEngine { 1581 engine := &automock.ConstraintEngine{} 1582 engine.On("EnforceConstraints", ctx, preDeleteLocation, deleteFormationDetails, FormationTemplateID).Return(nil).Once() 1583 return engine 1584 }, 1585 webhookRepoFn: func() *automock.WebhookRepository { 1586 webhookRepo := &automock.WebhookRepository{} 1587 webhookRepo.On("ListByReferenceObjectIDGlobal", ctx, FormationTemplateID, model.FormationTemplateWebhookReference).Return(emptyFormationLifecycleWebhooks, nil).Once() 1588 return webhookRepo 1589 }, 1590 InputFormation: in, 1591 ExpectedErrMessage: testErr.Error(), 1592 }, 1593 { 1594 Name: "error when validating automatic scenario assignment against the schema", 1595 LabelDefRepositoryFn: func() *automock.LabelDefRepository { 1596 labelDefRepo := &automock.LabelDefRepository{} 1597 labelDefRepo.On("GetByKey", ctx, TntInternalID, model.ScenariosKey).Return(&testSchemaLblDef, nil) 1598 return labelDefRepo 1599 }, 1600 LabelDefServiceFn: func() *automock.LabelDefService { 1601 labelDefService := &automock.LabelDefService{} 1602 labelDefService.On("ValidateExistingLabelsAgainstSchema", ctx, newSchema, TntInternalID, model.ScenariosKey).Return(nil) 1603 labelDefService.On("ValidateAutomaticScenarioAssignmentAgainstSchema", ctx, newSchema, TntInternalID, model.ScenariosKey).Return(testErr) 1604 return labelDefService 1605 }, 1606 NotificationsSvcFn: func() *automock.NotificationsService { 1607 notificationSvc := &automock.NotificationsService{} 1608 notificationSvc.On("GenerateFormationNotifications", ctx, emptyFormationLifecycleWebhooks, TntInternalID, formationWithReadyState, testFormationTemplateName, FormationTemplateID, model.DeleteFormation).Return(emptyFormationNotificationRequests, nil).Once() 1609 return notificationSvc 1610 }, 1611 FormationRepoFn: func() *automock.FormationRepository { 1612 formationRepoMock := &automock.FormationRepository{} 1613 formationRepoMock.On("GetByName", ctx, testFormationName, TntInternalID).Return(expectedFormation, nil).Once() 1614 return formationRepoMock 1615 }, 1616 FormationTemplateRepoFn: func() *automock.FormationTemplateRepository { 1617 repo := &automock.FormationTemplateRepository{} 1618 repo.On("Get", ctx, FormationTemplateID).Return(fixFormationTemplateModel(), nil).Once() 1619 return repo 1620 }, 1621 ConstraintEngineFn: func() *automock.ConstraintEngine { 1622 engine := &automock.ConstraintEngine{} 1623 engine.On("EnforceConstraints", ctx, preDeleteLocation, deleteFormationDetails, FormationTemplateID).Return(nil).Once() 1624 return engine 1625 }, 1626 webhookRepoFn: func() *automock.WebhookRepository { 1627 webhookRepo := &automock.WebhookRepository{} 1628 webhookRepo.On("ListByReferenceObjectIDGlobal", ctx, FormationTemplateID, model.FormationTemplateWebhookReference).Return(emptyFormationLifecycleWebhooks, nil).Once() 1629 return webhookRepo 1630 }, 1631 InputFormation: in, 1632 ExpectedErrMessage: "while validating Scenario Assignments against a new schema: Test error", 1633 }, 1634 { 1635 Name: "error when update with version fails", 1636 LabelDefRepositoryFn: func() *automock.LabelDefRepository { 1637 labelDefRepo := &automock.LabelDefRepository{} 1638 labelDefRepo.On("GetByKey", ctx, TntInternalID, model.ScenariosKey).Return(&newSchemaLblDef, nil) 1639 labelDefRepo.On("UpdateWithVersion", ctx, newSchemaLblDef).Return(testErr) 1640 return labelDefRepo 1641 }, 1642 LabelDefServiceFn: func() *automock.LabelDefService { 1643 labelDefService := &automock.LabelDefService{} 1644 labelDefService.On("ValidateExistingLabelsAgainstSchema", ctx, newSchema, TntInternalID, newSchemaLblDef.Key).Return(nil) 1645 labelDefService.On("ValidateAutomaticScenarioAssignmentAgainstSchema", ctx, newSchema, TntInternalID, newSchemaLblDef.Key).Return(nil) 1646 return labelDefService 1647 }, 1648 NotificationsSvcFn: func() *automock.NotificationsService { 1649 notificationSvc := &automock.NotificationsService{} 1650 notificationSvc.On("GenerateFormationNotifications", ctx, emptyFormationLifecycleWebhooks, TntInternalID, formationWithReadyState, testFormationTemplateName, FormationTemplateID, model.DeleteFormation).Return(emptyFormationNotificationRequests, nil).Once() 1651 return notificationSvc 1652 }, 1653 FormationRepoFn: func() *automock.FormationRepository { 1654 formationRepoMock := &automock.FormationRepository{} 1655 formationRepoMock.On("GetByName", ctx, testFormationName, TntInternalID).Return(expectedFormation, nil).Once() 1656 return formationRepoMock 1657 }, 1658 FormationTemplateRepoFn: func() *automock.FormationTemplateRepository { 1659 repo := &automock.FormationTemplateRepository{} 1660 repo.On("Get", ctx, FormationTemplateID).Return(fixFormationTemplateModel(), nil).Once() 1661 return repo 1662 }, 1663 ConstraintEngineFn: func() *automock.ConstraintEngine { 1664 engine := &automock.ConstraintEngine{} 1665 engine.On("EnforceConstraints", ctx, preDeleteLocation, deleteFormationDetails, FormationTemplateID).Return(nil).Once() 1666 return engine 1667 }, 1668 webhookRepoFn: func() *automock.WebhookRepository { 1669 webhookRepo := &automock.WebhookRepository{} 1670 webhookRepo.On("ListByReferenceObjectIDGlobal", ctx, FormationTemplateID, model.FormationTemplateWebhookReference).Return(emptyFormationLifecycleWebhooks, nil).Once() 1671 return webhookRepo 1672 }, 1673 InputFormation: in, 1674 ExpectedErrMessage: testErr.Error(), 1675 }, 1676 { 1677 Name: "Returns error when can't get formation by name", 1678 FormationRepoFn: func() *automock.FormationRepository { 1679 formationRepoMock := &automock.FormationRepository{} 1680 formationRepoMock.On("GetByName", ctx, testFormationName, TntInternalID).Return(nil, testErr).Once() 1681 return formationRepoMock 1682 }, 1683 InputFormation: in, 1684 ExpectedFormation: nil, 1685 ExpectedErrMessage: "while deleting formation: An error occurred while getting formation by name: \"test-formation\": Test error", 1686 }, 1687 { 1688 Name: "error when deleting formation by name fails", 1689 LabelDefRepositoryFn: func() *automock.LabelDefRepository { 1690 labelDefRepo := &automock.LabelDefRepository{} 1691 labelDefRepo.On("GetByKey", ctx, TntInternalID, model.ScenariosKey).Return(&testSchemaLblDef, nil) 1692 labelDefRepo.On("UpdateWithVersion", ctx, newSchemaLblDef).Return(nil) 1693 return labelDefRepo 1694 }, 1695 LabelDefServiceFn: func() *automock.LabelDefService { 1696 labelDefService := &automock.LabelDefService{} 1697 labelDefService.On("ValidateExistingLabelsAgainstSchema", ctx, newSchema, TntInternalID, model.ScenariosKey).Return(nil) 1698 labelDefService.On("ValidateAutomaticScenarioAssignmentAgainstSchema", ctx, newSchema, TntInternalID, model.ScenariosKey).Return(nil) 1699 return labelDefService 1700 }, 1701 NotificationsSvcFn: func() *automock.NotificationsService { 1702 notificationSvc := &automock.NotificationsService{} 1703 notificationSvc.On("GenerateFormationNotifications", ctx, emptyFormationLifecycleWebhooks, TntInternalID, formationWithReadyState, testFormationTemplateName, FormationTemplateID, model.DeleteFormation).Return(emptyFormationNotificationRequests, nil).Once() 1704 return notificationSvc 1705 }, 1706 FormationRepoFn: func() *automock.FormationRepository { 1707 formationRepoMock := &automock.FormationRepository{} 1708 formationRepoMock.On("GetByName", ctx, testFormationName, TntInternalID).Return(expectedFormation, nil).Once() 1709 formationRepoMock.On("DeleteByName", ctx, TntInternalID, testFormationName).Return(testErr).Once() 1710 return formationRepoMock 1711 }, 1712 FormationTemplateRepoFn: func() *automock.FormationTemplateRepository { 1713 repo := &automock.FormationTemplateRepository{} 1714 repo.On("Get", ctx, FormationTemplateID).Return(fixFormationTemplateModel(), nil).Once() 1715 return repo 1716 }, 1717 ConstraintEngineFn: func() *automock.ConstraintEngine { 1718 engine := &automock.ConstraintEngine{} 1719 engine.On("EnforceConstraints", ctx, preDeleteLocation, deleteFormationDetails, FormationTemplateID).Return(nil).Once() 1720 return engine 1721 }, 1722 webhookRepoFn: func() *automock.WebhookRepository { 1723 webhookRepo := &automock.WebhookRepository{} 1724 webhookRepo.On("ListByReferenceObjectIDGlobal", ctx, FormationTemplateID, model.FormationTemplateWebhookReference).Return(emptyFormationLifecycleWebhooks, nil).Once() 1725 return webhookRepo 1726 }, 1727 InputFormation: in, 1728 ExpectedErrMessage: "An error occurred while deleting formation with name: \"test-formation\": Test error", 1729 }, 1730 { 1731 Name: "error while enforcing constraints pre operation", 1732 FormationRepoFn: func() *automock.FormationRepository { 1733 formationRepoMock := &automock.FormationRepository{} 1734 formationRepoMock.On("GetByName", ctx, testFormationName, TntInternalID).Return(expectedFormation, nil).Once() 1735 return formationRepoMock 1736 }, 1737 FormationTemplateRepoFn: func() *automock.FormationTemplateRepository { 1738 repo := &automock.FormationTemplateRepository{} 1739 repo.On("Get", ctx, FormationTemplateID).Return(fixFormationTemplateModel(), nil).Once() 1740 return repo 1741 }, 1742 ConstraintEngineFn: func() *automock.ConstraintEngine { 1743 engine := &automock.ConstraintEngine{} 1744 engine.On("EnforceConstraints", ctx, preDeleteLocation, deleteFormationDetails, FormationTemplateID).Return(testErr).Once() 1745 return engine 1746 }, 1747 InputFormation: in, 1748 ExpectedErrMessage: "while enforcing constraints for target operation \"DELETE_FORMATION\" and constraint type \"PRE\": Test error", 1749 }, 1750 { 1751 Name: "error while enforcing constraint post operation", 1752 LabelDefRepositoryFn: func() *automock.LabelDefRepository { 1753 labelDefRepo := &automock.LabelDefRepository{} 1754 labelDefRepo.On("GetByKey", ctx, TntInternalID, model.ScenariosKey).Return(&testSchemaLblDef, nil) 1755 labelDefRepo.On("UpdateWithVersion", ctx, newSchemaLblDef).Return(nil) 1756 return labelDefRepo 1757 }, 1758 LabelDefServiceFn: func() *automock.LabelDefService { 1759 labelDefService := &automock.LabelDefService{} 1760 labelDefService.On("ValidateExistingLabelsAgainstSchema", ctx, newSchema, TntInternalID, model.ScenariosKey).Return(nil) 1761 labelDefService.On("ValidateAutomaticScenarioAssignmentAgainstSchema", ctx, newSchema, TntInternalID, model.ScenariosKey).Return(nil) 1762 return labelDefService 1763 }, 1764 NotificationsSvcFn: func() *automock.NotificationsService { 1765 notificationSvc := &automock.NotificationsService{} 1766 notificationSvc.On("GenerateFormationNotifications", ctx, emptyFormationLifecycleWebhooks, TntInternalID, formationWithReadyState, testFormationTemplateName, FormationTemplateID, model.DeleteFormation).Return(emptyFormationNotificationRequests, nil).Once() 1767 return notificationSvc 1768 }, 1769 FormationRepoFn: func() *automock.FormationRepository { 1770 formationRepoMock := &automock.FormationRepository{} 1771 formationRepoMock.On("DeleteByName", ctx, TntInternalID, testFormationName).Return(nil).Once() 1772 formationRepoMock.On("GetByName", ctx, testFormationName, TntInternalID).Return(expectedFormation, nil).Once() 1773 return formationRepoMock 1774 }, 1775 FormationTemplateRepoFn: func() *automock.FormationTemplateRepository { 1776 repo := &automock.FormationTemplateRepository{} 1777 repo.On("Get", ctx, FormationTemplateID).Return(fixFormationTemplateModel(), nil).Once() 1778 return repo 1779 }, 1780 ConstraintEngineFn: func() *automock.ConstraintEngine { 1781 engine := &automock.ConstraintEngine{} 1782 engine.On("EnforceConstraints", ctx, preDeleteLocation, deleteFormationDetails, FormationTemplateID).Return(nil).Once() 1783 engine.On("EnforceConstraints", ctx, postDeleteLocation, deleteFormationDetails, FormationTemplateID).Return(testErr).Once() 1784 return engine 1785 }, 1786 webhookRepoFn: func() *automock.WebhookRepository { 1787 webhookRepo := &automock.WebhookRepository{} 1788 webhookRepo.On("ListByReferenceObjectIDGlobal", ctx, FormationTemplateID, model.FormationTemplateWebhookReference).Return(emptyFormationLifecycleWebhooks, nil).Once() 1789 return webhookRepo 1790 }, 1791 InputFormation: in, 1792 ExpectedErrMessage: "while enforcing constraints for target operation \"DELETE_FORMATION\" and constraint type \"POST\": Test error", 1793 }, 1794 { 1795 Name: "Error when listing formation template's webhooks fails", 1796 FormationRepoFn: func() *automock.FormationRepository { 1797 formationRepoMock := &automock.FormationRepository{} 1798 formationRepoMock.On("GetByName", ctx, testFormationName, TntInternalID).Return(expectedFormation, nil).Once() 1799 return formationRepoMock 1800 }, 1801 FormationTemplateRepoFn: func() *automock.FormationTemplateRepository { 1802 repo := &automock.FormationTemplateRepository{} 1803 repo.On("Get", ctx, FormationTemplateID).Return(fixFormationTemplateModel(), nil).Once() 1804 return repo 1805 }, 1806 ConstraintEngineFn: func() *automock.ConstraintEngine { 1807 engine := &automock.ConstraintEngine{} 1808 engine.On("EnforceConstraints", ctx, preDeleteLocation, deleteFormationDetails, FormationTemplateID).Return(nil).Once() 1809 return engine 1810 }, 1811 webhookRepoFn: func() *automock.WebhookRepository { 1812 webhookRepo := &automock.WebhookRepository{} 1813 webhookRepo.On("ListByReferenceObjectIDGlobal", ctx, FormationTemplateID, model.FormationTemplateWebhookReference).Return(nil, testErr).Once() 1814 return webhookRepo 1815 }, 1816 InputFormation: in, 1817 ExpectedFormation: nil, 1818 ExpectedErrMessage: testErr.Error(), 1819 }, 1820 { 1821 Name: "Error when generating formation notifications fails", 1822 NotificationsSvcFn: func() *automock.NotificationsService { 1823 notificationSvc := &automock.NotificationsService{} 1824 notificationSvc.On("GenerateFormationNotifications", ctx, emptyFormationLifecycleWebhooks, TntInternalID, formationWithReadyState, testFormationTemplateName, FormationTemplateID, model.DeleteFormation).Return(nil, testErr).Once() 1825 return notificationSvc 1826 }, 1827 FormationRepoFn: func() *automock.FormationRepository { 1828 formationRepoMock := &automock.FormationRepository{} 1829 formationRepoMock.On("GetByName", ctx, testFormationName, TntInternalID).Return(expectedFormation, nil).Once() 1830 return formationRepoMock 1831 }, 1832 FormationTemplateRepoFn: func() *automock.FormationTemplateRepository { 1833 repo := &automock.FormationTemplateRepository{} 1834 repo.On("Get", ctx, FormationTemplateID).Return(fixFormationTemplateModel(), nil).Once() 1835 return repo 1836 }, 1837 ConstraintEngineFn: func() *automock.ConstraintEngine { 1838 engine := &automock.ConstraintEngine{} 1839 engine.On("EnforceConstraints", ctx, preDeleteLocation, deleteFormationDetails, FormationTemplateID).Return(nil).Once() 1840 return engine 1841 }, 1842 webhookRepoFn: func() *automock.WebhookRepository { 1843 webhookRepo := &automock.WebhookRepository{} 1844 webhookRepo.On("ListByReferenceObjectIDGlobal", ctx, FormationTemplateID, model.FormationTemplateWebhookReference).Return(emptyFormationLifecycleWebhooks, nil).Once() 1845 return webhookRepo 1846 }, 1847 InputFormation: in, 1848 ExpectedFormation: nil, 1849 ExpectedErrMessage: testErr.Error(), 1850 }, 1851 { 1852 Name: "Error when processing formation notifications fails", 1853 NotificationsSvcFn: func() *automock.NotificationsService { 1854 notificationSvc := &automock.NotificationsService{} 1855 notificationSvc.On("GenerateFormationNotifications", ctx, emptyFormationLifecycleWebhooks, TntInternalID, formationWithReadyState, testFormationTemplateName, FormationTemplateID, model.DeleteFormation).Return(formationNotificationAsyncDeleteRequests, nil).Once() 1856 notificationSvc.On("SendNotification", ctx, formationNotificationAsyncDeleteRequest).Return(nil, testErr).Once() 1857 return notificationSvc 1858 }, 1859 FormationRepoFn: func() *automock.FormationRepository { 1860 formationRepoMock := &automock.FormationRepository{} 1861 formationRepoMock.On("Update", ctx, formationWithCreateErrorStateAndTechnicalAssignmentError).Return(testErr).Once() 1862 formationRepoMock.On("GetByName", ctx, testFormationName, TntInternalID).Return(expectedFormation, nil).Once() 1863 return formationRepoMock 1864 }, 1865 FormationTemplateRepoFn: func() *automock.FormationTemplateRepository { 1866 repo := &automock.FormationTemplateRepository{} 1867 repo.On("Get", ctx, FormationTemplateID).Return(fixFormationTemplateModel(), nil).Once() 1868 return repo 1869 }, 1870 ConstraintEngineFn: func() *automock.ConstraintEngine { 1871 engine := &automock.ConstraintEngine{} 1872 engine.On("EnforceConstraints", ctx, preDeleteLocation, deleteFormationDetails, FormationTemplateID).Return(nil).Once() 1873 return engine 1874 }, 1875 webhookRepoFn: func() *automock.WebhookRepository { 1876 webhookRepo := &automock.WebhookRepository{} 1877 webhookRepo.On("ListByReferenceObjectIDGlobal", ctx, FormationTemplateID, model.FormationTemplateWebhookReference).Return(emptyFormationLifecycleWebhooks, nil).Once() 1878 return webhookRepo 1879 }, 1880 InputFormation: in, 1881 ExpectedFormation: nil, 1882 ExpectedErrMessage: testErr.Error(), 1883 }, 1884 } 1885 1886 for _, testCase := range testCases { 1887 t.Run(testCase.Name, func(t *testing.T) { 1888 // GIVEN 1889 labelDefRepo := unusedLabelDefRepository() 1890 if testCase.LabelDefRepositoryFn != nil { 1891 labelDefRepo = testCase.LabelDefRepositoryFn() 1892 } 1893 1894 labelDefService := unusedLabelDefService() 1895 if testCase.LabelDefServiceFn != nil { 1896 labelDefService = testCase.LabelDefServiceFn() 1897 } 1898 1899 notificationsService := unusedNotificationsService() 1900 if testCase.NotificationsSvcFn != nil { 1901 notificationsService = testCase.NotificationsSvcFn() 1902 } 1903 1904 formationRepo := unusedFormationRepo() 1905 if testCase.FormationRepoFn != nil { 1906 formationRepo = testCase.FormationRepoFn() 1907 } 1908 formationTemplateRepo := unusedFormationTemplateRepo() 1909 if testCase.FormationTemplateRepoFn != nil { 1910 formationTemplateRepo = testCase.FormationTemplateRepoFn() 1911 } 1912 1913 constraintEngine := unusedConstraintEngine() 1914 if testCase.ConstraintEngineFn != nil { 1915 constraintEngine = testCase.ConstraintEngineFn() 1916 } 1917 webhookRepo := unusedWebhookRepository() 1918 if testCase.webhookRepoFn != nil { 1919 webhookRepo = testCase.webhookRepoFn() 1920 } 1921 1922 svc := formation.NewService(nil, nil, labelDefRepo, nil, formationRepo, formationTemplateRepo, nil, nil, labelDefService, nil, nil, nil, nil, nil, nil, nil, notificationsService, constraintEngine, webhookRepo, nil, runtimeType, applicationType) 1923 1924 // WHEN 1925 actual, err := svc.DeleteFormation(ctx, TntInternalID, testCase.InputFormation) 1926 1927 // THEN 1928 if testCase.ExpectedErrMessage == "" { 1929 require.NoError(t, err) 1930 assert.Equal(t, testCase.ExpectedFormation, actual) 1931 } else { 1932 require.Error(t, err) 1933 require.Contains(t, err.Error(), testCase.ExpectedErrMessage) 1934 require.Nil(t, actual) 1935 } 1936 1937 mock.AssertExpectationsForObjects(t, labelDefRepo, labelDefService, notificationsService, formationRepo, formationTemplateRepo, constraintEngine) 1938 }) 1939 } 1940 } 1941 1942 func TestService_DeleteManyASAForSameTargetTenant(t *testing.T) { 1943 ctx := fixCtxWithTenant() 1944 1945 scenarioNameA := "scenario-A" 1946 scenarioNameB := "scenario-B" 1947 models := []*model.AutomaticScenarioAssignment{ 1948 { 1949 ScenarioName: scenarioNameA, 1950 TargetTenantID: TargetTenantID, 1951 }, 1952 { 1953 ScenarioName: scenarioNameB, 1954 TargetTenantID: TargetTenantID, 1955 }, 1956 } 1957 1958 formations := []*model.Formation{ 1959 { 1960 ID: FormationID, 1961 TenantID: tenantID.String(), 1962 FormationTemplateID: FormationTemplateID, 1963 Name: scenarioNameA, 1964 }, 1965 { 1966 ID: FormationID, 1967 TenantID: tenantID.String(), 1968 FormationTemplateID: FormationTemplateID, 1969 Name: scenarioNameB, 1970 }, 1971 } 1972 1973 t.Run("happy path", func(t *testing.T) { 1974 // GIVEN 1975 mockRepo := &automock.AutomaticFormationAssignmentRepository{} 1976 mockRepo.On("DeleteForTargetTenant", ctx, tenantID.String(), TargetTenantID).Return(nil).Once() 1977 1978 runtimeRepo := &automock.RuntimeRepository{} 1979 runtimeRepo.On("ListOwnedRuntimes", ctx, TargetTenantID, runtimeLblFilters).Return(make([]*model.Runtime, 0), nil).Twice() 1980 1981 runtimeRepo.On("ListAllWithUnionSetCombination", ctx, TargetTenantID, runtimeLblFilters).Return(make([]*model.Runtime, 0), nil).Twice() 1982 1983 formationRepo := &automock.FormationRepository{} 1984 formationRepo.On("GetByName", ctx, scenarioNameA, "").Return(formations[0], nil).Once() 1985 formationRepo.On("GetByName", ctx, scenarioNameB, "").Return(formations[1], nil).Once() 1986 1987 formationTemplateRepo := &automock.FormationTemplateRepository{} 1988 formationTemplateRepo.On("Get", ctx, formations[0].FormationTemplateID).Return(&formationTemplate, nil).Once() 1989 formationTemplateRepo.On("Get", ctx, formations[1].FormationTemplateID).Return(&formationTemplate, nil).Once() 1990 1991 defer mock.AssertExpectationsForObjects(t, mockRepo, runtimeRepo, formationRepo, formationTemplateRepo) 1992 1993 svc := formation.NewService(nil, nil, nil, nil, formationRepo, formationTemplateRepo, nil, nil, nil, mockRepo, nil, nil, runtimeRepo, nil, nil, nil, nil, nil, nil, nil, runtimeType, applicationType) 1994 1995 // WHEN 1996 err := svc.DeleteManyASAForSameTargetTenant(ctx, models) 1997 1998 // THEN 1999 require.NoError(t, err) 2000 }) 2001 2002 t.Run("return error when removing assigned scenarios fails", func(t *testing.T) { 2003 // GIVEN 2004 mockRepo := &automock.AutomaticFormationAssignmentRepository{} 2005 mockRepo.On("DeleteForTargetTenant", ctx, tenantID.String(), TargetTenantID).Return(nil).Once() 2006 2007 runtimeRepo := &automock.RuntimeRepository{} 2008 runtimeRepo.On("ListOwnedRuntimes", ctx, TargetTenantID, runtimeLblFilters).Return(nil, fixError()) 2009 2010 formationRepo := &automock.FormationRepository{} 2011 2012 formationRepo.On("GetByName", ctx, scenarioNameA, "").Return(formations[0], nil).Once() 2013 2014 formationTemplateRepo := &automock.FormationTemplateRepository{} 2015 formationTemplateRepo.On("Get", ctx, formations[0].FormationTemplateID).Return(&formationTemplate, nil).Once() 2016 2017 defer mock.AssertExpectationsForObjects(t, mockRepo, runtimeRepo, formationRepo, formationTemplateRepo) 2018 2019 svc := formation.NewService(nil, nil, nil, nil, formationRepo, formationTemplateRepo, nil, nil, nil, mockRepo, nil, nil, runtimeRepo, nil, nil, nil, nil, nil, nil, nil, runtimeType, applicationType) 2020 2021 // WHEN 2022 err := svc.DeleteManyASAForSameTargetTenant(ctx, models) 2023 2024 // THEN 2025 require.Error(t, err) 2026 assert.Contains(t, err.Error(), fixError().Error()) 2027 }) 2028 2029 t.Run("return error when input slice is empty", func(t *testing.T) { 2030 // GIVEN 2031 svc := formation.NewService(nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, runtimeType, applicationType) 2032 2033 // WHEN 2034 err := svc.DeleteManyASAForSameTargetTenant(ctx, []*model.AutomaticScenarioAssignment{}) 2035 2036 // THEN 2037 require.Error(t, err) 2038 assert.Contains(t, err.Error(), "expected at least one item in Assignments slice") 2039 }) 2040 2041 t.Run("return error when input slice contains assignments with different selectors", func(t *testing.T) { 2042 // GIVEN 2043 modelsWithDifferentSelectors := []*model.AutomaticScenarioAssignment{ 2044 { 2045 ScenarioName: scenarioNameA, 2046 TargetTenantID: TargetTenantID, 2047 }, 2048 { 2049 ScenarioName: scenarioNameB, 2050 TargetTenantID: "differentTargetTenantID", 2051 }, 2052 } 2053 2054 svc := formation.NewService(nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, runtimeType, applicationType) 2055 // WHEN 2056 err := svc.DeleteManyASAForSameTargetTenant(ctx, modelsWithDifferentSelectors) 2057 2058 // THEN 2059 require.Error(t, err) 2060 assert.Contains(t, err.Error(), "all input items have to have the same target tenant") 2061 }) 2062 2063 t.Run("returns error on error from repository", func(t *testing.T) { 2064 // GIVEN 2065 mockRepo := &automock.AutomaticFormationAssignmentRepository{} 2066 2067 mockRepo.On("DeleteForTargetTenant", ctx, tenantID.String(), TargetTenantID).Return(fixError()).Once() 2068 2069 defer mock.AssertExpectationsForObjects(t, mockRepo) 2070 2071 svc := formation.NewService(nil, nil, nil, nil, nil, nil, nil, nil, nil, mockRepo, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, runtimeType, applicationType) 2072 // WHEN 2073 err := svc.DeleteManyASAForSameTargetTenant(ctx, models) 2074 2075 // THEN 2076 require.EqualError(t, err, fmt.Sprintf("while deleting the Assignments: %s", ErrMsg)) 2077 }) 2078 2079 t.Run("returns error when empty tenant", func(t *testing.T) { 2080 svc := formation.NewService(nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, runtimeType, applicationType) 2081 err := svc.DeleteManyASAForSameTargetTenant(context.TODO(), models) 2082 require.EqualError(t, err, "cannot read tenant from context") 2083 }) 2084 } 2085 2086 func TestService_CreateAutomaticScenarioAssignment(t *testing.T) { 2087 ctx := fixCtxWithTenant() 2088 2089 testCases := []struct { 2090 Name string 2091 LabelDefServiceFn func() *automock.LabelDefService 2092 AsaRepoFn func() *automock.AutomaticFormationAssignmentRepository 2093 AsaEngineFN func() *automock.AsaEngine 2094 InputASA model.AutomaticScenarioAssignment 2095 ExpectedASA model.AutomaticScenarioAssignment 2096 ExpectedErrMessage string 2097 }{ 2098 { 2099 Name: "happy path", 2100 LabelDefServiceFn: func() *automock.LabelDefService { 2101 return mockScenarioDefServiceThatReturns([]string{testFormationName}) 2102 }, 2103 AsaRepoFn: func() *automock.AutomaticFormationAssignmentRepository { 2104 mockRepo := &automock.AutomaticFormationAssignmentRepository{} 2105 mockRepo.On("Create", ctx, fixModel(testFormationName)).Return(nil).Once() 2106 return mockRepo 2107 }, 2108 AsaEngineFN: func() *automock.AsaEngine { 2109 engine := &automock.AsaEngine{} 2110 engine.On("EnsureScenarioAssigned", ctx, fixModel(testFormationName), mock.Anything).Return(nil).Once() 2111 return engine 2112 }, 2113 InputASA: fixModel(testFormationName), 2114 ExpectedASA: fixModel(testFormationName), 2115 }, 2116 { 2117 Name: "returns error on getting available scenarios from label definition", 2118 LabelDefServiceFn: func() *automock.LabelDefService { 2119 labelDefSvc := &automock.LabelDefService{} 2120 labelDefSvc.On("GetAvailableScenarios", mock.Anything, tenantID.String()).Return(nil, fixError()).Once() 2121 return labelDefSvc 2122 }, 2123 AsaRepoFn: unusedASARepo, 2124 AsaEngineFN: unusedASAEngine, 2125 InputASA: fixModel(ScenarioName), 2126 ExpectedASA: model.AutomaticScenarioAssignment{}, 2127 ExpectedErrMessage: "while getting available scenarios: some error", 2128 }, 2129 { 2130 Name: "returns error on creating asa", 2131 LabelDefServiceFn: func() *automock.LabelDefService { 2132 return mockScenarioDefServiceThatReturns([]string{testFormationName}) 2133 }, 2134 AsaRepoFn: func() *automock.AutomaticFormationAssignmentRepository { 2135 mockRepo := &automock.AutomaticFormationAssignmentRepository{} 2136 mockRepo.On("Create", ctx, fixModel(testFormationName)).Return(fixError()).Once() 2137 return mockRepo 2138 }, 2139 AsaEngineFN: unusedASAEngine, 2140 InputASA: fixModel(testFormationName), 2141 ExpectedASA: model.AutomaticScenarioAssignment{}, 2142 ExpectedErrMessage: "while persisting Assignment", 2143 }, 2144 2145 {Name: "returns error on creating asa", 2146 LabelDefServiceFn: func() *automock.LabelDefService { 2147 return mockScenarioDefServiceThatReturns([]string{testFormationName}) 2148 }, 2149 AsaRepoFn: func() *automock.AutomaticFormationAssignmentRepository { 2150 mockRepo := &automock.AutomaticFormationAssignmentRepository{} 2151 mockRepo.On("Create", ctx, fixModel(testFormationName)).Return(apperrors.NewNotUniqueError(resource.AutomaticScenarioAssigment)).Once() 2152 return mockRepo 2153 }, 2154 AsaEngineFN: unusedASAEngine, 2155 InputASA: fixModel(testFormationName), 2156 ExpectedASA: model.AutomaticScenarioAssignment{}, 2157 ExpectedErrMessage: "a given scenario already has an assignment", 2158 }, 2159 { 2160 Name: "returns error on ensure scenario assigned", 2161 LabelDefServiceFn: func() *automock.LabelDefService { 2162 return mockScenarioDefServiceThatReturns([]string{testFormationName}) 2163 }, 2164 AsaRepoFn: func() *automock.AutomaticFormationAssignmentRepository { 2165 mockRepo := &automock.AutomaticFormationAssignmentRepository{} 2166 mockRepo.On("Create", ctx, fixModel(testFormationName)).Return(nil).Once() 2167 return mockRepo 2168 }, 2169 AsaEngineFN: func() *automock.AsaEngine { 2170 engine := &automock.AsaEngine{} 2171 engine.On("EnsureScenarioAssigned", ctx, fixModel(testFormationName), mock.Anything).Return(fixError()).Once() 2172 return engine 2173 }, 2174 InputASA: fixModel(testFormationName), 2175 ExpectedASA: model.AutomaticScenarioAssignment{}, 2176 ExpectedErrMessage: "while assigning scenario to runtimes matching selector", 2177 }, 2178 } 2179 for _, testCase := range testCases { 2180 t.Run(testCase.Name, func(t *testing.T) { 2181 // GIVEN 2182 asaRepo := testCase.AsaRepoFn() 2183 tenantSvc := &automock.TenantService{} 2184 labelDefService := testCase.LabelDefServiceFn() 2185 asaEngine := testCase.AsaEngineFN() 2186 2187 svc := formation.NewServiceWithAsaEngine(nil, nil, nil, nil, nil, nil, nil, nil, labelDefService, asaRepo, nil, tenantSvc, nil, nil, nil, nil, nil, nil, nil, runtimeType, applicationType, asaEngine, nil) 2188 2189 // WHEN 2190 actual, err := svc.CreateAutomaticScenarioAssignment(ctx, testCase.InputASA) 2191 2192 // THEN 2193 if testCase.ExpectedErrMessage == "" { 2194 require.NoError(t, err) 2195 assert.Equal(t, testCase.ExpectedASA, actual) 2196 } else { 2197 require.Error(t, err) 2198 require.Contains(t, err.Error(), testCase.ExpectedErrMessage) 2199 require.Equal(t, testCase.ExpectedASA, actual) 2200 } 2201 2202 mock.AssertExpectationsForObjects(t, tenantSvc, asaRepo, labelDefService, asaEngine) 2203 }) 2204 } 2205 2206 t.Run("returns error on missing tenant in context", func(t *testing.T) { 2207 // GIVEN 2208 svc := formation.NewService(nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, runtimeType, applicationType) 2209 2210 // WHEN 2211 _, err := svc.CreateAutomaticScenarioAssignment(context.TODO(), fixModel(ScenarioName)) 2212 2213 // THEN 2214 assert.EqualError(t, err, "cannot read tenant from context") 2215 }) 2216 } 2217 2218 func TestService_DeleteAutomaticScenarioAssignment(t *testing.T) { 2219 ctx := fixCtxWithTenant() 2220 2221 testErr := errors.New("test err") 2222 2223 testCases := []struct { 2224 Name string 2225 AsaRepoFn func() *automock.AutomaticFormationAssignmentRepository 2226 AsaEngineFN func() *automock.AsaEngine 2227 InputASA model.AutomaticScenarioAssignment 2228 ExpectedASA model.AutomaticScenarioAssignment 2229 ExpectedErrMessage string 2230 }{ 2231 { 2232 Name: "Success", 2233 AsaRepoFn: func() *automock.AutomaticFormationAssignmentRepository { 2234 mockRepo := &automock.AutomaticFormationAssignmentRepository{} 2235 mockRepo.On("DeleteForScenarioName", ctx, tenantID.String(), testFormationName).Return(nil).Once() 2236 return mockRepo 2237 }, 2238 AsaEngineFN: func() *automock.AsaEngine { 2239 engine := &automock.AsaEngine{} 2240 engine.Mock.On("RemoveAssignedScenario", ctx, fixModel(testFormationName), mock.Anything).Return(nil).Once() 2241 return engine 2242 }, 2243 InputASA: fixModel(testFormationName), 2244 ExpectedASA: fixModel(testFormationName), 2245 }, 2246 { 2247 Name: "Returns error when deleting ASA for scenario name fails", 2248 AsaRepoFn: func() *automock.AutomaticFormationAssignmentRepository { 2249 mockRepo := &automock.AutomaticFormationAssignmentRepository{} 2250 mockRepo.On("DeleteForScenarioName", ctx, tenantID.String(), testFormationName).Return(testErr).Once() 2251 return mockRepo 2252 }, 2253 AsaEngineFN: unusedASAEngine, 2254 InputASA: fixModel(testFormationName), 2255 ExpectedASA: model.AutomaticScenarioAssignment{}, 2256 ExpectedErrMessage: "while deleting the Assignment", 2257 }, 2258 { 2259 Name: "Returns error when removing assigned scenario", 2260 AsaRepoFn: func() *automock.AutomaticFormationAssignmentRepository { 2261 mockRepo := &automock.AutomaticFormationAssignmentRepository{} 2262 mockRepo.On("DeleteForScenarioName", ctx, tenantID.String(), testFormationName).Return(nil).Once() 2263 return mockRepo 2264 }, 2265 AsaEngineFN: func() *automock.AsaEngine { 2266 engine := &automock.AsaEngine{} 2267 engine.Mock.On("RemoveAssignedScenario", ctx, fixModel(testFormationName), mock.Anything).Return(testErr).Once() 2268 return engine 2269 }, 2270 InputASA: fixModel(testFormationName), 2271 ExpectedASA: model.AutomaticScenarioAssignment{}, 2272 ExpectedErrMessage: "while unassigning scenario from runtimes", 2273 }, 2274 } 2275 for _, testCase := range testCases { 2276 t.Run(testCase.Name, func(t *testing.T) { 2277 // GIVEN 2278 asaRepo := testCase.AsaRepoFn() 2279 asaEngine := testCase.AsaEngineFN() 2280 tenantSvc := &automock.TenantService{} 2281 2282 svc := formation.NewServiceWithAsaEngine(nil, nil, nil, nil, nil, nil, nil, nil, nil, asaRepo, nil, tenantSvc, nil, nil, nil, nil, nil, nil, nil, runtimeType, applicationType, asaEngine, nil) 2283 2284 // WHEN 2285 err := svc.DeleteAutomaticScenarioAssignment(ctx, testCase.InputASA) 2286 2287 // THEN 2288 if testCase.ExpectedErrMessage == "" { 2289 require.NoError(t, err) 2290 } else { 2291 require.Error(t, err) 2292 require.Contains(t, err.Error(), testCase.ExpectedErrMessage) 2293 } 2294 2295 mock.AssertExpectationsForObjects(t, tenantSvc, asaRepo, asaEngine) 2296 }) 2297 } 2298 2299 t.Run("returns error on missing tenant in context", func(t *testing.T) { 2300 // GIVEN 2301 svc := formation.NewService(nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, runtimeType, applicationType) 2302 2303 // WHEN 2304 err := svc.DeleteAutomaticScenarioAssignment(context.TODO(), fixModel(ScenarioName)) 2305 2306 // THEN 2307 assert.Contains(t, err.Error(), "cannot read tenant from context") 2308 }) 2309 } 2310 2311 func TestService_MergeScenariosFromInputLabelsAndAssignments(t *testing.T) { 2312 // GIVEN 2313 ctx := fixCtxWithTenant() 2314 2315 differentTargetTenant := "differentTargetTenant" 2316 runtimeID := "runtimeID" 2317 labelKey := "key" 2318 labelValue := "val" 2319 2320 scenario := "SCENARIO" 2321 2322 assignments := []*model.AutomaticScenarioAssignment{ 2323 { 2324 ScenarioName: ScenarioName, 2325 Tenant: tenantID.String(), 2326 TargetTenantID: TargetTenantID, 2327 }, 2328 { 2329 ScenarioName: ScenarioName2, 2330 Tenant: tenantID.String(), 2331 TargetTenantID: differentTargetTenant, 2332 }, 2333 } 2334 2335 formations := []*model.Formation{ 2336 { 2337 ID: FormationID, 2338 TenantID: tenantID.String(), 2339 FormationTemplateID: FormationTemplateID, 2340 Name: ScenarioName, 2341 }, 2342 { 2343 ID: FormationID, 2344 TenantID: tenantID.String(), 2345 FormationTemplateID: FormationTemplateID, 2346 Name: ScenarioName2, 2347 }, 2348 } 2349 2350 testCases := []struct { 2351 Name string 2352 AsaRepoFn func() *automock.AutomaticFormationAssignmentRepository 2353 RuntimeContextRepoFn func() *automock.RuntimeContextRepository 2354 RuntimeRepoFn func() *automock.RuntimeRepository 2355 FormationRepoFn func() *automock.FormationRepository 2356 FormationTemplateRepoFn func() *automock.FormationTemplateRepository 2357 InputLabels map[string]interface{} 2358 ExpectedScenarios []interface{} 2359 ExpectedErrMessage string 2360 }{ 2361 { 2362 Name: "Success", 2363 AsaRepoFn: func() *automock.AutomaticFormationAssignmentRepository { 2364 asaRepo := &automock.AutomaticFormationAssignmentRepository{} 2365 asaRepo.On("ListAll", ctx, tenantID.String()).Return(assignments, nil) 2366 return asaRepo 2367 }, 2368 RuntimeContextRepoFn: func() *automock.RuntimeContextRepository { 2369 runtimeContextRepo := &automock.RuntimeContextRepository{} 2370 runtimeContextRepo.On("ExistsByRuntimeID", ctx, TargetTenantID, runtimeID).Return(false, nil).Once() 2371 return runtimeContextRepo 2372 }, 2373 RuntimeRepoFn: func() *automock.RuntimeRepository { 2374 runtimeRepo := &automock.RuntimeRepository{} 2375 runtimeRepo.On("OwnerExistsByFiltersAndID", ctx, TargetTenantID, runtimeID, runtimeLblFilters).Return(true, nil).Once() 2376 runtimeRepo.On("OwnerExistsByFiltersAndID", ctx, differentTargetTenant, runtimeID, runtimeLblFilters).Return(false, nil).Once() 2377 return runtimeRepo 2378 }, 2379 FormationRepoFn: func() *automock.FormationRepository { 2380 formationRepo := &automock.FormationRepository{} 2381 formationRepo.On("GetByName", ctx, ScenarioName, tenantID.String()).Return(formations[0], nil).Once() 2382 formationRepo.On("GetByName", ctx, ScenarioName2, tenantID.String()).Return(formations[1], nil).Once() 2383 return formationRepo 2384 }, 2385 FormationTemplateRepoFn: func() *automock.FormationTemplateRepository { 2386 formationTemplateRepo := &automock.FormationTemplateRepository{} 2387 formationTemplateRepo.On("Get", ctx, formations[0].FormationTemplateID).Return(&formationTemplate, nil).Once() 2388 formationTemplateRepo.On("Get", ctx, formations[1].FormationTemplateID).Return(&formationTemplate, nil).Once() 2389 return formationTemplateRepo 2390 }, 2391 InputLabels: map[string]interface{}{ 2392 labelKey: labelValue, 2393 }, 2394 ExpectedScenarios: []interface{}{ScenarioName}, 2395 }, 2396 { 2397 Name: "Success if scenarios label is in input", 2398 AsaRepoFn: func() *automock.AutomaticFormationAssignmentRepository { 2399 asaRepo := &automock.AutomaticFormationAssignmentRepository{} 2400 asaRepo.On("ListAll", ctx, tenantID.String()).Return(assignments, nil) 2401 return asaRepo 2402 }, 2403 RuntimeContextRepoFn: func() *automock.RuntimeContextRepository { 2404 runtimeContextRepo := &automock.RuntimeContextRepository{} 2405 runtimeContextRepo.On("ExistsByRuntimeID", ctx, TargetTenantID, runtimeID).Return(false, nil).Once() 2406 return runtimeContextRepo 2407 }, 2408 RuntimeRepoFn: func() *automock.RuntimeRepository { 2409 runtimeRepo := &automock.RuntimeRepository{} 2410 runtimeRepo.On("OwnerExistsByFiltersAndID", ctx, TargetTenantID, runtimeID, runtimeLblFilters).Return(true, nil).Once() 2411 runtimeRepo.On("OwnerExistsByFiltersAndID", ctx, differentTargetTenant, runtimeID, runtimeLblFilters).Return(false, nil).Once() 2412 return runtimeRepo 2413 }, 2414 FormationRepoFn: func() *automock.FormationRepository { 2415 formationRepo := &automock.FormationRepository{} 2416 formationRepo.On("GetByName", ctx, ScenarioName, tenantID.String()).Return(formations[0], nil).Once() 2417 formationRepo.On("GetByName", ctx, ScenarioName2, tenantID.String()).Return(formations[1], nil).Once() 2418 return formationRepo 2419 }, 2420 FormationTemplateRepoFn: func() *automock.FormationTemplateRepository { 2421 formationTemplateRepo := &automock.FormationTemplateRepository{} 2422 formationTemplateRepo.On("Get", ctx, formations[0].FormationTemplateID).Return(&formationTemplate, nil).Once() 2423 formationTemplateRepo.On("Get", ctx, formations[1].FormationTemplateID).Return(&formationTemplate, nil).Once() 2424 return formationTemplateRepo 2425 }, 2426 InputLabels: map[string]interface{}{ 2427 labelKey: labelValue, 2428 model.ScenariosKey: []interface{}{scenario}, 2429 }, 2430 ExpectedScenarios: []interface{}{ScenarioName, scenario}, 2431 }, 2432 { 2433 Name: "Returns error when checking if ASA is matching to runtime fails", 2434 AsaRepoFn: func() *automock.AutomaticFormationAssignmentRepository { 2435 asaRepo := &automock.AutomaticFormationAssignmentRepository{} 2436 asaRepo.On("ListAll", ctx, tenantID.String()).Return(assignments, nil) 2437 return asaRepo 2438 }, 2439 RuntimeContextRepoFn: unusedRuntimeContextRepo, 2440 RuntimeRepoFn: func() *automock.RuntimeRepository { 2441 runtimeRepo := &automock.RuntimeRepository{} 2442 runtimeRepo.On("OwnerExistsByFiltersAndID", ctx, TargetTenantID, runtimeID, runtimeLblFilters).Return(false, testErr).Once() 2443 return runtimeRepo 2444 }, 2445 FormationRepoFn: func() *automock.FormationRepository { 2446 formationRepo := &automock.FormationRepository{} 2447 formationRepo.On("GetByName", ctx, ScenarioName, tenantID.String()).Return(formations[0], nil).Once() 2448 return formationRepo 2449 }, 2450 FormationTemplateRepoFn: func() *automock.FormationTemplateRepository { 2451 formationTemplateRepo := &automock.FormationTemplateRepository{} 2452 formationTemplateRepo.On("Get", ctx, formations[0].FormationTemplateID).Return(&formationTemplate, nil).Once() 2453 return formationTemplateRepo 2454 }, 2455 InputLabels: map[string]interface{}{ 2456 labelKey: labelValue, 2457 }, 2458 ExpectedScenarios: []interface{}{}, 2459 ExpectedErrMessage: testErr.Error(), 2460 }, 2461 { 2462 Name: "Returns error when scenarios from input are not interface slice", 2463 AsaRepoFn: func() *automock.AutomaticFormationAssignmentRepository { 2464 asaRepo := &automock.AutomaticFormationAssignmentRepository{} 2465 asaRepo.On("ListAll", ctx, tenantID.String()).Return(assignments, nil) 2466 return asaRepo 2467 }, 2468 RuntimeContextRepoFn: func() *automock.RuntimeContextRepository { 2469 runtimeContextRepo := &automock.RuntimeContextRepository{} 2470 runtimeContextRepo.On("ExistsByRuntimeID", ctx, TargetTenantID, runtimeID).Return(false, nil).Once() 2471 return runtimeContextRepo 2472 }, 2473 RuntimeRepoFn: func() *automock.RuntimeRepository { 2474 runtimeRepo := &automock.RuntimeRepository{} 2475 runtimeRepo.On("OwnerExistsByFiltersAndID", ctx, TargetTenantID, runtimeID, runtimeLblFilters).Return(true, nil).Once() 2476 runtimeRepo.On("OwnerExistsByFiltersAndID", ctx, differentTargetTenant, runtimeID, runtimeLblFilters).Return(false, nil).Once() 2477 return runtimeRepo 2478 }, 2479 FormationRepoFn: func() *automock.FormationRepository { 2480 formationRepo := &automock.FormationRepository{} 2481 formationRepo.On("GetByName", ctx, ScenarioName, tenantID.String()).Return(formations[0], nil).Once() 2482 formationRepo.On("GetByName", ctx, ScenarioName2, tenantID.String()).Return(formations[1], nil).Once() 2483 return formationRepo 2484 }, 2485 FormationTemplateRepoFn: func() *automock.FormationTemplateRepository { 2486 formationTemplateRepo := &automock.FormationTemplateRepository{} 2487 formationTemplateRepo.On("Get", ctx, formations[0].FormationTemplateID).Return(&formationTemplate, nil).Once() 2488 formationTemplateRepo.On("Get", ctx, formations[1].FormationTemplateID).Return(&formationTemplate, nil).Once() 2489 return formationTemplateRepo 2490 }, 2491 InputLabels: map[string]interface{}{ 2492 labelKey: labelValue, 2493 model.ScenariosKey: []string{scenario}, 2494 }, 2495 ExpectedScenarios: []interface{}{}, 2496 ExpectedErrMessage: "while converting scenarios label", 2497 }, 2498 } 2499 2500 for _, testCase := range testCases { 2501 t.Run(testCase.Name, func(t *testing.T) { 2502 // GIVEN 2503 asaRepo := testCase.AsaRepoFn() 2504 runtimeRepo := testCase.RuntimeRepoFn() 2505 runtimeContextRepo := testCase.RuntimeContextRepoFn() 2506 formationRepo := testCase.FormationRepoFn() 2507 formationTemplateRepo := testCase.FormationTemplateRepoFn() 2508 2509 svc := formation.NewService(nil, nil, nil, nil, formationRepo, formationTemplateRepo, nil, nil, nil, asaRepo, nil, nil, runtimeRepo, runtimeContextRepo, nil, nil, nil, nil, nil, nil, runtimeType, applicationType) 2510 2511 // WHEN 2512 actualScenarios, err := svc.MergeScenariosFromInputLabelsAndAssignments(ctx, testCase.InputLabels, runtimeID) 2513 2514 // THEN 2515 if testCase.ExpectedErrMessage == "" { 2516 require.NoError(t, err) 2517 require.ElementsMatch(t, testCase.ExpectedScenarios, actualScenarios) 2518 } else { 2519 require.Error(t, err) 2520 require.Contains(t, err.Error(), testCase.ExpectedErrMessage) 2521 } 2522 2523 mock.AssertExpectationsForObjects(t, asaRepo, runtimeRepo, formationTemplateRepo, formationRepo) 2524 }) 2525 } 2526 } 2527 2528 func TestService_GetFormationsForObject(t *testing.T) { 2529 id := "rtmID" 2530 testErr := "testErr" 2531 2532 scenarios := []interface{}{"scenario1", "scenario2"} 2533 2534 labelInput := &model.LabelInput{ 2535 Key: model.ScenariosKey, 2536 ObjectID: id, 2537 ObjectType: model.RuntimeLabelableObject, 2538 } 2539 2540 label := &model.Label{ 2541 ID: id, 2542 Key: "scenarios", 2543 Value: scenarios, 2544 ObjectID: id, 2545 ObjectType: model.RuntimeLabelableObject, 2546 } 2547 2548 t.Run("Success", func(t *testing.T) { 2549 // GIVEN 2550 ctx := context.TODO() 2551 2552 labelService := &automock.LabelService{} 2553 labelService.On("GetLabel", ctx, tenantID.String(), labelInput).Return(label, nil).Once() 2554 2555 svc := formation.NewService(nil, nil, nil, nil, nil, nil, labelService, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, runtimeType, applicationType) 2556 2557 // WHEN 2558 formations, err := svc.GetFormationsForObject(ctx, tenantID.String(), model.RuntimeLabelableObject, id) 2559 2560 // THEN 2561 require.NoError(t, err) 2562 require.ElementsMatch(t, formations, scenarios) 2563 mock.AssertExpectationsForObjects(t, labelService) 2564 }) 2565 2566 t.Run("Returns error while getting label", func(t *testing.T) { 2567 // GIVEN 2568 ctx := context.TODO() 2569 2570 labelService := &automock.LabelService{} 2571 labelService.On("GetLabel", ctx, tenantID.String(), labelInput).Return(nil, errors.New(testErr)).Once() 2572 2573 svc := formation.NewService(nil, nil, nil, nil, nil, nil, labelService, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, runtimeType, applicationType) 2574 2575 // WHEN 2576 formations, err := svc.GetFormationsForObject(ctx, tenantID.String(), model.RuntimeLabelableObject, id) 2577 2578 // THEN 2579 require.Error(t, err) 2580 require.Contains(t, err.Error(), "while fetching scenario label for") 2581 require.Nil(t, formations) 2582 mock.AssertExpectationsForObjects(t, labelService) 2583 }) 2584 } 2585 2586 func TestServiceResynchronizeFormationNotifications(t *testing.T) { 2587 ctx := context.TODO() 2588 ctx = tenant.SaveToContext(ctx, TntInternalID, TntExternalID) 2589 2590 allStates := []string{string(model.InitialAssignmentState), 2591 string(model.DeletingAssignmentState), 2592 string(model.CreateErrorAssignmentState), 2593 string(model.DeleteErrorAssignmentState)} 2594 2595 testFormation := fixFormationModelWithState(model.ReadyFormationState) 2596 formationInCreateErrorState := fixFormationModelWithStateAndAssignmentError(t, model.CreateErrorFormationState, testErr.Error(), formationassignment.ClientError) 2597 formationInCreateErrorStateTechnicalError := fixFormationModelWithStateAndAssignmentError(t, model.CreateErrorFormationState, testErr.Error(), formationassignment.TechnicalError) 2598 2599 formationAssignments := []*model.FormationAssignment{ 2600 fixFormationAssignmentModelWithParameters("id1", FormationID, RuntimeID, ApplicationID, model.FormationAssignmentTypeRuntime, model.FormationAssignmentTypeApplication, model.InitialFormationState), 2601 fixFormationAssignmentModelWithParameters("id2", FormationID, RuntimeContextID, ApplicationID, model.FormationAssignmentTypeRuntimeContext, model.FormationAssignmentTypeApplication, model.CreateErrorFormationState), 2602 fixFormationAssignmentModelWithParameters("id3", FormationID, RuntimeID, RuntimeContextID, model.FormationAssignmentTypeRuntime, model.FormationAssignmentTypeRuntimeContext, model.DeletingFormationState), 2603 fixFormationAssignmentModelWithParameters("id4", FormationID, RuntimeContextID, RuntimeContextID, model.FormationAssignmentTypeRuntimeContext, model.FormationAssignmentTypeRuntimeContext, model.DeleteErrorFormationState), 2604 } 2605 reverseAssignment := &model.FormationAssignment{ 2606 ID: "id1", 2607 FormationID: FormationID, 2608 Source: ApplicationID, 2609 SourceType: model.FormationAssignmentTypeApplication, 2610 Target: RuntimeID, 2611 TargetType: model.FormationAssignmentTypeRuntime, 2612 State: string(model.ReadyAssignmentState), 2613 } 2614 2615 notificationsForAssignments := []*webhookclient.FormationAssignmentNotificationRequest{ 2616 { 2617 Webhook: graphql.Webhook{ 2618 ID: WebhookID, 2619 }, 2620 }, 2621 { 2622 Webhook: graphql.Webhook{ 2623 ID: Webhook2ID, 2624 }, 2625 }, 2626 { 2627 Webhook: graphql.Webhook{ 2628 ID: Webhook3ID, 2629 }, 2630 }, 2631 { 2632 Webhook: graphql.Webhook{ 2633 ID: Webhook4ID, 2634 }, 2635 }, 2636 } 2637 var formationAssignmentPairs = make([]*formationassignment.AssignmentMappingPairWithOperation, 0, len(formationAssignments)) 2638 for i := range formationAssignments { 2639 formationAssignmentPairs = append(formationAssignmentPairs, fixFormationAssignmentPairWithNoReverseAssignment(notificationsForAssignments[i], formationAssignments[i])) 2640 } 2641 2642 testSchema, err := labeldef.NewSchemaForFormations([]string{testScenario, testFormationName}) 2643 assert.NoError(t, err) 2644 testSchemaLblDef := fixScenariosLabelDefinition(TntInternalID, testSchema) 2645 2646 newSchema, err := labeldef.NewSchemaForFormations([]string{testScenario}) 2647 assert.NoError(t, err) 2648 newSchemaLblDef := fixScenariosLabelDefinition(TntInternalID, newSchema) 2649 2650 nilSchemaLblDef := fixScenariosLabelDefinition(TntInternalID, testSchema) 2651 nilSchemaLblDef.Schema = nil 2652 2653 testCases := []struct { 2654 Name string 2655 LabelServiceFn func() *automock.LabelService 2656 LabelRepoFn func() *automock.LabelRepository 2657 AsaEngineFN func() *automock.AsaEngine 2658 FormationRepositoryFn func() *automock.FormationRepository 2659 FormationTemplateRepositoryFn func() *automock.FormationTemplateRepository 2660 FormationAssignmentNotificationServiceFN func() *automock.FormationAssignmentNotificationsService 2661 NotificationServiceFN func() *automock.NotificationsService 2662 FormationAssignmentServiceFn func() *automock.FormationAssignmentService 2663 WebhookRepoFn func() *automock.WebhookRepository 2664 RuntimeContextRepoFn func() *automock.RuntimeContextRepository 2665 LabelDefRepositoryFn func() *automock.LabelDefRepository 2666 LabelDefServiceFn func() *automock.LabelDefService 2667 StatusServiceFn func() *automock.StatusService 2668 ExpectedErrMessage string 2669 }{ 2670 // Business logic tests for tenant mapping notifications only 2671 { 2672 Name: "success when resynchronization is successful and there are leftover formation assignments", 2673 FormationAssignmentServiceFn: func() *automock.FormationAssignmentService { 2674 svc := &automock.FormationAssignmentService{} 2675 svc.On("GetAssignmentsForFormationWithStates", ctx, TntInternalID, FormationID, allStates).Return(formationAssignments, nil).Once() 2676 2677 for _, fa := range formationAssignments { 2678 svc.On("GetReverseBySourceAndTarget", ctx, FormationID, fa.Source, fa.Target).Return(nil, apperrors.NewNotFoundError(resource.FormationAssignment, "")).Once() 2679 } 2680 2681 svc.On("ProcessFormationAssignmentPair", ctx, formationAssignmentPairs[0]).Return(false, nil).Once() 2682 svc.On("ProcessFormationAssignmentPair", ctx, formationAssignmentPairs[1]).Return(false, nil).Once() 2683 svc.On("CleanupFormationAssignment", ctx, formationAssignmentPairs[2]).Return(false, nil).Once() 2684 svc.On("CleanupFormationAssignment", ctx, formationAssignmentPairs[3]).Return(false, nil).Once() 2685 2686 svc.On("ListFormationAssignmentsForObjectID", ctx, FormationID, formationAssignments[3].Source).Return([]*model.FormationAssignment{{ID: "id6"}}, nil).Once() 2687 return svc 2688 }, 2689 FormationAssignmentNotificationServiceFN: func() *automock.FormationAssignmentNotificationsService { 2690 svc := &automock.FormationAssignmentNotificationsService{} 2691 svc.On("GenerateFormationAssignmentNotification", ctx, formationAssignments[0], model.AssignFormation).Return(notificationsForAssignments[0], nil).Once() 2692 svc.On("GenerateFormationAssignmentNotification", ctx, formationAssignments[1], model.AssignFormation).Return(notificationsForAssignments[1], nil).Once() 2693 svc.On("GenerateFormationAssignmentNotification", ctx, formationAssignments[2], model.UnassignFormation).Return(notificationsForAssignments[2], nil).Once() 2694 svc.On("GenerateFormationAssignmentNotification", ctx, formationAssignments[3], model.UnassignFormation).Return(notificationsForAssignments[3], nil).Once() 2695 return svc 2696 }, 2697 FormationRepositoryFn: func() *automock.FormationRepository { 2698 repo := &automock.FormationRepository{} 2699 repo.On("Get", ctx, FormationID, TntInternalID).Return(testFormation, nil).Once() 2700 return repo 2701 }, 2702 }, 2703 { 2704 Name: "success when resynchronization is successful and there no left formation assignments should unassign", 2705 FormationAssignmentServiceFn: func() *automock.FormationAssignmentService { 2706 svc := &automock.FormationAssignmentService{} 2707 svc.On("GetAssignmentsForFormationWithStates", ctx, TntInternalID, FormationID, allStates).Return(formationAssignments, nil).Once() 2708 2709 for _, fa := range formationAssignments { 2710 svc.On("GetReverseBySourceAndTarget", ctx, FormationID, fa.Source, fa.Target).Return(nil, apperrors.NewNotFoundError(resource.FormationAssignment, "")).Once() 2711 } 2712 2713 svc.On("ProcessFormationAssignmentPair", ctx, formationAssignmentPairs[0]).Return(false, nil).Once() 2714 svc.On("ProcessFormationAssignmentPair", ctx, formationAssignmentPairs[1]).Return(false, nil).Once() 2715 svc.On("CleanupFormationAssignment", ctx, formationAssignmentPairs[2]).Return(false, nil).Once() 2716 svc.On("CleanupFormationAssignment", ctx, formationAssignmentPairs[3]).Return(false, nil).Once() 2717 2718 svc.On("ListFormationAssignmentsForObjectID", ctx, FormationID, formationAssignments[3].Source).Return([]*model.FormationAssignment{{ID: "id6"}}, nil).Once() 2719 return svc 2720 }, 2721 FormationAssignmentNotificationServiceFN: func() *automock.FormationAssignmentNotificationsService { 2722 svc := &automock.FormationAssignmentNotificationsService{} 2723 svc.On("GenerateFormationAssignmentNotification", ctx, formationAssignments[0], model.AssignFormation).Return(notificationsForAssignments[0], nil).Once() 2724 svc.On("GenerateFormationAssignmentNotification", ctx, formationAssignments[1], model.AssignFormation).Return(notificationsForAssignments[1], nil).Once() 2725 svc.On("GenerateFormationAssignmentNotification", ctx, formationAssignments[2], model.UnassignFormation).Return(notificationsForAssignments[2], nil).Once() 2726 svc.On("GenerateFormationAssignmentNotification", ctx, formationAssignments[3], model.UnassignFormation).Return(notificationsForAssignments[3], nil).Once() 2727 return svc 2728 }, 2729 FormationRepositoryFn: func() *automock.FormationRepository { 2730 repo := &automock.FormationRepository{} 2731 repo.On("Get", ctx, FormationID, TntInternalID).Return(testFormation, nil).Once() 2732 return repo 2733 }, 2734 }, 2735 { 2736 Name: "returns error when failing to unassign from formation after resynchronizing", 2737 FormationAssignmentServiceFn: func() *automock.FormationAssignmentService { 2738 svc := &automock.FormationAssignmentService{} 2739 svc.On("GetAssignmentsForFormationWithStates", ctx, TntInternalID, FormationID, allStates).Return(formationAssignments, nil).Once() 2740 2741 for _, fa := range formationAssignments { 2742 svc.On("GetReverseBySourceAndTarget", ctx, FormationID, fa.Source, fa.Target).Return(nil, apperrors.NewNotFoundError(resource.FormationAssignment, "")).Once() 2743 } 2744 2745 svc.On("ProcessFormationAssignmentPair", ctx, formationAssignmentPairs[0]).Return(false, nil).Once() 2746 svc.On("ProcessFormationAssignmentPair", ctx, formationAssignmentPairs[1]).Return(false, nil).Once() 2747 svc.On("CleanupFormationAssignment", ctx, formationAssignmentPairs[2]).Return(false, nil).Once() 2748 svc.On("CleanupFormationAssignment", ctx, formationAssignmentPairs[3]).Return(false, nil).Once() 2749 2750 svc.On("ListFormationAssignmentsForObjectID", ctx, FormationID, formationAssignments[3].Target).Return([]*model.FormationAssignment{}, nil).Once() 2751 2752 return svc 2753 }, 2754 FormationAssignmentNotificationServiceFN: func() *automock.FormationAssignmentNotificationsService { 2755 svc := &automock.FormationAssignmentNotificationsService{} 2756 svc.On("GenerateFormationAssignmentNotification", ctx, formationAssignments[0], model.AssignFormation).Return(notificationsForAssignments[0], nil).Once() 2757 svc.On("GenerateFormationAssignmentNotification", ctx, formationAssignments[1], model.AssignFormation).Return(notificationsForAssignments[1], nil).Once() 2758 svc.On("GenerateFormationAssignmentNotification", ctx, formationAssignments[2], model.UnassignFormation).Return(notificationsForAssignments[2], nil).Once() 2759 svc.On("GenerateFormationAssignmentNotification", ctx, formationAssignments[3], model.UnassignFormation).Return(notificationsForAssignments[3], nil).Once() 2760 return svc 2761 }, 2762 FormationRepositoryFn: func() *automock.FormationRepository { 2763 repo := &automock.FormationRepository{} 2764 repo.On("Get", ctx, FormationID, TntInternalID).Return(testFormation, nil).Once() 2765 return repo 2766 }, 2767 AsaEngineFN: func() *automock.AsaEngine { 2768 engine := &automock.AsaEngine{} 2769 engine.On("IsFormationComingFromASA", ctx, RuntimeContextID, testFormation.Name, graphql.FormationObjectTypeRuntimeContext).Return(false, testErr).Once() 2770 return engine 2771 }, 2772 ExpectedErrMessage: testErr.Error(), 2773 }, 2774 { 2775 Name: "returns error when failing to list formation assignments for participant", 2776 FormationAssignmentServiceFn: func() *automock.FormationAssignmentService { 2777 svc := &automock.FormationAssignmentService{} 2778 svc.On("GetAssignmentsForFormationWithStates", ctx, TntInternalID, FormationID, allStates).Return(formationAssignments, nil).Once() 2779 2780 for _, fa := range formationAssignments { 2781 svc.On("GetReverseBySourceAndTarget", ctx, FormationID, fa.Source, fa.Target).Return(nil, apperrors.NewNotFoundError(resource.FormationAssignment, "")).Once() 2782 } 2783 2784 svc.On("ProcessFormationAssignmentPair", ctx, formationAssignmentPairs[0]).Return(false, nil).Once() 2785 svc.On("ProcessFormationAssignmentPair", ctx, formationAssignmentPairs[1]).Return(false, nil).Once() 2786 svc.On("CleanupFormationAssignment", ctx, formationAssignmentPairs[2]).Return(false, nil).Once() 2787 svc.On("CleanupFormationAssignment", ctx, formationAssignmentPairs[3]).Return(false, nil).Once() 2788 2789 svc.On("ListFormationAssignmentsForObjectID", ctx, FormationID, mock.Anything).Return(nil, testErr).Once() 2790 return svc 2791 }, 2792 FormationAssignmentNotificationServiceFN: func() *automock.FormationAssignmentNotificationsService { 2793 svc := &automock.FormationAssignmentNotificationsService{} 2794 svc.On("GenerateFormationAssignmentNotification", ctx, formationAssignments[0], model.AssignFormation).Return(notificationsForAssignments[0], nil).Once() 2795 svc.On("GenerateFormationAssignmentNotification", ctx, formationAssignments[1], model.AssignFormation).Return(notificationsForAssignments[1], nil).Once() 2796 svc.On("GenerateFormationAssignmentNotification", ctx, formationAssignments[2], model.UnassignFormation).Return(notificationsForAssignments[2], nil).Once() 2797 svc.On("GenerateFormationAssignmentNotification", ctx, formationAssignments[3], model.UnassignFormation).Return(notificationsForAssignments[3], nil).Once() 2798 return svc 2799 }, 2800 FormationRepositoryFn: func() *automock.FormationRepository { 2801 repo := &automock.FormationRepository{} 2802 repo.On("Get", ctx, FormationID, TntInternalID).Return(testFormation, nil).Once() 2803 return repo 2804 }, 2805 ExpectedErrMessage: testErr.Error(), 2806 }, 2807 { 2808 Name: "returns error when failing to get formation", 2809 FormationRepositoryFn: func() *automock.FormationRepository { 2810 repo := &automock.FormationRepository{} 2811 repo.On("Get", ctx, FormationID, TntInternalID).Return(nil, testErr).Once() 2812 return repo 2813 }, 2814 ExpectedErrMessage: testErr.Error(), 2815 }, 2816 { 2817 Name: "returns error when failing processing formation assignments fails", 2818 FormationAssignmentServiceFn: func() *automock.FormationAssignmentService { 2819 svc := &automock.FormationAssignmentService{} 2820 svc.On("GetAssignmentsForFormationWithStates", ctx, TntInternalID, FormationID, allStates).Return(formationAssignments, nil).Once() 2821 2822 for _, fa := range formationAssignments { 2823 svc.On("GetReverseBySourceAndTarget", ctx, FormationID, fa.Source, fa.Target).Return(nil, apperrors.NewNotFoundError(resource.FormationAssignment, "")).Once() 2824 } 2825 2826 svc.On("ProcessFormationAssignmentPair", ctx, formationAssignmentPairs[0]).Return(false, testErr).Once() 2827 svc.On("ProcessFormationAssignmentPair", ctx, formationAssignmentPairs[1]).Return(false, testErr).Once() 2828 svc.On("CleanupFormationAssignment", ctx, formationAssignmentPairs[2]).Return(false, testErr).Once() 2829 svc.On("CleanupFormationAssignment", ctx, formationAssignmentPairs[3]).Return(false, testErr).Once() 2830 2831 svc.On("ListFormationAssignmentsForObjectID", ctx, FormationID, formationAssignments[3].Source).Return([]*model.FormationAssignment{{ID: "id6"}}, nil).Once() 2832 return svc 2833 }, 2834 FormationAssignmentNotificationServiceFN: func() *automock.FormationAssignmentNotificationsService { 2835 svc := &automock.FormationAssignmentNotificationsService{} 2836 svc.On("GenerateFormationAssignmentNotification", ctx, formationAssignments[0], model.AssignFormation).Return(notificationsForAssignments[0], nil).Once() 2837 svc.On("GenerateFormationAssignmentNotification", ctx, formationAssignments[1], model.AssignFormation).Return(notificationsForAssignments[1], nil).Once() 2838 svc.On("GenerateFormationAssignmentNotification", ctx, formationAssignments[2], model.UnassignFormation).Return(notificationsForAssignments[2], nil).Once() 2839 svc.On("GenerateFormationAssignmentNotification", ctx, formationAssignments[3], model.UnassignFormation).Return(notificationsForAssignments[3], nil).Once() 2840 return svc 2841 }, 2842 FormationRepositoryFn: func() *automock.FormationRepository { 2843 repo := &automock.FormationRepository{} 2844 repo.On("Get", ctx, FormationID, TntInternalID).Return(testFormation, nil).Once() 2845 return repo 2846 }, 2847 ExpectedErrMessage: testErr.Error(), 2848 }, 2849 { 2850 Name: "returns error when getting reverse formation assignment", 2851 FormationAssignmentServiceFn: func() *automock.FormationAssignmentService { 2852 svc := &automock.FormationAssignmentService{} 2853 svc.On("GetAssignmentsForFormationWithStates", ctx, TntInternalID, FormationID, allStates).Return([]*model.FormationAssignment{formationAssignments[0]}, nil).Once() 2854 2855 svc.On("GetReverseBySourceAndTarget", ctx, FormationID, formationAssignments[0].Source, formationAssignments[0].Target).Return(nil, testErr).Once() 2856 2857 return svc 2858 }, 2859 FormationAssignmentNotificationServiceFN: func() *automock.FormationAssignmentNotificationsService { 2860 svc := &automock.FormationAssignmentNotificationsService{} 2861 svc.On("GenerateFormationAssignmentNotification", ctx, formationAssignments[0], model.AssignFormation).Return(notificationsForAssignments[0], nil).Once() 2862 return svc 2863 }, 2864 FormationRepositoryFn: func() *automock.FormationRepository { 2865 repo := &automock.FormationRepository{} 2866 repo.On("Get", ctx, FormationID, TntInternalID).Return(testFormation, nil).Once() 2867 return repo 2868 }, 2869 ExpectedErrMessage: testErr.Error(), 2870 }, 2871 { 2872 Name: "returns error when generating reverse formation assignment notification", 2873 FormationAssignmentServiceFn: func() *automock.FormationAssignmentService { 2874 svc := &automock.FormationAssignmentService{} 2875 svc.On("GetAssignmentsForFormationWithStates", ctx, TntInternalID, FormationID, allStates).Return([]*model.FormationAssignment{formationAssignments[0]}, nil) 2876 2877 svc.On("GetReverseBySourceAndTarget", ctx, FormationID, formationAssignments[0].Source, formationAssignments[0].Target).Return(reverseAssignment, nil) 2878 2879 return svc 2880 }, 2881 FormationAssignmentNotificationServiceFN: func() *automock.FormationAssignmentNotificationsService { 2882 svc := &automock.FormationAssignmentNotificationsService{} 2883 svc.On("GenerateFormationAssignmentNotification", ctx, formationAssignments[0], model.AssignFormation).Return(notificationsForAssignments[0], nil) 2884 svc.On("GenerateFormationAssignmentNotification", ctx, reverseAssignment, model.AssignFormation).Return(nil, testErr) 2885 return svc 2886 }, 2887 FormationRepositoryFn: func() *automock.FormationRepository { 2888 repo := &automock.FormationRepository{} 2889 repo.On("Get", ctx, FormationID, TntInternalID).Return(testFormation, nil).Once() 2890 return repo 2891 }, 2892 ExpectedErrMessage: testErr.Error(), 2893 }, 2894 { 2895 Name: "returns error when generating formation assignment notification", 2896 FormationAssignmentServiceFn: func() *automock.FormationAssignmentService { 2897 svc := &automock.FormationAssignmentService{} 2898 svc.On("GetAssignmentsForFormationWithStates", ctx, TntInternalID, FormationID, allStates).Return([]*model.FormationAssignment{formationAssignments[0]}, nil).Once() 2899 2900 return svc 2901 }, 2902 FormationAssignmentNotificationServiceFN: func() *automock.FormationAssignmentNotificationsService { 2903 svc := &automock.FormationAssignmentNotificationsService{} 2904 svc.On("GenerateFormationAssignmentNotification", ctx, formationAssignments[0], model.AssignFormation).Return(nil, testErr).Once() 2905 return svc 2906 }, 2907 FormationRepositoryFn: func() *automock.FormationRepository { 2908 repo := &automock.FormationRepository{} 2909 repo.On("Get", ctx, FormationID, TntInternalID).Return(testFormation, nil).Once() 2910 return repo 2911 }, 2912 ExpectedErrMessage: testErr.Error(), 2913 }, 2914 { 2915 Name: "returns error when listing formation assignments with states", 2916 FormationAssignmentServiceFn: func() *automock.FormationAssignmentService { 2917 svc := &automock.FormationAssignmentService{} 2918 svc.On("GetAssignmentsForFormationWithStates", ctx, TntInternalID, FormationID, allStates).Return(nil, testErr).Once() 2919 2920 return svc 2921 }, 2922 FormationRepositoryFn: func() *automock.FormationRepository { 2923 repo := &automock.FormationRepository{} 2924 repo.On("Get", ctx, FormationID, TntInternalID).Return(testFormation, nil).Once() 2925 return repo 2926 }, 2927 ExpectedErrMessage: testErr.Error(), 2928 }, 2929 // Business logic tests for formation and tenant mapping notifications 2930 { 2931 Name: "success when both formation and formation assignment resynchronization are successful and there no left formation assignments should unassign", 2932 FormationAssignmentServiceFn: func() *automock.FormationAssignmentService { 2933 svc := &automock.FormationAssignmentService{} 2934 svc.On("GetAssignmentsForFormationWithStates", ctx, TntInternalID, FormationID, allStates).Return(formationAssignments, nil).Once() 2935 2936 for _, fa := range formationAssignments { 2937 svc.On("GetReverseBySourceAndTarget", ctx, FormationID, fa.Source, fa.Target).Return(nil, apperrors.NewNotFoundError(resource.FormationAssignment, "")).Once() 2938 } 2939 2940 svc.On("ProcessFormationAssignmentPair", ctx, formationAssignmentPairs[0]).Return(false, nil).Once() 2941 svc.On("ProcessFormationAssignmentPair", ctx, formationAssignmentPairs[1]).Return(false, nil).Once() 2942 svc.On("CleanupFormationAssignment", ctx, formationAssignmentPairs[2]).Return(false, nil).Once() 2943 svc.On("CleanupFormationAssignment", ctx, formationAssignmentPairs[3]).Return(false, nil).Once() 2944 2945 svc.On("ListFormationAssignmentsForObjectID", ctx, FormationID, formationAssignments[3].Source).Return([]*model.FormationAssignment{{ID: "id6"}}, nil).Once() 2946 return svc 2947 }, 2948 FormationTemplateRepositoryFn: func() *automock.FormationTemplateRepository { 2949 repo := &automock.FormationTemplateRepository{} 2950 repo.On("Get", ctx, FormationTemplateID).Return(&formationTemplate, nil) 2951 return repo 2952 }, 2953 NotificationServiceFN: func() *automock.NotificationsService { 2954 notificationSvc := &automock.NotificationsService{} 2955 notificationSvc.On("GenerateFormationNotifications", ctx, formationLifecycleSyncWebhooks, TntInternalID, fixFormationModelWithState(model.InitialFormationState), testFormationTemplateName, FormationTemplateID, model.CreateFormation).Return(formationNotificationSyncCreateRequests, nil).Once() 2956 notificationSvc.On("SendNotification", ctx, formationNotificationSyncCreateRequest).Return(formationNotificationWebhookSuccessResponse, nil).Once() 2957 return notificationSvc 2958 }, 2959 FormationAssignmentNotificationServiceFN: func() *automock.FormationAssignmentNotificationsService { 2960 svc := &automock.FormationAssignmentNotificationsService{} 2961 2962 svc.On("GenerateFormationAssignmentNotification", ctx, formationAssignments[0], model.AssignFormation).Return(notificationsForAssignments[0], nil).Once() 2963 svc.On("GenerateFormationAssignmentNotification", ctx, formationAssignments[1], model.AssignFormation).Return(notificationsForAssignments[1], nil).Once() 2964 svc.On("GenerateFormationAssignmentNotification", ctx, formationAssignments[2], model.UnassignFormation).Return(notificationsForAssignments[2], nil).Once() 2965 svc.On("GenerateFormationAssignmentNotification", ctx, formationAssignments[3], model.UnassignFormation).Return(notificationsForAssignments[3], nil).Once() 2966 return svc 2967 }, 2968 WebhookRepoFn: func() *automock.WebhookRepository { 2969 webhookRepo := &automock.WebhookRepository{} 2970 webhookRepo.On("ListByReferenceObjectIDGlobal", ctx, FormationTemplateID, model.FormationTemplateWebhookReference).Return(formationLifecycleSyncWebhooks, nil).Once() 2971 return webhookRepo 2972 }, 2973 FormationRepositoryFn: func() *automock.FormationRepository { 2974 repo := &automock.FormationRepository{} 2975 repo.On("Get", ctx, FormationID, TntInternalID).Return(fixFormationModelWithState(model.InitialFormationState), nil).Once() 2976 repo.On("Get", ctx, FormationID, TntInternalID).Return(testFormation, nil).Once() 2977 return repo 2978 }, 2979 StatusServiceFn: func() *automock.StatusService { 2980 svc := &automock.StatusService{} 2981 svc.On("UpdateWithConstraints", ctx, fixFormationModelWithState(model.ReadyFormationState), model.CreateFormation).Return(nil).Once() 2982 return svc 2983 }, 2984 }, 2985 // Business logic tests for formation notifications only 2986 { 2987 Name: "success when resynchronization is successful for formation notifications", 2988 FormationTemplateRepositoryFn: func() *automock.FormationTemplateRepository { 2989 repo := &automock.FormationTemplateRepository{} 2990 repo.On("Get", ctx, FormationTemplateID).Return(&formationTemplate, nil) 2991 return repo 2992 }, 2993 NotificationServiceFN: func() *automock.NotificationsService { 2994 notificationSvc := &automock.NotificationsService{} 2995 notificationSvc.On("GenerateFormationNotifications", ctx, formationLifecycleSyncWebhooks, TntInternalID, fixFormationModelWithState(model.InitialFormationState), testFormationTemplateName, FormationTemplateID, model.CreateFormation).Return(formationNotificationSyncCreateRequests, nil).Once() 2996 notificationSvc.On("SendNotification", ctx, formationNotificationSyncCreateRequest).Return(formationNotificationWebhookErrorResponse, nil).Once() 2997 return notificationSvc 2998 }, 2999 WebhookRepoFn: func() *automock.WebhookRepository { 3000 webhookRepo := &automock.WebhookRepository{} 3001 webhookRepo.On("ListByReferenceObjectIDGlobal", ctx, FormationTemplateID, model.FormationTemplateWebhookReference).Return(formationLifecycleSyncWebhooks, nil).Once() 3002 return webhookRepo 3003 }, 3004 FormationRepositoryFn: func() *automock.FormationRepository { 3005 repo := &automock.FormationRepository{} 3006 repo.On("Get", ctx, FormationID, TntInternalID).Return(fixFormationModelWithState(model.InitialFormationState), nil).Once() 3007 repo.On("Get", ctx, FormationID, TntInternalID).Return(formationInCreateErrorState, nil).Once() 3008 return repo 3009 }, 3010 StatusServiceFn: func() *automock.StatusService { 3011 svc := &automock.StatusService{} 3012 svc.On("SetFormationToErrorStateWithConstraints", ctx, formationWithInitialState, testErr.Error(), formationassignment.AssignmentErrorCode(formationassignment.ClientError), model.CreateErrorFormationState, model.CreateFormation).Return(nil).Once() 3013 return svc 3014 }, 3015 }, 3016 { 3017 Name: "error when resynchronization is successful for formation notifications but fails while getting formation", 3018 FormationTemplateRepositoryFn: func() *automock.FormationTemplateRepository { 3019 repo := &automock.FormationTemplateRepository{} 3020 repo.On("Get", ctx, FormationTemplateID).Return(&formationTemplate, nil) 3021 return repo 3022 }, 3023 NotificationServiceFN: func() *automock.NotificationsService { 3024 notificationSvc := &automock.NotificationsService{} 3025 notificationSvc.On("GenerateFormationNotifications", ctx, formationLifecycleSyncWebhooks, TntInternalID, fixFormationModelWithState(model.InitialFormationState), testFormationTemplateName, FormationTemplateID, model.CreateFormation).Return(formationNotificationSyncCreateRequests, nil).Once() 3026 notificationSvc.On("SendNotification", ctx, formationNotificationSyncCreateRequest).Return(formationNotificationWebhookErrorResponse, nil).Once() 3027 return notificationSvc 3028 }, 3029 WebhookRepoFn: func() *automock.WebhookRepository { 3030 webhookRepo := &automock.WebhookRepository{} 3031 webhookRepo.On("ListByReferenceObjectIDGlobal", ctx, FormationTemplateID, model.FormationTemplateWebhookReference).Return(formationLifecycleSyncWebhooks, nil).Once() 3032 return webhookRepo 3033 }, 3034 FormationRepositoryFn: func() *automock.FormationRepository { 3035 repo := &automock.FormationRepository{} 3036 repo.On("Get", ctx, FormationID, TntInternalID).Return(fixFormationModelWithState(model.InitialFormationState), nil).Once() 3037 repo.On("Get", ctx, FormationID, TntInternalID).Return(nil, testErr).Once() 3038 return repo 3039 }, 3040 StatusServiceFn: func() *automock.StatusService { 3041 svc := &automock.StatusService{} 3042 svc.On("SetFormationToErrorStateWithConstraints", ctx, formationWithInitialState, testErr.Error(), formationassignment.AssignmentErrorCode(formationassignment.ClientError), model.CreateErrorFormationState, model.CreateFormation).Return(nil).Once() 3043 return svc 3044 }, 3045 ExpectedErrMessage: testErr.Error(), 3046 }, 3047 { 3048 Name: "error when resynchronization is unsuccessful for formation notifications due to technical error", 3049 FormationTemplateRepositoryFn: func() *automock.FormationTemplateRepository { 3050 repo := &automock.FormationTemplateRepository{} 3051 repo.On("Get", ctx, FormationTemplateID).Return(&formationTemplate, nil) 3052 return repo 3053 }, 3054 NotificationServiceFN: func() *automock.NotificationsService { 3055 notificationSvc := &automock.NotificationsService{} 3056 notificationSvc.On("GenerateFormationNotifications", ctx, formationLifecycleSyncWebhooks, TntInternalID, fixFormationModelWithState(model.InitialFormationState), testFormationTemplateName, FormationTemplateID, model.CreateFormation).Return(formationNotificationSyncCreateRequests, nil).Once() 3057 notificationSvc.On("SendNotification", ctx, formationNotificationSyncCreateRequest).Return(nil, testErr).Once() 3058 return notificationSvc 3059 }, 3060 WebhookRepoFn: func() *automock.WebhookRepository { 3061 webhookRepo := &automock.WebhookRepository{} 3062 webhookRepo.On("ListByReferenceObjectIDGlobal", ctx, FormationTemplateID, model.FormationTemplateWebhookReference).Return(formationLifecycleSyncWebhooks, nil).Once() 3063 return webhookRepo 3064 }, 3065 FormationRepositoryFn: func() *automock.FormationRepository { 3066 repo := &automock.FormationRepository{} 3067 repo.On("Get", ctx, FormationID, TntInternalID).Return(fixFormationModelWithState(model.InitialFormationState), nil).Once() 3068 repo.On("Update", ctx, formationInCreateErrorStateTechnicalError).Return(nil).Once() 3069 return repo 3070 }, 3071 ExpectedErrMessage: testErr.Error(), 3072 }, 3073 { 3074 Name: "error when resynchronization is unsuccessful for formation notifications during generating notifications", 3075 FormationTemplateRepositoryFn: func() *automock.FormationTemplateRepository { 3076 repo := &automock.FormationTemplateRepository{} 3077 repo.On("Get", ctx, FormationTemplateID).Return(&formationTemplate, nil) 3078 return repo 3079 }, 3080 NotificationServiceFN: func() *automock.NotificationsService { 3081 notificationSvc := &automock.NotificationsService{} 3082 notificationSvc.On("GenerateFormationNotifications", ctx, formationLifecycleSyncWebhooks, TntInternalID, fixFormationModelWithState(model.InitialFormationState), testFormationTemplateName, FormationTemplateID, model.CreateFormation).Return(nil, testErr).Once() 3083 return notificationSvc 3084 }, 3085 WebhookRepoFn: func() *automock.WebhookRepository { 3086 webhookRepo := &automock.WebhookRepository{} 3087 webhookRepo.On("ListByReferenceObjectIDGlobal", ctx, FormationTemplateID, model.FormationTemplateWebhookReference).Return(formationLifecycleSyncWebhooks, nil).Once() 3088 return webhookRepo 3089 }, 3090 FormationRepositoryFn: func() *automock.FormationRepository { 3091 repo := &automock.FormationRepository{} 3092 repo.On("Get", ctx, FormationID, TntInternalID).Return(fixFormationModelWithState(model.InitialFormationState), nil).Once() 3093 return repo 3094 }, 3095 ExpectedErrMessage: testErr.Error(), 3096 }, 3097 { 3098 Name: "error when resynchronization is unsuccessful for formation notifications during getting webhooks", 3099 FormationTemplateRepositoryFn: func() *automock.FormationTemplateRepository { 3100 repo := &automock.FormationTemplateRepository{} 3101 repo.On("Get", ctx, FormationTemplateID).Return(&formationTemplate, nil) 3102 return repo 3103 }, 3104 WebhookRepoFn: func() *automock.WebhookRepository { 3105 webhookRepo := &automock.WebhookRepository{} 3106 webhookRepo.On("ListByReferenceObjectIDGlobal", ctx, FormationTemplateID, model.FormationTemplateWebhookReference).Return(nil, testErr).Once() 3107 return webhookRepo 3108 }, 3109 FormationRepositoryFn: func() *automock.FormationRepository { 3110 repo := &automock.FormationRepository{} 3111 repo.On("Get", ctx, FormationID, TntInternalID).Return(fixFormationModelWithState(model.InitialFormationState), nil).Once() 3112 return repo 3113 }, 3114 ExpectedErrMessage: testErr.Error(), 3115 }, 3116 { 3117 Name: "error when resynchronization is unsuccessful for formation notifications during getting formation template", 3118 FormationTemplateRepositoryFn: func() *automock.FormationTemplateRepository { 3119 repo := &automock.FormationTemplateRepository{} 3120 repo.On("Get", ctx, FormationTemplateID).Return(nil, testErr) 3121 return repo 3122 }, 3123 FormationRepositoryFn: func() *automock.FormationRepository { 3124 repo := &automock.FormationRepository{} 3125 repo.On("Get", ctx, FormationID, TntInternalID).Return(fixFormationModelWithState(model.InitialFormationState), nil).Once() 3126 return repo 3127 }, 3128 ExpectedErrMessage: testErr.Error(), 3129 }, 3130 { 3131 Name: "success when resynchronization is successful for formation notifications with DELETE_ERROR state", 3132 FormationTemplateRepositoryFn: func() *automock.FormationTemplateRepository { 3133 repo := &automock.FormationTemplateRepository{} 3134 repo.On("Get", ctx, FormationTemplateID).Return(&formationTemplate, nil) 3135 return repo 3136 }, 3137 LabelDefRepositoryFn: func() *automock.LabelDefRepository { 3138 labelDefRepo := &automock.LabelDefRepository{} 3139 labelDefRepo.On("GetByKey", ctx, TntInternalID, model.ScenariosKey).Return(&testSchemaLblDef, nil) 3140 labelDefRepo.On("UpdateWithVersion", ctx, newSchemaLblDef).Return(nil) 3141 return labelDefRepo 3142 }, 3143 LabelDefServiceFn: func() *automock.LabelDefService { 3144 labelDefService := &automock.LabelDefService{} 3145 labelDefService.On("ValidateExistingLabelsAgainstSchema", ctx, newSchema, TntInternalID, model.ScenariosKey).Return(nil) 3146 labelDefService.On("ValidateAutomaticScenarioAssignmentAgainstSchema", ctx, newSchema, TntInternalID, model.ScenariosKey).Return(nil) 3147 return labelDefService 3148 }, 3149 NotificationServiceFN: func() *automock.NotificationsService { 3150 notificationSvc := &automock.NotificationsService{} 3151 notificationSvc.On("GenerateFormationNotifications", ctx, formationLifecycleSyncWebhooks, TntInternalID, fixFormationModelWithStateAndAssignmentError(t, model.DeleteErrorFormationState, testErr.Error(), formationassignment.ClientError), testFormationTemplateName, FormationTemplateID, model.DeleteFormation).Return(formationNotificationSyncDeleteRequests, nil).Once() 3152 notificationSvc.On("SendNotification", ctx, formationNotificationSyncDeleteRequest).Return(formationNotificationWebhookSuccessResponse, nil).Once() 3153 return notificationSvc 3154 }, 3155 WebhookRepoFn: func() *automock.WebhookRepository { 3156 webhookRepo := &automock.WebhookRepository{} 3157 webhookRepo.On("ListByReferenceObjectIDGlobal", ctx, FormationTemplateID, model.FormationTemplateWebhookReference).Return(formationLifecycleSyncWebhooks, nil).Once() 3158 return webhookRepo 3159 }, 3160 FormationRepositoryFn: func() *automock.FormationRepository { 3161 repo := &automock.FormationRepository{} 3162 repo.On("Get", ctx, FormationID, TntInternalID).Return(fixFormationModelWithStateAndAssignmentError(t, model.DeleteErrorFormationState, testErr.Error(), formationassignment.ClientError), nil).Once() 3163 repo.On("DeleteByName", ctx, TntInternalID, testFormationName).Return(nil).Once() 3164 return repo 3165 }, 3166 StatusServiceFn: func() *automock.StatusService { 3167 svc := &automock.StatusService{} 3168 svc.On("UpdateWithConstraints", ctx, fixFormationModelWithState(model.ReadyFormationState), model.DeleteFormation).Return(nil).Once() 3169 return svc 3170 }, 3171 }, 3172 { 3173 Name: "error when resynchronization is successful for formation notifications with DELETE_ERROR during deletion of formation", 3174 FormationTemplateRepositoryFn: func() *automock.FormationTemplateRepository { 3175 repo := &automock.FormationTemplateRepository{} 3176 repo.On("Get", ctx, FormationTemplateID).Return(&formationTemplate, nil) 3177 return repo 3178 }, 3179 LabelDefRepositoryFn: func() *automock.LabelDefRepository { 3180 labelDefRepo := &automock.LabelDefRepository{} 3181 labelDefRepo.On("GetByKey", ctx, TntInternalID, model.ScenariosKey).Return(&testSchemaLblDef, nil) 3182 labelDefRepo.On("UpdateWithVersion", ctx, newSchemaLblDef).Return(nil) 3183 return labelDefRepo 3184 }, 3185 LabelDefServiceFn: func() *automock.LabelDefService { 3186 labelDefService := &automock.LabelDefService{} 3187 labelDefService.On("ValidateExistingLabelsAgainstSchema", ctx, newSchema, TntInternalID, model.ScenariosKey).Return(nil) 3188 labelDefService.On("ValidateAutomaticScenarioAssignmentAgainstSchema", ctx, newSchema, TntInternalID, model.ScenariosKey).Return(nil) 3189 return labelDefService 3190 }, 3191 NotificationServiceFN: func() *automock.NotificationsService { 3192 notificationSvc := &automock.NotificationsService{} 3193 notificationSvc.On("GenerateFormationNotifications", ctx, formationLifecycleSyncWebhooks, TntInternalID, fixFormationModelWithStateAndAssignmentError(t, model.DeleteErrorFormationState, testErr.Error(), formationassignment.ClientError), testFormationTemplateName, FormationTemplateID, model.DeleteFormation).Return(formationNotificationSyncDeleteRequests, nil).Once() 3194 notificationSvc.On("SendNotification", ctx, formationNotificationSyncDeleteRequest).Return(formationNotificationWebhookSuccessResponse, nil).Once() 3195 return notificationSvc 3196 }, 3197 WebhookRepoFn: func() *automock.WebhookRepository { 3198 webhookRepo := &automock.WebhookRepository{} 3199 webhookRepo.On("ListByReferenceObjectIDGlobal", ctx, FormationTemplateID, model.FormationTemplateWebhookReference).Return(formationLifecycleSyncWebhooks, nil).Once() 3200 return webhookRepo 3201 }, 3202 FormationRepositoryFn: func() *automock.FormationRepository { 3203 repo := &automock.FormationRepository{} 3204 repo.On("Get", ctx, FormationID, TntInternalID).Return(fixFormationModelWithStateAndAssignmentError(t, model.DeleteErrorFormationState, testErr.Error(), formationassignment.ClientError), nil).Once() 3205 repo.On("DeleteByName", ctx, TntInternalID, testFormationName).Return(testErr).Once() 3206 return repo 3207 }, 3208 StatusServiceFn: func() *automock.StatusService { 3209 svc := &automock.StatusService{} 3210 svc.On("UpdateWithConstraints", ctx, fixFormationModelWithState(model.ReadyFormationState), model.DeleteFormation).Return(nil).Once() 3211 return svc 3212 }, 3213 ExpectedErrMessage: testErr.Error(), 3214 }, 3215 } 3216 3217 for _, testCase := range testCases { 3218 t.Run(testCase.Name, func(t *testing.T) { 3219 // GIVEN 3220 labelService := unusedLabelService() 3221 if testCase.LabelServiceFn != nil { 3222 labelService = testCase.LabelServiceFn() 3223 } 3224 runtimeContextRepo := unusedRuntimeContextRepo() 3225 if testCase.RuntimeContextRepoFn != nil { 3226 runtimeContextRepo = testCase.RuntimeContextRepoFn() 3227 } 3228 formationRepo := unusedFormationRepo() 3229 if testCase.FormationRepositoryFn != nil { 3230 formationRepo = testCase.FormationRepositoryFn() 3231 } 3232 formationTemplateRepo := unusedFormationTemplateRepo() 3233 if testCase.FormationTemplateRepositoryFn != nil { 3234 formationTemplateRepo = testCase.FormationTemplateRepositoryFn() 3235 } 3236 labelRepo := unusedLabelRepo() 3237 if testCase.LabelRepoFn != nil { 3238 labelRepo = testCase.LabelRepoFn() 3239 } 3240 notificationsSvc := unusedNotificationsService() 3241 if testCase.NotificationServiceFN != nil { 3242 notificationsSvc = testCase.NotificationServiceFN() 3243 } 3244 formationAssignmentSvc := unusedFormationAssignmentService() 3245 if testCase.FormationAssignmentServiceFn != nil { 3246 formationAssignmentSvc = testCase.FormationAssignmentServiceFn() 3247 } 3248 asaEngine := unusedASAEngine() 3249 if testCase.AsaEngineFN != nil { 3250 asaEngine = testCase.AsaEngineFN() 3251 } 3252 formationAssignmentNotificationService := unusedFormationAssignmentNotificationService() 3253 if testCase.FormationAssignmentNotificationServiceFN != nil { 3254 formationAssignmentNotificationService = testCase.FormationAssignmentNotificationServiceFN() 3255 } 3256 webhookRepo := unusedWebhookRepository() 3257 if testCase.WebhookRepoFn != nil { 3258 webhookRepo = testCase.WebhookRepoFn() 3259 } 3260 labelDefRepo := unusedLabelDefRepository() 3261 if testCase.LabelDefRepositoryFn != nil { 3262 labelDefRepo = testCase.LabelDefRepositoryFn() 3263 } 3264 labelDefSvc := unusedLabelDefService() 3265 if testCase.LabelDefServiceFn != nil { 3266 labelDefSvc = testCase.LabelDefServiceFn() 3267 } 3268 statusService := &automock.StatusService{} 3269 if testCase.StatusServiceFn != nil { 3270 statusService = testCase.StatusServiceFn() 3271 } 3272 3273 svc := formation.NewServiceWithAsaEngine(nil, nil, labelDefRepo, labelRepo, formationRepo, formationTemplateRepo, labelService, nil, labelDefSvc, nil, nil, nil, nil, runtimeContextRepo, formationAssignmentSvc, webhookRepo, formationAssignmentNotificationService, notificationsSvc, nil, runtimeType, applicationType, asaEngine, statusService) 3274 3275 // WHEN 3276 _, err := svc.ResynchronizeFormationNotifications(ctx, FormationID) 3277 3278 // THEN 3279 if testCase.ExpectedErrMessage == "" { 3280 require.NoError(t, err) 3281 } else { 3282 require.Error(t, err) 3283 require.Contains(t, err.Error(), testCase.ExpectedErrMessage) 3284 } 3285 mock.AssertExpectationsForObjects(t, labelService, runtimeContextRepo, asaEngine, formationRepo, labelRepo, notificationsSvc, formationAssignmentSvc, formationAssignmentNotificationService, formationTemplateRepo, webhookRepo, statusService) 3286 }) 3287 } 3288 t.Run("returns error when empty tenant", func(t *testing.T) { 3289 svc := formation.NewService(nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, runtimeType, applicationType) 3290 _, err := svc.ResynchronizeFormationNotifications(context.TODO(), FormationID) 3291 require.Contains(t, err.Error(), "cannot read tenant from context") 3292 }) 3293 }