github.com/kyma-incubator/compass/components/director@v0.0.0-20230623144113-d764f56ff805/internal/domain/formation/resolver_test.go (about) 1 package formation_test 2 3 import ( 4 "context" 5 "encoding/json" 6 "errors" 7 "testing" 8 9 dataloader "github.com/kyma-incubator/compass/components/director/internal/dataloaders" 10 "github.com/kyma-incubator/compass/components/director/pkg/resource" 11 12 "github.com/kyma-incubator/compass/components/director/pkg/pagination" 13 persistenceautomock "github.com/kyma-incubator/compass/components/director/pkg/persistence/automock" 14 15 "github.com/kyma-incubator/compass/components/director/internal/domain/formation" 16 "github.com/kyma-incubator/compass/components/director/internal/domain/formation/automock" 17 "github.com/kyma-incubator/compass/components/director/internal/domain/tenant" 18 "github.com/kyma-incubator/compass/components/director/internal/model" 19 "github.com/kyma-incubator/compass/components/director/pkg/apperrors" 20 "github.com/kyma-incubator/compass/components/director/pkg/graphql" 21 "github.com/kyma-incubator/compass/components/director/pkg/persistence/txtest" 22 "github.com/stretchr/testify/assert" 23 "github.com/stretchr/testify/mock" 24 "github.com/stretchr/testify/require" 25 ) 26 27 func TestCreateFormation(t *testing.T) { 28 formationInput := graphql.FormationInput{ 29 Name: testFormationName, 30 } 31 32 testTemplateName := "test-template-name" 33 formationInputWithTemplateName := graphql.FormationInput{ 34 Name: testFormationName, 35 TemplateName: &testTemplateName, 36 } 37 38 tnt := "tenant" 39 externalTnt := "external-tenant" 40 testErr := errors.New("test error") 41 txGen := txtest.NewTransactionContextGenerator(testErr) 42 43 t.Run("successfully created formation with provided template name", func(t *testing.T) { 44 // GIVEN 45 persist, transact := txGen.ThatSucceeds() 46 47 mockService := &automock.Service{} 48 mockConverter := &automock.Converter{} 49 mockService.On("CreateFormation", contextThatHasTenant(tnt), tnt, modelFormation, testTemplateName).Return(&modelFormation, nil) 50 51 mockConverter.On("FromGraphQL", formationInputWithTemplateName).Return(modelFormation) 52 mockConverter.On("ToGraphQL", &modelFormation).Return(&graphqlFormation, nil) 53 54 ctx := tenant.SaveToContext(context.TODO(), tnt, externalTnt) 55 sut := formation.NewResolver(transact, mockService, mockConverter, nil, nil, nil) 56 57 // WHEN 58 actual, err := sut.CreateFormation(ctx, formationInputWithTemplateName) 59 60 // THEN 61 require.NoError(t, err) 62 assert.Equal(t, testFormationName, actual.Name) 63 mock.AssertExpectationsForObjects(t, persist, transact, mockService, mockConverter) 64 }) 65 66 t.Run("successfully created formation when no template is provided", func(t *testing.T) { 67 // GIVEN 68 persist, transact := txGen.ThatSucceeds() 69 70 mockService := &automock.Service{} 71 mockConverter := &automock.Converter{} 72 mockService.On("CreateFormation", contextThatHasTenant(tnt), tnt, modelFormation, model.DefaultTemplateName).Return(&modelFormation, nil) 73 74 mockConverter.On("FromGraphQL", formationInput).Return(modelFormation) 75 mockConverter.On("ToGraphQL", &modelFormation).Return(&graphqlFormation, nil) 76 77 ctx := tenant.SaveToContext(context.TODO(), tnt, externalTnt) 78 sut := formation.NewResolver(transact, mockService, mockConverter, nil, nil, nil) 79 80 // WHEN 81 actual, err := sut.CreateFormation(ctx, formationInput) 82 83 // THEN 84 require.NoError(t, err) 85 assert.Equal(t, testFormationName, actual.Name) 86 mock.AssertExpectationsForObjects(t, persist, transact, mockService, mockConverter) 87 }) 88 89 t.Run("returns error when can not load tenant from context", func(t *testing.T) { 90 // GIVEN 91 ctx := context.Background() 92 93 sut := formation.NewResolver(nil, nil, nil, nil, nil, nil) 94 95 // WHEN 96 _, err := sut.CreateFormation(ctx, formationInput) 97 98 // THEN 99 require.Error(t, err) 100 assert.Contains(t, err.Error(), apperrors.NewCannotReadTenantError().Error()) 101 }) 102 103 t.Run("returns error when can not start db transaction", func(t *testing.T) { 104 // GIVEN 105 persist, transact := txGen.ThatFailsOnBegin() 106 107 ctx := tenant.SaveToContext(context.TODO(), tnt, externalTnt) 108 sut := formation.NewResolver(transact, nil, nil, nil, nil, nil) 109 110 // WHEN 111 _, err := sut.CreateFormation(ctx, formationInput) 112 113 // THEN 114 require.Error(t, err) 115 assert.Contains(t, err.Error(), testErr.Error()) 116 mock.AssertExpectationsForObjects(t, persist, transact) 117 }) 118 119 t.Run("returns error when commit fails", func(t *testing.T) { 120 // GIVEN 121 persist, transact := txGen.ThatFailsOnCommit() 122 123 mockService := &automock.Service{} 124 mockService.On("CreateFormation", contextThatHasTenant(tnt), tnt, modelFormation, model.DefaultTemplateName).Return(&modelFormation, nil) 125 126 mockConverter := &automock.Converter{} 127 mockConverter.On("FromGraphQL", formationInput).Return(modelFormation) 128 129 ctx := tenant.SaveToContext(context.TODO(), tnt, externalTnt) 130 sut := formation.NewResolver(transact, mockService, mockConverter, nil, nil, nil) 131 132 // WHEN 133 _, err := sut.CreateFormation(ctx, formationInput) 134 135 // THEN 136 require.Error(t, err) 137 assert.Contains(t, err.Error(), testErr.Error()) 138 mock.AssertExpectationsForObjects(t, persist, transact, mockService, mockConverter) 139 }) 140 141 t.Run("returns error when create formation fails", func(t *testing.T) { 142 // GIVEN 143 persist, transact := txGen.ThatDoesntExpectCommit() 144 145 mockService := &automock.Service{} 146 mockService.On("CreateFormation", contextThatHasTenant(tnt), tnt, modelFormation, model.DefaultTemplateName).Return(nil, testErr) 147 148 mockConverter := &automock.Converter{} 149 mockConverter.On("FromGraphQL", formationInput).Return(modelFormation) 150 151 ctx := tenant.SaveToContext(context.TODO(), tnt, externalTnt) 152 sut := formation.NewResolver(transact, mockService, mockConverter, nil, nil, nil) 153 154 // WHEN 155 actual, err := sut.CreateFormation(ctx, formationInput) 156 157 // THEN 158 require.Error(t, err) 159 assert.Contains(t, err.Error(), testErr.Error()) 160 require.Nil(t, actual) 161 mock.AssertExpectationsForObjects(t, persist, transact, mockService, mockConverter) 162 }) 163 } 164 165 func TestDeleteFormation(t *testing.T) { 166 testFormation := testFormationName 167 formationInput := graphql.FormationInput{ 168 Name: testFormation, 169 } 170 tnt := "tenant" 171 externalTnt := "external-tenant" 172 testErr := errors.New("test error") 173 txGen := txtest.NewTransactionContextGenerator(testErr) 174 175 t.Run("successfully delete formation", func(t *testing.T) { 176 // GIVEN 177 persist, transact := txGen.ThatSucceeds() 178 179 mockService := &automock.Service{} 180 mockService.On("DeleteFormation", contextThatHasTenant(tnt), tnt, model.Formation{Name: testFormation}).Return(&model.Formation{Name: testFormation}, nil) 181 182 mockConverter := &automock.Converter{} 183 mockConverter.On("FromGraphQL", formationInput).Return(model.Formation{Name: testFormation}) 184 mockConverter.On("ToGraphQL", &model.Formation{Name: testFormation}).Return(&graphql.Formation{Name: testFormation}, nil) 185 186 ctx := tenant.SaveToContext(context.TODO(), tnt, externalTnt) 187 sut := formation.NewResolver(transact, mockService, mockConverter, nil, nil, nil) 188 189 // WHEN 190 actual, err := sut.DeleteFormation(ctx, formationInput) 191 192 // THEN 193 require.NoError(t, err) 194 assert.Equal(t, testFormation, actual.Name) 195 mock.AssertExpectationsForObjects(t, persist, transact, mockService, mockConverter) 196 }) 197 t.Run("returns error when can not load tenant from context", func(t *testing.T) { 198 // GIVEN 199 ctx := context.Background() 200 201 sut := formation.NewResolver(nil, nil, nil, nil, nil, nil) 202 203 // WHEN 204 _, err := sut.DeleteFormation(ctx, formationInput) 205 206 // THEN 207 require.Error(t, err) 208 }) 209 t.Run("returns error when can not start db transaction", func(t *testing.T) { 210 // GIVEN 211 persist, transact := txGen.ThatFailsOnBegin() 212 213 ctx := tenant.SaveToContext(context.TODO(), tnt, externalTnt) 214 sut := formation.NewResolver(transact, nil, nil, nil, nil, nil) 215 216 // WHEN 217 _, err := sut.DeleteFormation(ctx, formationInput) 218 219 // THEN 220 require.Error(t, err) 221 assert.Contains(t, err.Error(), testErr.Error()) 222 mock.AssertExpectationsForObjects(t, persist, transact) 223 }) 224 t.Run("returns error when commit fails", func(t *testing.T) { 225 // GIVEN 226 persist, transact := txGen.ThatFailsOnCommit() 227 228 mockService := &automock.Service{} 229 mockService.On("DeleteFormation", contextThatHasTenant(tnt), tnt, model.Formation{Name: testFormation}).Return(&model.Formation{Name: testFormation}, nil) 230 231 mockConverter := &automock.Converter{} 232 mockConverter.On("FromGraphQL", formationInput).Return(model.Formation{Name: testFormation}) 233 234 ctx := tenant.SaveToContext(context.TODO(), tnt, externalTnt) 235 sut := formation.NewResolver(transact, mockService, mockConverter, nil, nil, nil) 236 237 // WHEN 238 _, err := sut.DeleteFormation(ctx, formationInput) 239 240 // THEN 241 require.Error(t, err) 242 assert.Contains(t, err.Error(), testErr.Error()) 243 mock.AssertExpectationsForObjects(t, persist, transact, mockService, mockConverter) 244 }) 245 t.Run("returns error when create formation fails", func(t *testing.T) { 246 // GIVEN 247 persist, transact := txGen.ThatDoesntExpectCommit() 248 249 mockService := &automock.Service{} 250 mockService.On("DeleteFormation", contextThatHasTenant(tnt), tnt, model.Formation{Name: testFormation}).Return(nil, testErr) 251 252 mockConverter := &automock.Converter{} 253 mockConverter.On("FromGraphQL", formationInput).Return(model.Formation{Name: testFormation}) 254 255 ctx := tenant.SaveToContext(context.TODO(), tnt, externalTnt) 256 sut := formation.NewResolver(transact, mockService, mockConverter, nil, nil, nil) 257 258 // WHEN 259 actual, err := sut.DeleteFormation(ctx, formationInput) 260 261 // THEN 262 require.Error(t, err) 263 assert.Contains(t, err.Error(), testErr.Error()) 264 require.Nil(t, actual) 265 mock.AssertExpectationsForObjects(t, persist, transact, mockService, mockConverter) 266 }) 267 } 268 269 func TestAssignFormation(t *testing.T) { 270 formationInput := graphql.FormationInput{ 271 Name: testFormationName, 272 } 273 tnt := "tenant" 274 externalTnt := "external-tenant" 275 testObjectType := graphql.FormationObjectTypeTenant 276 testErr := errors.New("test error") 277 txGen := txtest.NewTransactionContextGenerator(testErr) 278 279 t.Run("successfully assigned formation", func(t *testing.T) { 280 // GIVEN 281 persist, transact := txGen.ThatSucceeds() 282 283 mockService := &automock.Service{} 284 mockConverter := &automock.Converter{} 285 fetcherSvc := &automock.TenantFetcher{} 286 mockService.On("AssignFormation", contextThatHasTenant(tnt), tnt, "", testObjectType, modelFormation).Return(&modelFormation, nil) 287 288 mockConverter.On("FromGraphQL", formationInput).Return(modelFormation) 289 mockConverter.On("ToGraphQL", &modelFormation).Return(&graphqlFormation, nil) 290 291 fetcherSvc.On("FetchOnDemand", "", tnt).Return(nil) 292 293 ctx := tenant.SaveToContext(context.TODO(), tnt, externalTnt) 294 sut := formation.NewResolver(transact, mockService, mockConverter, nil, nil, fetcherSvc) 295 296 // WHEN 297 actual, err := sut.AssignFormation(ctx, "", testObjectType, formationInput) 298 299 // THEN 300 require.NoError(t, err) 301 assert.Equal(t, testFormationName, actual.Name) 302 mock.AssertExpectationsForObjects(t, persist, transact, mockService, mockConverter, fetcherSvc) 303 }) 304 t.Run("returns error when objectType is tenant and cannot fetch its details", func(t *testing.T) { 305 // GIVEN 306 persist, transact := txGen.ThatDoesntStartTransaction() 307 308 mockService := &automock.Service{} 309 mockConverter := &automock.Converter{} 310 fetcherSvc := &automock.TenantFetcher{} 311 312 fetcherSvc.On("FetchOnDemand", "", tnt).Return(testErr) 313 314 ctx := tenant.SaveToContext(context.TODO(), tnt, externalTnt) 315 sut := formation.NewResolver(transact, mockService, mockConverter, nil, nil, fetcherSvc) 316 317 // WHEN 318 _, err := sut.AssignFormation(ctx, "", testObjectType, formationInput) 319 320 // THEN 321 require.Error(t, err) 322 assert.Contains(t, err.Error(), testErr.Error()) 323 mock.AssertExpectationsForObjects(t, persist, transact, mockService, mockConverter, fetcherSvc) 324 }) 325 t.Run("returns error when can not load tenant from context", func(t *testing.T) { 326 // GIVEN 327 ctx := context.Background() 328 329 sut := formation.NewResolver(nil, nil, nil, nil, nil, nil) 330 331 // WHEN 332 _, err := sut.AssignFormation(ctx, "", testObjectType, formationInput) 333 334 // THEN 335 require.Error(t, err) 336 assert.Contains(t, err.Error(), apperrors.NewCannotReadTenantError().Error()) 337 }) 338 t.Run("returns error when can not start db transaction", func(t *testing.T) { 339 // GIVEN 340 persist, transact := txGen.ThatFailsOnBegin() 341 342 fetcherSvc := &automock.TenantFetcher{} 343 fetcherSvc.On("FetchOnDemand", "", tnt).Return(nil) 344 345 ctx := tenant.SaveToContext(context.TODO(), tnt, externalTnt) 346 sut := formation.NewResolver(transact, nil, nil, nil, nil, fetcherSvc) 347 348 // WHEN 349 _, err := sut.AssignFormation(ctx, "", testObjectType, formationInput) 350 351 // THEN 352 require.Error(t, err) 353 assert.Contains(t, err.Error(), testErr.Error()) 354 mock.AssertExpectationsForObjects(t, persist, transact, fetcherSvc) 355 }) 356 t.Run("returns error when commit fails", func(t *testing.T) { 357 // GIVEN 358 persist, transact := txGen.ThatFailsOnCommit() 359 360 mockService := &automock.Service{} 361 mockService.On("AssignFormation", contextThatHasTenant(tnt), tnt, "", testObjectType, modelFormation).Return(&modelFormation, nil) 362 363 mockConverter := &automock.Converter{} 364 mockConverter.On("FromGraphQL", formationInput).Return(modelFormation) 365 366 fetcherSvc := &automock.TenantFetcher{} 367 fetcherSvc.On("FetchOnDemand", "", tnt).Return(nil) 368 369 ctx := tenant.SaveToContext(context.TODO(), tnt, externalTnt) 370 sut := formation.NewResolver(transact, mockService, mockConverter, nil, nil, fetcherSvc) 371 372 // WHEN 373 _, err := sut.AssignFormation(ctx, "", testObjectType, formationInput) 374 375 // THEN 376 require.Error(t, err) 377 assert.Contains(t, err.Error(), testErr.Error()) 378 mock.AssertExpectationsForObjects(t, persist, transact, mockService, mockConverter, fetcherSvc) 379 }) 380 t.Run("returns error when assign formation fails", func(t *testing.T) { 381 // GIVEN 382 persist, transact := txGen.ThatDoesntExpectCommit() 383 384 mockService := &automock.Service{} 385 mockService.On("AssignFormation", contextThatHasTenant(tnt), tnt, "", testObjectType, modelFormation).Return(nil, testErr) 386 387 mockConverter := &automock.Converter{} 388 mockConverter.On("FromGraphQL", formationInput).Return(modelFormation) 389 390 fetcherSvc := &automock.TenantFetcher{} 391 fetcherSvc.On("FetchOnDemand", "", tnt).Return(nil) 392 393 ctx := tenant.SaveToContext(context.TODO(), tnt, externalTnt) 394 sut := formation.NewResolver(transact, mockService, mockConverter, nil, nil, fetcherSvc) 395 396 // WHEN 397 actual, err := sut.AssignFormation(ctx, "", testObjectType, formationInput) 398 399 // THEN 400 require.Error(t, err) 401 assert.Contains(t, err.Error(), testErr.Error()) 402 require.Nil(t, actual) 403 mock.AssertExpectationsForObjects(t, persist, transact, mockService, mockConverter, fetcherSvc) 404 }) 405 } 406 407 func TestUnassignFormation(t *testing.T) { 408 formationInput := graphql.FormationInput{ 409 Name: testFormationName, 410 } 411 formationAssignment := model.FormationAssignment{ID: FormationAssignmentID} 412 tnt := "tenant" 413 externalTnt := "external-tenant" 414 testErr := errors.New("test error") 415 txGen := txtest.NewTransactionContextGenerator(testErr) 416 testCases := []struct { 417 Name string 418 TxFn func() (*persistenceautomock.PersistenceTx, *persistenceautomock.Transactioner) 419 ServiceFn func() *automock.Service 420 ConverterFn func() *automock.Converter 421 FormationAssignmentSvcFn func() *automock.FormationAssignmentService 422 InputID string 423 ObjectType graphql.FormationObjectType 424 Context context.Context 425 ExpectedFormation *graphql.Formation 426 ExpectedError error 427 }{ 428 { 429 Name: "successfully unassigned formation", 430 TxFn: txGen.ThatSucceeds, 431 ConverterFn: func() *automock.Converter { 432 conv := &automock.Converter{} 433 conv.On("FromGraphQL", formationInput).Return(modelFormation) 434 conv.On("ToGraphQL", &modelFormation).Return(&graphqlFormation, nil) 435 return conv 436 }, 437 Context: tenant.SaveToContext(context.TODO(), tnt, externalTnt), 438 ObjectType: graphql.FormationObjectTypeTenant, 439 ServiceFn: func() *automock.Service { 440 svc := &automock.Service{} 441 svc.On("UnassignFormation", contextThatHasTenant(tnt), tnt, "", graphql.FormationObjectTypeTenant, modelFormation).Return(&modelFormation, nil) 442 return svc 443 }, 444 ExpectedFormation: &graphqlFormation, 445 }, 446 { 447 Name: "successfully unassigned formation deletes self formation assignment", 448 TxFn: txGen.ThatSucceedsTwice, 449 ConverterFn: func() *automock.Converter { 450 conv := &automock.Converter{} 451 conv.On("FromGraphQL", formationInput).Return(modelFormation) 452 conv.On("ToGraphQL", &modelFormation).Return(&graphqlFormation, nil) 453 return conv 454 }, 455 Context: tenant.SaveToContext(context.TODO(), tnt, externalTnt), 456 ObjectType: graphql.FormationObjectTypeApplication, 457 ServiceFn: func() *automock.Service { 458 svc := &automock.Service{} 459 svc.On("GetFormationByName", contextThatHasTenant(tnt), formationInput.Name, tnt).Return(&modelFormation, nil) 460 svc.On("UnassignFormation", contextThatHasTenant(tnt), tnt, ApplicationID, graphql.FormationObjectTypeApplication, modelFormation).Return(&modelFormation, nil) 461 return svc 462 }, 463 FormationAssignmentSvcFn: func() *automock.FormationAssignmentService { 464 svc := &automock.FormationAssignmentService{} 465 svc.On("GetReverseBySourceAndTarget", contextThatHasTenant(tnt), modelFormation.ID, ApplicationID, ApplicationID).Return(&formationAssignment, nil) 466 svc.On("Delete", contextThatHasTenant(tnt), formationAssignment.ID).Return(nil) 467 return svc 468 }, 469 InputID: ApplicationID, 470 ExpectedFormation: &graphqlFormation, 471 }, 472 { 473 Name: "successfully unassigned formation when self formation assignment is missing", 474 TxFn: txGen.ThatSucceedsTwice, 475 ConverterFn: func() *automock.Converter { 476 conv := &automock.Converter{} 477 conv.On("FromGraphQL", formationInput).Return(modelFormation) 478 conv.On("ToGraphQL", &modelFormation).Return(&graphqlFormation, nil) 479 return conv 480 }, 481 Context: tenant.SaveToContext(context.TODO(), tnt, externalTnt), 482 ObjectType: graphql.FormationObjectTypeApplication, 483 ServiceFn: func() *automock.Service { 484 svc := &automock.Service{} 485 svc.On("GetFormationByName", contextThatHasTenant(tnt), formationInput.Name, tnt).Return(&modelFormation, nil) 486 svc.On("UnassignFormation", contextThatHasTenant(tnt), tnt, ApplicationID, graphql.FormationObjectTypeApplication, modelFormation).Return(&modelFormation, nil) 487 return svc 488 }, 489 FormationAssignmentSvcFn: func() *automock.FormationAssignmentService { 490 svc := &automock.FormationAssignmentService{} 491 svc.On("GetReverseBySourceAndTarget", contextThatHasTenant(tnt), modelFormation.ID, ApplicationID, ApplicationID).Return(nil, testErr) 492 return svc 493 }, 494 InputID: ApplicationID, 495 ExpectedFormation: &graphqlFormation, 496 }, 497 { 498 Name: "successfully unassigned formation when self formation assignment is missing", 499 TxFn: txGen.ThatDoesntExpectCommit, 500 ConverterFn: unusedConverter, 501 Context: tenant.SaveToContext(context.TODO(), tnt, externalTnt), 502 ObjectType: graphql.FormationObjectTypeApplication, 503 ServiceFn: func() *automock.Service { 504 svc := &automock.Service{} 505 svc.On("GetFormationByName", contextThatHasTenant(tnt), formationInput.Name, tnt).Return(nil, testErr) 506 return svc 507 }, 508 InputID: ApplicationID, 509 ExpectedError: testErr, 510 }, 511 { 512 Name: "successfully unassigned formation when self formation assignment is missing", 513 TxFn: txGen.ThatDoesntExpectCommit, 514 ConverterFn: unusedConverter, 515 Context: tenant.SaveToContext(context.TODO(), tnt, externalTnt), 516 ObjectType: graphql.FormationObjectTypeApplication, 517 ServiceFn: func() *automock.Service { 518 svc := &automock.Service{} 519 svc.On("GetFormationByName", contextThatHasTenant(tnt), formationInput.Name, tnt).Return(nil, testErr) 520 return svc 521 }, 522 InputID: ApplicationID, 523 ExpectedError: testErr, 524 }, 525 { 526 Name: "successfully unassigned formation when self formation assignment is missing", 527 TxFn: txGen.ThatFailsOnCommit, 528 ConverterFn: unusedConverter, 529 Context: tenant.SaveToContext(context.TODO(), tnt, externalTnt), 530 ObjectType: graphql.FormationObjectTypeApplication, 531 ServiceFn: func() *automock.Service { 532 svc := &automock.Service{} 533 svc.On("GetFormationByName", contextThatHasTenant(tnt), formationInput.Name, tnt).Return(&modelFormation, nil) 534 return svc 535 }, 536 FormationAssignmentSvcFn: func() *automock.FormationAssignmentService { 537 svc := &automock.FormationAssignmentService{} 538 svc.On("GetReverseBySourceAndTarget", contextThatHasTenant(tnt), modelFormation.ID, ApplicationID, ApplicationID).Return(&formationAssignment, nil) 539 svc.On("Delete", contextThatHasTenant(tnt), formationAssignment.ID).Return(nil) 540 return svc 541 }, 542 InputID: ApplicationID, 543 ExpectedError: testErr, 544 }, 545 { 546 Name: "fails when transaction fails to open", 547 TxFn: txGen.ThatFailsOnBegin, 548 ConverterFn: unusedConverter, 549 Context: tenant.SaveToContext(context.TODO(), tnt, externalTnt), 550 ObjectType: graphql.FormationObjectTypeApplication, 551 ServiceFn: unusedService, 552 InputID: ApplicationID, 553 ExpectedError: testErr, 554 }, 555 { 556 Name: "returns error when can not load tenant from context", 557 TxFn: txGen.ThatDoesntExpectCommit, 558 Context: context.TODO(), 559 ObjectType: graphql.FormationObjectTypeTenant, 560 ConverterFn: unusedConverter, 561 ServiceFn: unusedService, 562 ExpectedError: apperrors.NewCannotReadTenantError(), 563 }, { 564 Name: "returns error when can not start db transaction", 565 TxFn: txGen.ThatFailsOnBegin, 566 Context: tenant.SaveToContext(context.TODO(), tnt, externalTnt), 567 ObjectType: graphql.FormationObjectTypeTenant, 568 ConverterFn: unusedConverter, 569 ServiceFn: unusedService, 570 ExpectedError: testErr, 571 }, { 572 Name: "returns error when commit fails", 573 TxFn: txGen.ThatFailsOnCommit, 574 Context: tenant.SaveToContext(context.TODO(), tnt, externalTnt), 575 ObjectType: graphql.FormationObjectTypeTenant, 576 ConverterFn: func() *automock.Converter { 577 conv := &automock.Converter{} 578 conv.On("FromGraphQL", formationInput).Return(modelFormation) 579 return conv 580 }, 581 ServiceFn: func() *automock.Service { 582 svc := &automock.Service{} 583 svc.On("UnassignFormation", contextThatHasTenant(tnt), tnt, "", graphql.FormationObjectTypeTenant, modelFormation).Return(&modelFormation, nil) 584 return svc 585 }, 586 ExpectedError: testErr, 587 }, { 588 Name: "returns error when assign formation fails", 589 TxFn: txGen.ThatDoesntExpectCommit, 590 Context: tenant.SaveToContext(context.TODO(), tnt, externalTnt), 591 ObjectType: graphql.FormationObjectTypeTenant, 592 ConverterFn: func() *automock.Converter { 593 conv := &automock.Converter{} 594 conv.On("FromGraphQL", formationInput).Return(modelFormation) 595 return conv 596 }, 597 ServiceFn: func() *automock.Service { 598 svc := &automock.Service{} 599 svc.On("UnassignFormation", contextThatHasTenant(tnt), tnt, "", graphql.FormationObjectTypeTenant, modelFormation).Return(nil, testErr) 600 return svc 601 }, 602 ExpectedError: testErr, 603 }, 604 } 605 for _, testCase := range testCases { 606 t.Run(testCase.Name, func(t *testing.T) { 607 // GIVEN 608 persist, transact := testCase.TxFn() 609 service := testCase.ServiceFn() 610 converter := testCase.ConverterFn() 611 formationAssignmentSvc := &automock.FormationAssignmentService{} 612 if testCase.FormationAssignmentSvcFn != nil { 613 formationAssignmentSvc = testCase.FormationAssignmentSvcFn() 614 } 615 616 resolver := formation.NewResolver(transact, service, converter, formationAssignmentSvc, nil, nil) 617 618 // WHEN 619 f, err := resolver.UnassignFormation(testCase.Context, testCase.InputID, testCase.ObjectType, formationInput) 620 621 // THEN 622 if testCase.ExpectedError != nil { 623 require.Error(t, err) 624 assert.Contains(t, err.Error(), testCase.ExpectedError.Error()) 625 } else { 626 assert.NoError(t, err) 627 } 628 assert.Equal(t, testCase.ExpectedFormation, f) 629 630 mock.AssertExpectationsForObjects(t, persist, service, converter, formationAssignmentSvc) 631 }) 632 } 633 } 634 635 func TestFormation(t *testing.T) { 636 testErr := errors.New("test error") 637 638 txGen := txtest.NewTransactionContextGenerator(testErr) 639 640 ctx := context.TODO() 641 642 testCases := []struct { 643 Name string 644 TxFn func() (*persistenceautomock.PersistenceTx, *persistenceautomock.Transactioner) 645 ServiceFn func() *automock.Service 646 ConverterFn func() *automock.Converter 647 FetcherFn func() *automock.TenantFetcher 648 InputID string 649 ExpectedFormation *graphql.Formation 650 ExpectedError error 651 }{ 652 { 653 Name: "Success", 654 TxFn: txGen.ThatSucceeds, 655 ServiceFn: func() *automock.Service { 656 service := &automock.Service{} 657 service.On("Get", txtest.CtxWithDBMatcher(), FormationID).Return(&modelFormation, nil).Once() 658 return service 659 }, 660 ConverterFn: func() *automock.Converter { 661 conv := &automock.Converter{} 662 conv.On("ToGraphQL", &modelFormation).Return(&graphqlFormation, nil).Once() 663 return conv 664 }, 665 InputID: FormationID, 666 ExpectedFormation: &graphqlFormation, 667 ExpectedError: nil, 668 }, 669 { 670 Name: "Returns error when getting formation fails", 671 TxFn: txGen.ThatDoesntExpectCommit, 672 ServiceFn: func() *automock.Service { 673 service := &automock.Service{} 674 service.On("Get", txtest.CtxWithDBMatcher(), FormationID).Return(nil, testErr).Once() 675 return service 676 }, 677 ConverterFn: unusedConverter, 678 InputID: FormationID, 679 ExpectedFormation: nil, 680 ExpectedError: testErr, 681 }, 682 { 683 Name: "Returns error when can't start transaction", 684 TxFn: txGen.ThatFailsOnBegin, 685 ServiceFn: unusedService, 686 ConverterFn: unusedConverter, 687 InputID: FormationID, 688 ExpectedFormation: nil, 689 ExpectedError: testErr, 690 }, 691 { 692 Name: "Returns error when can't commit transaction", 693 TxFn: txGen.ThatFailsOnCommit, 694 ServiceFn: func() *automock.Service { 695 service := &automock.Service{} 696 service.On("Get", txtest.CtxWithDBMatcher(), FormationID).Return(formationModel, nil).Once() 697 return service 698 }, ConverterFn: unusedConverter, 699 InputID: FormationID, 700 ExpectedFormation: nil, 701 ExpectedError: testErr, 702 }, 703 } 704 705 for _, testCase := range testCases { 706 t.Run(testCase.Name, func(t *testing.T) { 707 // GIVEN 708 persist, transact := testCase.TxFn() 709 service := testCase.ServiceFn() 710 converter := testCase.ConverterFn() 711 712 resolver := formation.NewResolver(transact, service, converter, nil, nil, nil) 713 714 // WHEN 715 f, err := resolver.Formation(ctx, testCase.InputID) 716 717 // THEN 718 if testCase.ExpectedError != nil { 719 require.Error(t, err) 720 assert.Contains(t, err.Error(), testCase.ExpectedError.Error()) 721 } else { 722 assert.NoError(t, err) 723 } 724 assert.Equal(t, testCase.ExpectedFormation, f) 725 726 mock.AssertExpectationsForObjects(t, persist, service, converter) 727 }) 728 } 729 } 730 731 func TestFormationByName(t *testing.T) { 732 testErr := errors.New("test error") 733 734 txGen := txtest.NewTransactionContextGenerator(testErr) 735 736 tnt := "tenant" 737 externalTnt := "external-tenant" 738 ctx := tenant.SaveToContext(context.TODO(), tnt, externalTnt) 739 emptyCtx := context.Background() 740 741 testCases := []struct { 742 Name string 743 Ctx context.Context 744 TxFn func() (*persistenceautomock.PersistenceTx, *persistenceautomock.Transactioner) 745 ServiceFn func() *automock.Service 746 ConverterFn func() *automock.Converter 747 FetcherFn func() *automock.TenantFetcher 748 InputName string 749 ExpectedFormation *graphql.Formation 750 ExpectedError error 751 }{ 752 { 753 Name: "Success", 754 Ctx: ctx, 755 TxFn: txGen.ThatSucceeds, 756 ServiceFn: func() *automock.Service { 757 service := &automock.Service{} 758 service.On("GetFormationByName", contextThatHasTenant(tnt), testFormationName, tnt).Return(&modelFormation, nil).Once() 759 return service 760 }, 761 ConverterFn: func() *automock.Converter { 762 conv := &automock.Converter{} 763 conv.On("ToGraphQL", &modelFormation).Return(&graphqlFormation, nil).Once() 764 return conv 765 }, 766 InputName: testFormationName, 767 ExpectedFormation: &graphqlFormation, 768 ExpectedError: nil, 769 }, 770 { 771 Name: "Returns error when getting tenant fails", 772 Ctx: emptyCtx, 773 TxFn: txGen.ThatDoesntExpectCommit, 774 ServiceFn: unusedService, 775 ConverterFn: unusedConverter, 776 InputName: testFormationName, 777 ExpectedFormation: nil, 778 ExpectedError: errors.New("cannot read tenant from context"), 779 }, 780 { 781 Name: "Returns error when getting formation fails", 782 Ctx: ctx, 783 TxFn: txGen.ThatDoesntExpectCommit, 784 ServiceFn: func() *automock.Service { 785 service := &automock.Service{} 786 service.On("GetFormationByName", txtest.CtxWithDBMatcher(), testFormationName, tnt).Return(nil, testErr).Once() 787 return service 788 }, 789 ConverterFn: unusedConverter, 790 InputName: testFormationName, 791 ExpectedFormation: nil, 792 ExpectedError: testErr, 793 }, 794 { 795 Name: "Returns error when can't start transaction", 796 Ctx: ctx, 797 TxFn: txGen.ThatFailsOnBegin, 798 ServiceFn: unusedService, 799 ConverterFn: unusedConverter, 800 InputName: testFormationName, 801 ExpectedFormation: nil, 802 ExpectedError: testErr, 803 }, 804 { 805 Name: "Returns error when can't commit transaction", 806 Ctx: ctx, 807 TxFn: txGen.ThatFailsOnCommit, 808 ServiceFn: func() *automock.Service { 809 service := &automock.Service{} 810 service.On("GetFormationByName", txtest.CtxWithDBMatcher(), testFormationName, tnt).Return(formationModel, nil).Once() 811 return service 812 }, ConverterFn: unusedConverter, 813 InputName: testFormationName, 814 ExpectedFormation: nil, 815 ExpectedError: testErr, 816 }, 817 } 818 819 for _, testCase := range testCases { 820 t.Run(testCase.Name, func(t *testing.T) { 821 // GIVEN 822 testCtx := testCase.Ctx 823 persist, transact := testCase.TxFn() 824 service := testCase.ServiceFn() 825 converter := testCase.ConverterFn() 826 827 resolver := formation.NewResolver(transact, service, converter, nil, nil, nil) 828 829 // WHEN 830 f, err := resolver.FormationByName(testCtx, testCase.InputName) 831 832 // THEN 833 if testCase.ExpectedError != nil { 834 require.Error(t, err) 835 assert.Contains(t, err.Error(), testCase.ExpectedError.Error()) 836 } else { 837 assert.NoError(t, err) 838 } 839 assert.Equal(t, testCase.ExpectedFormation, f) 840 841 mock.AssertExpectationsForObjects(t, persist, service, converter) 842 }) 843 } 844 } 845 func TestFormations(t *testing.T) { 846 testErr := errors.New("test error") 847 848 txGen := txtest.NewTransactionContextGenerator(testErr) 849 850 ctx := context.TODO() 851 852 first := 100 853 afterStr := "after" 854 after := graphql.PageCursor(afterStr) 855 856 modelFormations := []*model.Formation{&modelFormation} 857 858 graphqlFormations := []*graphql.Formation{&graphqlFormation} 859 860 modelPage := &model.FormationPage{ 861 Data: modelFormations, 862 PageInfo: &pagination.Page{ 863 StartCursor: "start", 864 EndCursor: "end", 865 HasNextPage: false, 866 }, 867 TotalCount: 1, 868 } 869 870 expectedOutput := &graphql.FormationPage{ 871 Data: graphqlFormations, 872 PageInfo: &graphql.PageInfo{ 873 StartCursor: "start", 874 EndCursor: "end", 875 HasNextPage: false, 876 }, 877 TotalCount: 1, 878 } 879 880 testCases := []struct { 881 Name string 882 TxFn func() (*persistenceautomock.PersistenceTx, *persistenceautomock.Transactioner) 883 ServiceFn func() *automock.Service 884 ConverterFn func() *automock.Converter 885 FetcherFn func() *automock.TenantFetcher 886 InputID string 887 ExpectedFormations *graphql.FormationPage 888 ExpectedError error 889 }{ 890 { 891 Name: "Success", 892 TxFn: txGen.ThatSucceeds, 893 ServiceFn: func() *automock.Service { 894 service := &automock.Service{} 895 service.On("List", txtest.CtxWithDBMatcher(), first, afterStr).Return(modelPage, nil).Once() 896 return service 897 }, 898 ConverterFn: func() *automock.Converter { 899 conv := &automock.Converter{} 900 conv.On("MultipleToGraphQL", modelFormations).Return(graphqlFormations, nil).Once() 901 return conv 902 }, 903 InputID: FormationID, 904 ExpectedFormations: expectedOutput, 905 ExpectedError: nil, 906 }, 907 { 908 Name: "Returns error when listing formations fails", 909 TxFn: txGen.ThatDoesntExpectCommit, 910 ServiceFn: func() *automock.Service { 911 service := &automock.Service{} 912 service.On("List", txtest.CtxWithDBMatcher(), first, afterStr).Return(nil, testErr).Once() 913 return service 914 }, 915 ConverterFn: unusedConverter, 916 InputID: FormationID, 917 ExpectedFormations: nil, 918 ExpectedError: testErr, 919 }, 920 { 921 Name: "Returns error when converting formations to graphql fails", 922 TxFn: txGen.ThatSucceeds, 923 ServiceFn: func() *automock.Service { 924 service := &automock.Service{} 925 service.On("List", txtest.CtxWithDBMatcher(), first, afterStr).Return(modelPage, nil).Once() 926 return service 927 }, 928 ConverterFn: func() *automock.Converter { 929 conv := &automock.Converter{} 930 conv.On("MultipleToGraphQL", modelFormations).Return(nil, testErr).Once() 931 return conv 932 }, 933 InputID: FormationID, 934 ExpectedFormations: nil, 935 ExpectedError: testErr, 936 }, 937 { 938 Name: "Returns error when can't start transaction", 939 TxFn: txGen.ThatFailsOnBegin, 940 ServiceFn: unusedService, 941 ConverterFn: unusedConverter, 942 InputID: FormationID, 943 ExpectedFormations: nil, 944 ExpectedError: testErr, 945 }, 946 { 947 Name: "Returns error when can't commit transaction", 948 TxFn: txGen.ThatFailsOnCommit, 949 ServiceFn: func() *automock.Service { 950 service := &automock.Service{} 951 service.On("List", txtest.CtxWithDBMatcher(), first, afterStr).Return(modelPage, nil).Once() 952 return service 953 }, 954 ConverterFn: unusedConverter, 955 InputID: FormationID, 956 ExpectedFormations: nil, 957 ExpectedError: testErr, 958 }, 959 } 960 for _, testCase := range testCases { 961 t.Run(testCase.Name, func(t *testing.T) { 962 // GIVEN 963 persist, transact := testCase.TxFn() 964 service := testCase.ServiceFn() 965 converter := testCase.ConverterFn() 966 967 resolver := formation.NewResolver(transact, service, converter, nil, nil, nil) 968 969 // WHEN 970 f, err := resolver.Formations(ctx, &first, &after) 971 972 // THEN 973 if testCase.ExpectedError != nil { 974 require.Error(t, err) 975 assert.Contains(t, err.Error(), testCase.ExpectedError.Error()) 976 } else { 977 assert.NoError(t, err) 978 } 979 assert.Equal(t, testCase.ExpectedFormations, f) 980 981 mock.AssertExpectationsForObjects(t, persist, service, converter) 982 }) 983 } 984 985 t.Run("Returns error when 'first' is nil", func(t *testing.T) { 986 resolver := formation.NewResolver(nil, nil, nil, nil, nil, nil) 987 988 // WHEN 989 f, err := resolver.Formations(ctx, nil, &after) 990 991 // THEN 992 require.Error(t, err) 993 require.Nil(t, f) 994 }) 995 } 996 997 func TestResolver_FormationAssignment(t *testing.T) { 998 // GIVEN 999 ctx := context.TODO() 1000 1001 testErr := errors.New("test error") 1002 notFoundErr := apperrors.NewNotFoundError(resource.FormationAssignment, FormationAssignmentID) 1003 1004 txGen := txtest.NewTransactionContextGenerator(testErr) 1005 1006 gqlFormation := fixGqlFormation() 1007 gqlFormationAssignment := fixGqlFormationAssignment(FormationAssignmentState, &TestConfigValueStr) 1008 formationAssignmentModel := fixFormationAssignmentModel(FormationAssignmentState, TestConfigValueRawJSON) 1009 1010 testCases := []struct { 1011 Name string 1012 TransactionerFn func() (*persistenceautomock.PersistenceTx, *persistenceautomock.Transactioner) 1013 ServiceFn func() *automock.FormationAssignmentService 1014 ConverterFn func() *automock.FormationAssignmentConverter 1015 Formation *graphql.Formation 1016 FormationAssignmentID string 1017 ExpectedFormationAssignment *graphql.FormationAssignment 1018 ExpectedErrMsg string 1019 }{ 1020 { 1021 Name: "Success", 1022 TransactionerFn: txGen.ThatSucceeds, 1023 ServiceFn: func() *automock.FormationAssignmentService { 1024 faSvc := &automock.FormationAssignmentService{} 1025 faSvc.On("GetForFormation", txtest.CtxWithDBMatcher(), FormationAssignmentID, FormationID).Return(formationAssignmentModel, nil).Once() 1026 return faSvc 1027 }, 1028 ConverterFn: func() *automock.FormationAssignmentConverter { 1029 faConv := &automock.FormationAssignmentConverter{} 1030 faConv.On("ToGraphQL", formationAssignmentModel).Return(gqlFormationAssignment, nil).Once() 1031 return faConv 1032 }, 1033 Formation: gqlFormation, 1034 FormationAssignmentID: FormationAssignmentID, 1035 ExpectedFormationAssignment: gqlFormationAssignment, 1036 }, 1037 { 1038 Name: "Return error when formation object is nil", 1039 TransactionerFn: txGen.ThatDoesntStartTransaction, 1040 ExpectedFormationAssignment: nil, 1041 ExpectedErrMsg: "Formation cannot be empty", 1042 }, 1043 { 1044 Name: "Returns error when commit begin fails", 1045 TransactionerFn: txGen.ThatFailsOnBegin, 1046 Formation: gqlFormation, 1047 ExpectedFormationAssignment: nil, 1048 ExpectedErrMsg: testErr.Error(), 1049 }, 1050 { 1051 Name: "Returns error when formation assignment retrieval failed", 1052 TransactionerFn: txGen.ThatDoesntExpectCommit, 1053 ServiceFn: func() *automock.FormationAssignmentService { 1054 faSvc := &automock.FormationAssignmentService{} 1055 faSvc.On("GetForFormation", txtest.CtxWithDBMatcher(), FormationAssignmentID, FormationID).Return(nil, testErr).Once() 1056 return faSvc 1057 }, 1058 Formation: gqlFormation, 1059 FormationAssignmentID: FormationAssignmentID, 1060 ExpectedFormationAssignment: nil, 1061 ExpectedErrMsg: testErr.Error(), 1062 }, 1063 { 1064 Name: "Returns error when formation assignment for formation is not found", 1065 TransactionerFn: txGen.ThatSucceeds, 1066 ServiceFn: func() *automock.FormationAssignmentService { 1067 faSvc := &automock.FormationAssignmentService{} 1068 faSvc.On("GetForFormation", txtest.CtxWithDBMatcher(), FormationAssignmentID, FormationID).Return(nil, notFoundErr).Once() 1069 return faSvc 1070 }, 1071 Formation: gqlFormation, 1072 FormationAssignmentID: FormationAssignmentID, 1073 ExpectedFormationAssignment: nil, 1074 }, 1075 { 1076 Name: "Returns error when commit failed", 1077 TransactionerFn: txGen.ThatFailsOnCommit, 1078 ServiceFn: func() *automock.FormationAssignmentService { 1079 faSvc := &automock.FormationAssignmentService{} 1080 faSvc.On("GetForFormation", txtest.CtxWithDBMatcher(), FormationAssignmentID, FormationID).Return(formationAssignmentModel, nil).Once() 1081 return faSvc 1082 }, 1083 Formation: gqlFormation, 1084 FormationAssignmentID: FormationAssignmentID, 1085 ExpectedFormationAssignment: nil, 1086 ExpectedErrMsg: testErr.Error(), 1087 }, 1088 { 1089 Name: "Returns error when converting to GraphQL failed", 1090 TransactionerFn: txGen.ThatSucceeds, 1091 ServiceFn: func() *automock.FormationAssignmentService { 1092 faSvc := &automock.FormationAssignmentService{} 1093 faSvc.On("GetForFormation", txtest.CtxWithDBMatcher(), FormationAssignmentID, FormationID).Return(formationAssignmentModel, nil).Once() 1094 return faSvc 1095 }, 1096 ConverterFn: func() *automock.FormationAssignmentConverter { 1097 faConv := &automock.FormationAssignmentConverter{} 1098 faConv.On("ToGraphQL", formationAssignmentModel).Return(nil, testErr).Once() 1099 return faConv 1100 }, 1101 Formation: gqlFormation, 1102 FormationAssignmentID: FormationAssignmentID, 1103 ExpectedFormationAssignment: nil, 1104 ExpectedErrMsg: testErr.Error(), 1105 }, 1106 } 1107 1108 for _, testCase := range testCases { 1109 t.Run(testCase.Name, func(t *testing.T) { 1110 // GIVEN 1111 persist, transact := testCase.TransactionerFn() 1112 1113 faSvc := &automock.FormationAssignmentService{} 1114 if testCase.ServiceFn != nil { 1115 faSvc = testCase.ServiceFn() 1116 } 1117 1118 faConv := &automock.FormationAssignmentConverter{} 1119 if testCase.ConverterFn != nil { 1120 faConv = testCase.ConverterFn() 1121 } 1122 1123 resolver := formation.NewResolver(transact, nil, nil, faSvc, faConv, nil) 1124 1125 // WHEN 1126 fa, err := resolver.FormationAssignment(ctx, testCase.Formation, testCase.FormationAssignmentID) 1127 1128 // THEN 1129 require.Equal(t, testCase.ExpectedFormationAssignment, fa) 1130 if testCase.ExpectedErrMsg != "" { 1131 require.Error(t, err) 1132 require.Contains(t, err.Error(), testCase.ExpectedErrMsg) 1133 } else { 1134 require.NoError(t, err) 1135 } 1136 1137 mock.AssertExpectationsForObjects(t, faSvc, faConv, transact, persist) 1138 }) 1139 } 1140 } 1141 1142 func TestResolver_FormationAssignments(t *testing.T) { 1143 // GIVEN 1144 ctx := context.TODO() 1145 1146 testErr := errors.New("test error") 1147 1148 txGen := txtest.NewTransactionContextGenerator(testErr) 1149 1150 first := 2 1151 gqlAfter := graphql.PageCursor("test") 1152 after := "test" 1153 1154 formationIDs := []string{FormationID, FormationID + "2"} 1155 1156 // Formation Assignments model fixtures 1157 faModelFirst := fixFormationAssignmentModel(FormationAssignmentState, TestConfigValueRawJSON) 1158 faModelSecond := fixFormationAssignmentModelWithSuffix(FormationAssignmentState, TestConfigValueRawJSON, "-2") 1159 1160 fasFirst := []*model.FormationAssignment{faModelFirst} 1161 fasSecond := []*model.FormationAssignment{faModelSecond} 1162 1163 faPageFirst := fixFormationAssignmentPage(fasFirst) 1164 faPageSecond := fixFormationAssignmentPage(fasSecond) 1165 faPages := []*model.FormationAssignmentPage{faPageFirst, faPageSecond} 1166 1167 // Formation Assignments GraphQL fixtures 1168 gqlFormationAssignmentFirst := fixGqlFormationAssignment(FormationAssignmentState, &TestConfigValueStr) 1169 gqlFormationAssignmentSecond := fixGqlFormationAssignmentWithSuffix(FormationAssignmentState, &TestConfigValueStr, "-2") 1170 1171 gqlFAFist := []*graphql.FormationAssignment{gqlFormationAssignmentFirst} 1172 gqlFASecond := []*graphql.FormationAssignment{gqlFormationAssignmentSecond} 1173 1174 gqlFAPageFirst := fixGQLFormationAssignmentPage(gqlFAFist) 1175 gqlFAPageSecond := fixGQLFormationAssignmentPage(gqlFASecond) 1176 gqlFAPages := []*graphql.FormationAssignmentPage{gqlFAPageFirst, gqlFAPageSecond} 1177 1178 testCases := []struct { 1179 Name string 1180 TransactionerFn func() (*persistenceautomock.PersistenceTx, *persistenceautomock.Transactioner) 1181 ServiceFn func() *automock.FormationAssignmentService 1182 ConverterFn func() *automock.FormationAssignmentConverter 1183 ExpectedResult []*graphql.FormationAssignmentPage 1184 ExpectedErr []error 1185 }{ 1186 { 1187 Name: "Success", 1188 TransactionerFn: txGen.ThatSucceeds, 1189 ServiceFn: func() *automock.FormationAssignmentService { 1190 faSvc := &automock.FormationAssignmentService{} 1191 faSvc.On("ListByFormationIDs", txtest.CtxWithDBMatcher(), formationIDs, first, after).Return(faPages, nil).Once() 1192 return faSvc 1193 }, 1194 ConverterFn: func() *automock.FormationAssignmentConverter { 1195 faConv := &automock.FormationAssignmentConverter{} 1196 faConv.On("MultipleToGraphQL", fasFirst).Return(gqlFAFist, nil).Once() 1197 faConv.On("MultipleToGraphQL", fasSecond).Return(gqlFASecond, nil).Once() 1198 return faConv 1199 }, 1200 ExpectedResult: gqlFAPages, 1201 ExpectedErr: nil, 1202 }, 1203 { 1204 Name: "Returns error when transaction begin failed", 1205 TransactionerFn: txGen.ThatFailsOnBegin, 1206 ExpectedResult: nil, 1207 ExpectedErr: []error{testErr}, 1208 }, 1209 { 1210 Name: "Returns error when listing formations", 1211 TransactionerFn: txGen.ThatDoesntExpectCommit, 1212 ServiceFn: func() *automock.FormationAssignmentService { 1213 faSvc := &automock.FormationAssignmentService{} 1214 faSvc.On("ListByFormationIDs", txtest.CtxWithDBMatcher(), formationIDs, first, after).Return(nil, testErr).Once() 1215 return faSvc 1216 }, 1217 ExpectedResult: nil, 1218 ExpectedErr: []error{testErr}, 1219 }, 1220 { 1221 Name: "Returns error when transaction commit failed", 1222 TransactionerFn: txGen.ThatFailsOnCommit, 1223 ServiceFn: func() *automock.FormationAssignmentService { 1224 faSvc := &automock.FormationAssignmentService{} 1225 faSvc.On("ListByFormationIDs", txtest.CtxWithDBMatcher(), formationIDs, first, after).Return(faPages, nil).Once() 1226 return faSvc 1227 }, 1228 ConverterFn: func() *automock.FormationAssignmentConverter { 1229 faConv := &automock.FormationAssignmentConverter{} 1230 faConv.On("MultipleToGraphQL", fasFirst).Return(gqlFAFist, nil).Once() 1231 faConv.On("MultipleToGraphQL", fasSecond).Return(gqlFASecond, nil).Once() 1232 return faConv 1233 }, 1234 ExpectedResult: nil, 1235 ExpectedErr: []error{testErr}, 1236 }, 1237 { 1238 Name: "Returns error when converting to GraphQL failed", 1239 TransactionerFn: txGen.ThatDoesntExpectCommit, 1240 ServiceFn: func() *automock.FormationAssignmentService { 1241 faSvc := &automock.FormationAssignmentService{} 1242 faSvc.On("ListByFormationIDs", txtest.CtxWithDBMatcher(), formationIDs, first, after).Return(faPages, nil).Once() 1243 return faSvc 1244 }, 1245 ConverterFn: func() *automock.FormationAssignmentConverter { 1246 faConv := &automock.FormationAssignmentConverter{} 1247 faConv.On("MultipleToGraphQL", fasFirst).Return(nil, testErr).Once() 1248 return faConv 1249 }, 1250 ExpectedResult: nil, 1251 ExpectedErr: []error{testErr}, 1252 }, 1253 } 1254 1255 for _, testCase := range testCases { 1256 t.Run(testCase.Name, func(t *testing.T) { 1257 // GIVEN 1258 persist, transact := testCase.TransactionerFn() 1259 1260 faSvc := &automock.FormationAssignmentService{} 1261 if testCase.ServiceFn != nil { 1262 faSvc = testCase.ServiceFn() 1263 } 1264 1265 faConv := &automock.FormationAssignmentConverter{} 1266 if testCase.ConverterFn != nil { 1267 faConv = testCase.ConverterFn() 1268 } 1269 1270 resolver := formation.NewResolver(transact, nil, nil, faSvc, faConv, nil) 1271 firstFormationAssignmentParams := dataloader.ParamFormationAssignment{ID: FormationID, Ctx: ctx, First: &first, After: &gqlAfter} 1272 secondFormationAssignmentParams := dataloader.ParamFormationAssignment{ID: FormationID + "2", Ctx: ctx, First: &first, After: &gqlAfter} 1273 keys := []dataloader.ParamFormationAssignment{firstFormationAssignmentParams, secondFormationAssignmentParams} 1274 1275 // WHEN 1276 result, err := resolver.FormationAssignmentsDataLoader(keys) 1277 1278 // THEN 1279 require.Equal(t, testCase.ExpectedResult, result) 1280 require.Equal(t, testCase.ExpectedErr, err) 1281 1282 mock.AssertExpectationsForObjects(t, faSvc, faConv, transact, persist) 1283 }) 1284 } 1285 1286 t.Run("Returns error when there are no formations IDs", func(t *testing.T) { 1287 resolver := formation.NewResolver(nil, nil, nil, nil, nil, nil) 1288 1289 // WHEN 1290 _, errs := resolver.FormationAssignmentsDataLoader([]dataloader.ParamFormationAssignment{}) 1291 1292 // THEN 1293 require.Error(t, errs[0]) 1294 require.EqualError(t, errs[0], apperrors.NewInternalError("No Formations found").Error()) 1295 }) 1296 1297 t.Run("Returns error when start cursor is nil", func(t *testing.T) { 1298 firstFormationAssignmentParams := dataloader.ParamFormationAssignment{ID: FormationID, Ctx: ctx, First: nil, After: &gqlAfter} 1299 keys := []dataloader.ParamFormationAssignment{firstFormationAssignmentParams} 1300 1301 resolver := formation.NewResolver(nil, nil, nil, nil, nil, nil) 1302 1303 // WHEN 1304 _, errs := resolver.FormationAssignmentsDataLoader(keys) 1305 1306 // THEN 1307 require.Error(t, errs[0]) 1308 require.EqualError(t, errs[0], apperrors.NewInvalidDataError("missing required parameter 'first'").Error()) 1309 }) 1310 } 1311 1312 func TestResolver_Status(t *testing.T) { 1313 // GIVEN 1314 ctx := context.TODO() 1315 1316 testErr := errors.New("test error") 1317 1318 txGen := txtest.NewTransactionContextGenerator(testErr) 1319 1320 formationIDs := []string{FormationID, FormationID + "2", FormationID + "3", FormationID + "4"} 1321 1322 // Formation Assignments model fixtures 1323 faModelReady := fixFormationAssignmentModel(string(model.ReadyAssignmentState), TestConfigValueRawJSON) 1324 faModelInitial := fixFormationAssignmentModelWithSuffix(string(model.InitialAssignmentState), TestConfigValueRawJSON, "-2") 1325 faModelError := fixFormationAssignmentModelWithSuffix(string(model.CreateErrorAssignmentState), json.RawMessage(`{"error": {"message": "failure", "errorCode": 1}}`), "-3") 1326 faModelEmptyError := fixFormationAssignmentModelWithSuffix(string(model.CreateErrorAssignmentState), nil, "-4") 1327 1328 fasReady := []*model.FormationAssignment{faModelReady} // all are READY -> READY condition 1329 fasInProgress := []*model.FormationAssignment{faModelInitial, faModelReady} // no errors, but one is INITIAL -> IN_PROGRESS condition 1330 fasError := []*model.FormationAssignment{faModelReady, faModelError, faModelInitial} // have error -> ERROR condition 1331 fasEmptyError := []*model.FormationAssignment{faModelReady, faModelEmptyError, faModelInitial} // should handle empty Value and have ERROR condition 1332 1333 fasPerFormation := [][]*model.FormationAssignment{fasReady, fasInProgress, fasError, fasEmptyError} 1334 1335 fasUnmarshallable := []*model.FormationAssignment{fixFormationAssignmentModelWithSuffix(string(model.DeleteErrorAssignmentState), json.RawMessage(`unmarshallable structure`), "-4")} 1336 1337 faPagesWithUnmarshallableError := [][]*model.FormationAssignment{fasUnmarshallable} 1338 1339 // Formation Assignments GraphQL fixtures 1340 1341 gqlStatusFirst := graphql.FormationStatus{Condition: graphql.FormationStatusConditionReady, Errors: nil} 1342 gqlStatusSecond := graphql.FormationStatus{Condition: graphql.FormationStatusConditionInProgress, Errors: nil} 1343 gqlStatusThird := graphql.FormationStatus{ 1344 Condition: graphql.FormationStatusConditionError, 1345 Errors: []*graphql.FormationStatusError{{ 1346 AssignmentID: addSuffix(FormationAssignmentID, "-3"), 1347 Message: "failure", 1348 ErrorCode: 1, 1349 }}, 1350 } 1351 gqlStatusFourth := graphql.FormationStatus{ 1352 Condition: graphql.FormationStatusConditionError, 1353 Errors: []*graphql.FormationStatusError{{ 1354 AssignmentID: addSuffix(FormationAssignmentID, "-4"), 1355 }}, 1356 } 1357 1358 gqlStatuses := []*graphql.FormationStatus{&gqlStatusFirst, &gqlStatusSecond, &gqlStatusThird, &gqlStatusFourth} 1359 1360 emptyFaPage := [][]*model.FormationAssignment{nil, nil, nil, nil} 1361 1362 testCases := []struct { 1363 Name string 1364 TransactionerFn func() (*persistenceautomock.PersistenceTx, *persistenceautomock.Transactioner) 1365 ServiceFn func() *automock.FormationAssignmentService 1366 Params []dataloader.ParamFormationStatus 1367 ExpectedResult []*graphql.FormationStatus 1368 ExpectedErr error 1369 }{ 1370 { 1371 Name: "Success", 1372 TransactionerFn: txGen.ThatSucceeds, 1373 ServiceFn: func() *automock.FormationAssignmentService { 1374 faSvc := &automock.FormationAssignmentService{} 1375 faSvc.On("ListByFormationIDsNoPaging", txtest.CtxWithDBMatcher(), formationIDs).Return(fasPerFormation, nil).Once() 1376 return faSvc 1377 }, 1378 Params: []dataloader.ParamFormationStatus{firstFormationStatusParams, secondFormationStatusParams, thirdFormationStatusParams, fourthPageFormations}, 1379 ExpectedResult: gqlStatuses, 1380 ExpectedErr: nil, 1381 }, 1382 { 1383 Name: "Success when there are no formation assignments", 1384 TransactionerFn: txGen.ThatSucceeds, 1385 ServiceFn: func() *automock.FormationAssignmentService { 1386 faSvc := &automock.FormationAssignmentService{} 1387 faSvc.On("ListByFormationIDsNoPaging", txtest.CtxWithDBMatcher(), []string{FormationID}).Return(emptyFaPage, nil).Once() 1388 return faSvc 1389 }, 1390 Params: []dataloader.ParamFormationStatus{firstFormationStatusParams}, 1391 ExpectedResult: []*graphql.FormationStatus{&gqlStatusFirst}, 1392 ExpectedErr: nil, 1393 }, 1394 { 1395 Name: "Success when there are no FAs and the formation is in error state", 1396 TransactionerFn: txGen.ThatSucceeds, 1397 ServiceFn: func() *automock.FormationAssignmentService { 1398 faSvc := &automock.FormationAssignmentService{} 1399 faSvc.On("ListByFormationIDsNoPaging", txtest.CtxWithDBMatcher(), []string{FormationID}).Return(emptyFaPage, nil).Once() 1400 return faSvc 1401 }, 1402 Params: []dataloader.ParamFormationStatus{{ID: FormationID, State: string(model.CreateErrorFormationState), Message: "failure", ErrorCode: 1}}, 1403 ExpectedResult: []*graphql.FormationStatus{{ 1404 Condition: graphql.FormationStatusConditionError, 1405 Errors: []*graphql.FormationStatusError{{ 1406 Message: "failure", 1407 ErrorCode: 1, 1408 }}, 1409 }}, 1410 ExpectedErr: nil, 1411 }, 1412 { 1413 Name: "Returns error when transaction begin failed", 1414 TransactionerFn: txGen.ThatFailsOnBegin, 1415 Params: []dataloader.ParamFormationStatus{firstFormationStatusParams, secondFormationStatusParams, thirdFormationStatusParams, fourthPageFormations}, 1416 ExpectedResult: nil, 1417 ExpectedErr: testErr, 1418 }, 1419 { 1420 Name: "Returns error when listing formations", 1421 TransactionerFn: txGen.ThatDoesntExpectCommit, 1422 ServiceFn: func() *automock.FormationAssignmentService { 1423 faSvc := &automock.FormationAssignmentService{} 1424 faSvc.On("ListByFormationIDsNoPaging", txtest.CtxWithDBMatcher(), formationIDs).Return(nil, testErr).Once() 1425 return faSvc 1426 }, 1427 Params: []dataloader.ParamFormationStatus{firstFormationStatusParams, secondFormationStatusParams, thirdFormationStatusParams, fourthPageFormations}, 1428 ExpectedResult: nil, 1429 ExpectedErr: testErr, 1430 }, 1431 { 1432 Name: "Returns error when can't unmarshal assignment value", 1433 TransactionerFn: txGen.ThatDoesntExpectCommit, 1434 ServiceFn: func() *automock.FormationAssignmentService { 1435 faSvc := &automock.FormationAssignmentService{} 1436 faSvc.On("ListByFormationIDsNoPaging", txtest.CtxWithDBMatcher(), formationIDs).Return(faPagesWithUnmarshallableError, nil).Once() 1437 return faSvc 1438 }, 1439 Params: []dataloader.ParamFormationStatus{firstFormationStatusParams, secondFormationStatusParams, thirdFormationStatusParams, fourthPageFormations}, 1440 ExpectedResult: nil, 1441 ExpectedErr: errors.New("while unmarshalling formation assignment error with assignment ID \"FormationAssignmentID-4\""), 1442 }, 1443 { 1444 Name: "Returns error when transaction commit failed", 1445 TransactionerFn: txGen.ThatFailsOnCommit, 1446 ServiceFn: func() *automock.FormationAssignmentService { 1447 faSvc := &automock.FormationAssignmentService{} 1448 faSvc.On("ListByFormationIDsNoPaging", txtest.CtxWithDBMatcher(), formationIDs).Return(fasPerFormation, nil).Once() 1449 return faSvc 1450 }, 1451 Params: []dataloader.ParamFormationStatus{firstFormationStatusParams, secondFormationStatusParams, thirdFormationStatusParams, fourthPageFormations}, 1452 ExpectedResult: nil, 1453 ExpectedErr: testErr, 1454 }, 1455 } 1456 1457 for _, testCase := range testCases { 1458 t.Run(testCase.Name, func(t *testing.T) { 1459 // GIVEN 1460 persist, transact := testCase.TransactionerFn() 1461 1462 faSvc := &automock.FormationAssignmentService{} 1463 if testCase.ServiceFn != nil { 1464 faSvc = testCase.ServiceFn() 1465 } 1466 1467 resolver := formation.NewResolver(transact, nil, nil, faSvc, nil, nil) 1468 1469 params := make([]dataloader.ParamFormationStatus, 0, len(testCase.Params)) 1470 for _, param := range testCase.Params { 1471 param.Ctx = ctx 1472 params = append(params, param) 1473 } 1474 1475 // WHEN 1476 result, errs := resolver.StatusDataLoader(params) 1477 1478 // THEN 1479 require.EqualValues(t, testCase.ExpectedResult, result) 1480 if errs != nil { 1481 require.Contains(t, errs[0].Error(), testCase.ExpectedErr.Error()) 1482 } 1483 1484 mock.AssertExpectationsForObjects(t, faSvc, transact, persist) 1485 }) 1486 } 1487 1488 t.Run("Returns error when there are no formations IDs", func(t *testing.T) { 1489 resolver := formation.NewResolver(nil, nil, nil, nil, nil, nil) 1490 1491 // WHEN 1492 _, errs := resolver.StatusDataLoader([]dataloader.ParamFormationStatus{}) 1493 1494 // THEN 1495 require.Error(t, errs[0]) 1496 require.EqualError(t, errs[0], apperrors.NewInternalError("No Formations found").Error()) 1497 }) 1498 } 1499 1500 func TestResynchronizeFormationNotifications(t *testing.T) { 1501 txGen := txtest.NewTransactionContextGenerator(testErr) 1502 1503 ctx := tenant.SaveToContext(context.TODO(), TntInternalID, TntExternalID) 1504 1505 testCases := []struct { 1506 Name string 1507 Context context.Context 1508 TxFn func() (*persistenceautomock.PersistenceTx, *persistenceautomock.Transactioner) 1509 FormationService func() *automock.Service 1510 Converter func() *automock.Converter 1511 ExpectedFormation *graphql.Formation 1512 ExpectedErrorMsg string 1513 }{ 1514 { 1515 Name: "successfully resynchronized formation notifications", 1516 TxFn: txGen.ThatSucceeds, 1517 FormationService: func() *automock.Service { 1518 svc := &automock.Service{} 1519 1520 svc.On("ResynchronizeFormationNotifications", contextThatHasTenant(TntInternalID), FormationID).Return(&modelFormation, nil).Once() 1521 1522 return svc 1523 }, 1524 Converter: func() *automock.Converter { 1525 conv := &automock.Converter{} 1526 conv.On("ToGraphQL", &modelFormation).Return(&graphqlFormation, nil).Once() 1527 return conv 1528 }, 1529 ExpectedFormation: &graphqlFormation, 1530 }, 1531 { 1532 Name: "failed during resynchronizing", 1533 TxFn: txGen.ThatDoesntExpectCommit, 1534 FormationService: func() *automock.Service { 1535 svc := &automock.Service{} 1536 1537 svc.On("ResynchronizeFormationNotifications", contextThatHasTenant(TntInternalID), FormationID).Return(nil, testErr) 1538 1539 return svc 1540 }, 1541 ExpectedErrorMsg: testErr.Error(), 1542 }, 1543 { 1544 Name: "failed to commit after resynchronizing", 1545 TxFn: txGen.ThatFailsOnCommit, 1546 FormationService: func() *automock.Service { 1547 svc := &automock.Service{} 1548 1549 svc.On("ResynchronizeFormationNotifications", contextThatHasTenant(TntInternalID), FormationID).Return(&modelFormation, nil).Once() 1550 1551 return svc 1552 }, 1553 ExpectedErrorMsg: testErr.Error(), 1554 }, 1555 { 1556 Name: "returns error when can not start db transaction", 1557 TxFn: txGen.ThatFailsOnBegin, 1558 ExpectedErrorMsg: testErr.Error(), 1559 }, 1560 } 1561 for _, testCase := range testCases { 1562 t.Run(testCase.Name, func(t *testing.T) { 1563 conv := unusedConverter() 1564 if testCase.Converter != nil { 1565 conv = testCase.Converter() 1566 } 1567 formationService := unusedService() 1568 if testCase.FormationService != nil { 1569 formationService = testCase.FormationService() 1570 } 1571 persist, transact := testCase.TxFn() 1572 1573 resolver := formation.NewResolver(transact, formationService, conv, nil, nil, nil) 1574 1575 // WHEN 1576 resultFormationModel, err := resolver.ResynchronizeFormationNotifications(ctx, FormationID) 1577 1578 if testCase.ExpectedErrorMsg != "" { 1579 require.Error(t, err) 1580 require.Contains(t, err.Error(), testCase.ExpectedErrorMsg) 1581 require.Nil(t, resultFormationModel) 1582 } else { 1583 require.NoError(t, err) 1584 require.Equal(t, testCase.ExpectedFormation, resultFormationModel) 1585 } 1586 mock.AssertExpectationsForObjects(t, conv, formationService, persist, transact) 1587 }) 1588 } 1589 } 1590 1591 func contextThatHasTenant(expectedTenant string) interface{} { 1592 return mock.MatchedBy(func(actual context.Context) bool { 1593 actualTenant, err := tenant.LoadFromContext(actual) 1594 if err != nil { 1595 return false 1596 } 1597 return actualTenant == expectedTenant 1598 }) 1599 } 1600 1601 func addSuffix(str, suffix string) *string { 1602 res := str + suffix 1603 return &res 1604 }