github.com/kyma-incubator/compass/components/director@v0.0.0-20230623144113-d764f56ff805/internal/domain/formationassignment/service_test.go (about)

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