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

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