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

     1  package formation_test
     2  
     3  import (
     4  	"context"
     5  	"testing"
     6  
     7  	"github.com/kyma-incubator/compass/components/director/internal/domain/formation"
     8  	"github.com/kyma-incubator/compass/components/director/internal/domain/formation/automock"
     9  	"github.com/kyma-incubator/compass/components/director/internal/domain/tenant"
    10  	"github.com/kyma-incubator/compass/components/director/internal/model"
    11  	"github.com/kyma-incubator/compass/components/director/pkg/formationconstraint"
    12  	"github.com/kyma-incubator/compass/components/director/pkg/graphql"
    13  	webhookdir "github.com/kyma-incubator/compass/components/director/pkg/webhook"
    14  	webhookclient "github.com/kyma-incubator/compass/components/director/pkg/webhook_client"
    15  	"github.com/stretchr/testify/assert"
    16  	"github.com/stretchr/testify/mock"
    17  	"github.com/stretchr/testify/require"
    18  )
    19  
    20  func TestBuildFormationAssignmentNotificationRequest(t *testing.T) {
    21  	ctx := context.TODO()
    22  	ctx = tenant.SaveToContext(ctx, TntInternalID, TntExternalID)
    23  
    24  	ApplicationID := "04f3568d-3e0c-4f6b-b646-e6979e9d060c"
    25  	webhook := fixConfigurationChangedWebhookModel(WebhookID, ApplicationID, model.ApplicationWebhookReference)
    26  	gqlWebhook := &graphql.Webhook{ID: WebhookID, ApplicationID: &ApplicationID, Type: graphql.WebhookTypeConfigurationChanged}
    27  
    28  	applicationTenantMappingWebhook := fixApplicationTenantMappingWebhookModel(AppTenantMappingWebhookIDForApp1, ApplicationID)
    29  	gqlApplicationTenantMappingWebhook := &graphql.Webhook{ID: AppTenantMappingWebhookIDForApp1, ApplicationID: &ApplicationID, Type: graphql.WebhookTypeApplicationTenantMapping}
    30  
    31  	testCases := []struct {
    32  		Name                 string
    33  		WebhookConverter     func() *automock.WebhookConverter
    34  		ConstraintEngineFn   func() *automock.ConstraintEngine
    35  		Details              *formationconstraint.GenerateFormationAssignmentNotificationOperationDetails
    36  		Webhook              *model.Webhook
    37  		ExpectedNotification *webhookclient.FormationAssignmentNotificationRequest
    38  		ExpectedErrMessage   string
    39  	}{
    40  		{
    41  			Name: "success for configuration changed webhook",
    42  			WebhookConverter: func() *automock.WebhookConverter {
    43  				conv := &automock.WebhookConverter{}
    44  				conv.On("ToGraphQL", webhook).Return(gqlWebhook, nil).Once()
    45  				return conv
    46  			},
    47  			ConstraintEngineFn: func() *automock.ConstraintEngine {
    48  				engine := &automock.ConstraintEngine{}
    49  				engine.On("EnforceConstraints", ctx, preGenerateFormationAssignmentNotificationLocation, generateConfigurationChangeNotificationDetails, FormationTemplateID).Return(nil).Once()
    50  				engine.On("EnforceConstraints", ctx, postGenerateFormationAssignmentNotificationLocation, generateConfigurationChangeNotificationDetails, FormationTemplateID).Return(nil).Once()
    51  				return engine
    52  			},
    53  			Details:              generateConfigurationChangeNotificationDetails,
    54  			Webhook:              webhook,
    55  			ExpectedNotification: applicationNotificationWithAppTemplate,
    56  			ExpectedErrMessage:   "",
    57  		},
    58  		{
    59  			Name: "success for application tenant mapping webhook",
    60  			WebhookConverter: func() *automock.WebhookConverter {
    61  				conv := &automock.WebhookConverter{}
    62  				conv.On("ToGraphQL", applicationTenantMappingWebhook).Return(gqlApplicationTenantMappingWebhook, nil).Once()
    63  				return conv
    64  			},
    65  			ConstraintEngineFn: func() *automock.ConstraintEngine {
    66  				engine := &automock.ConstraintEngine{}
    67  				engine.On("EnforceConstraints", ctx, preGenerateFormationAssignmentNotificationLocation, generateAppToAppNotificationDetails, FormationTemplateID).Return(nil).Once()
    68  				engine.On("EnforceConstraints", ctx, postGenerateFormationAssignmentNotificationLocation, generateAppToAppNotificationDetails, FormationTemplateID).Return(nil).Once()
    69  				return engine
    70  			},
    71  			Details:              generateAppToAppNotificationDetails,
    72  			Webhook:              applicationTenantMappingWebhook,
    73  			ExpectedNotification: appToAppNotificationWithoutSourceTemplateWithTargetTemplate,
    74  			ExpectedErrMessage:   "",
    75  		},
    76  		{
    77  			Name: "error while enforcing constraints pre operation",
    78  			ConstraintEngineFn: func() *automock.ConstraintEngine {
    79  				engine := &automock.ConstraintEngine{}
    80  				engine.On("EnforceConstraints", ctx, preGenerateFormationAssignmentNotificationLocation, generateConfigurationChangeNotificationDetails, FormationTemplateID).Return(testErr).Once()
    81  				return engine
    82  			},
    83  			Details: generateConfigurationChangeNotificationDetails,
    84  			Webhook: webhook,
    85  		},
    86  		{
    87  			Name: "error when webhook type is not supported",
    88  			ConstraintEngineFn: func() *automock.ConstraintEngine {
    89  				engine := &automock.ConstraintEngine{}
    90  				engine.On("EnforceConstraints", ctx, preGenerateFormationAssignmentNotificationLocation, generateConfigurationChangeNotificationDetails, FormationTemplateID).Return(nil).Once()
    91  				return engine
    92  			},
    93  			Details:            generateConfigurationChangeNotificationDetails,
    94  			Webhook:            &model.Webhook{Type: model.WebhookTypeRegisterApplication},
    95  			ExpectedErrMessage: "Unsupported Webhook Type",
    96  		},
    97  		{
    98  			Name: "error while converting webhook",
    99  			WebhookConverter: func() *automock.WebhookConverter {
   100  				conv := &automock.WebhookConverter{}
   101  				conv.On("ToGraphQL", webhook).Return(nil, testErr).Once()
   102  				return conv
   103  			},
   104  			ConstraintEngineFn: func() *automock.ConstraintEngine {
   105  				engine := &automock.ConstraintEngine{}
   106  				engine.On("EnforceConstraints", ctx, preGenerateFormationAssignmentNotificationLocation, generateConfigurationChangeNotificationDetails, FormationTemplateID).Return(nil).Once()
   107  				return engine
   108  			},
   109  			Details:            generateConfigurationChangeNotificationDetails,
   110  			Webhook:            webhook,
   111  			ExpectedErrMessage: "while converting webhook with ID",
   112  		},
   113  		{
   114  			Name: "error while enforcing constraints post operation",
   115  			WebhookConverter: func() *automock.WebhookConverter {
   116  				conv := &automock.WebhookConverter{}
   117  				conv.On("ToGraphQL", webhook).Return(gqlWebhook, nil).Once()
   118  				return conv
   119  			},
   120  			ConstraintEngineFn: func() *automock.ConstraintEngine {
   121  				engine := &automock.ConstraintEngine{}
   122  				engine.On("EnforceConstraints", ctx, preGenerateFormationAssignmentNotificationLocation, generateConfigurationChangeNotificationDetails, FormationTemplateID).Return(nil).Once()
   123  				engine.On("EnforceConstraints", ctx, postGenerateFormationAssignmentNotificationLocation, generateConfigurationChangeNotificationDetails, FormationTemplateID).Return(testErr).Once()
   124  				return engine
   125  			},
   126  			Details: generateConfigurationChangeNotificationDetails,
   127  			Webhook: webhook,
   128  		},
   129  	}
   130  
   131  	for _, testCase := range testCases {
   132  		t.Run(testCase.Name, func(t *testing.T) {
   133  			// GIVEN
   134  			webhookConverter := unusedWebhookConverter()
   135  			if testCase.WebhookConverter != nil {
   136  				webhookConverter = testCase.WebhookConverter()
   137  			}
   138  			constraintEngine := unusedConstraintEngine()
   139  			if testCase.ConstraintEngineFn != nil {
   140  				constraintEngine = testCase.ConstraintEngineFn()
   141  			}
   142  
   143  			builder := formation.NewNotificationsBuilder(webhookConverter, constraintEngine, runtimeType, applicationType)
   144  
   145  			// WHEN
   146  			actual, err := builder.BuildFormationAssignmentNotificationRequest(ctx, FormationTemplateID, testCase.Details, testCase.Webhook)
   147  
   148  			// THEN
   149  			if testCase.ExpectedErrMessage == "" {
   150  				require.NoError(t, err)
   151  				assert.Equal(t, testCase.ExpectedNotification, actual)
   152  			} else {
   153  				require.Error(t, err)
   154  				require.Contains(t, err.Error(), testCase.ExpectedErrMessage)
   155  				require.Nil(t, actual)
   156  			}
   157  
   158  			mock.AssertExpectationsForObjects(t, webhookConverter, constraintEngine)
   159  		})
   160  	}
   161  }
   162  
   163  func TestBuildFormationNotificationRequests(t *testing.T) {
   164  	ctx := context.Background()
   165  	formationLifecycleGQLWebhook := fixFormationLifecycleWebhookGQLModel(FormationLifecycleWebhookID, FormationTemplateID, graphql.WebhookModeSync)
   166  	formationInput := fixFormationModelWithoutError()
   167  
   168  	testCases := []struct {
   169  		name                              string
   170  		constraintEngineFn                func() *automock.ConstraintEngine
   171  		webhookConverterFn                func() *automock.WebhookConverter
   172  		formationTemplateWebhooks         []*model.Webhook
   173  		expectedErrMsg                    string
   174  		expectedFormationNotificationReqs []*webhookclient.FormationNotificationRequest
   175  	}{
   176  		{
   177  			name: "Successfully build formation notification requests",
   178  			constraintEngineFn: func() *automock.ConstraintEngine {
   179  				engine := &automock.ConstraintEngine{}
   180  				engine.On("EnforceConstraints", ctx, preGenerateFormationNotificationLocation, formationNotificationDetails, FormationTemplateID).Return(nil).Once()
   181  				engine.On("EnforceConstraints", ctx, postGenerateFormationNotificationLocation, formationNotificationDetails, FormationTemplateID).Return(nil).Once()
   182  				return engine
   183  			},
   184  			webhookConverterFn: func() *automock.WebhookConverter {
   185  				webhookConv := &automock.WebhookConverter{}
   186  				webhookConv.On("ToGraphQL", formationLifecycleSyncWebhook).Return(&formationLifecycleGQLWebhook, nil).Once()
   187  				return webhookConv
   188  			},
   189  			formationTemplateWebhooks:         formationLifecycleSyncWebhooks,
   190  			expectedFormationNotificationReqs: formationNotificationSyncCreateRequests,
   191  		},
   192  		{
   193  			name: "Error when enforcing pre generate formation notification constraints",
   194  			constraintEngineFn: func() *automock.ConstraintEngine {
   195  				engine := &automock.ConstraintEngine{}
   196  				engine.On("EnforceConstraints", ctx, preGenerateFormationNotificationLocation, formationNotificationDetails, FormationTemplateID).Return(testErr).Once()
   197  				return engine
   198  			},
   199  			formationTemplateWebhooks: formationLifecycleSyncWebhooks,
   200  			expectedErrMsg:            testErr.Error(),
   201  		},
   202  		{
   203  			name: "Success when there are no formation template webhooks",
   204  			constraintEngineFn: func() *automock.ConstraintEngine {
   205  				engine := &automock.ConstraintEngine{}
   206  				engine.On("EnforceConstraints", ctx, preGenerateFormationNotificationLocation, formationNotificationDetails, FormationTemplateID).Return(nil).Once()
   207  				return engine
   208  			},
   209  			formationTemplateWebhooks: emptyFormationLifecycleWebhooks,
   210  		},
   211  		{
   212  			name: "Error when converting formation template webhook to graphql one",
   213  			constraintEngineFn: func() *automock.ConstraintEngine {
   214  				engine := &automock.ConstraintEngine{}
   215  				engine.On("EnforceConstraints", ctx, preGenerateFormationNotificationLocation, formationNotificationDetails, FormationTemplateID).Return(nil).Once()
   216  				return engine
   217  			},
   218  			webhookConverterFn: func() *automock.WebhookConverter {
   219  				webhookConv := &automock.WebhookConverter{}
   220  				webhookConv.On("ToGraphQL", formationLifecycleSyncWebhook).Return(nil, testErr).Once()
   221  				return webhookConv
   222  			},
   223  			formationTemplateWebhooks: formationLifecycleSyncWebhooks,
   224  			expectedErrMsg:            testErr.Error(),
   225  		},
   226  		{
   227  			name: "Error when enforcing post generate formation notification constraints",
   228  			constraintEngineFn: func() *automock.ConstraintEngine {
   229  				engine := &automock.ConstraintEngine{}
   230  				engine.On("EnforceConstraints", ctx, preGenerateFormationNotificationLocation, formationNotificationDetails, FormationTemplateID).Return(nil).Once()
   231  				engine.On("EnforceConstraints", ctx, postGenerateFormationNotificationLocation, formationNotificationDetails, FormationTemplateID).Return(testErr).Once()
   232  				return engine
   233  			},
   234  			webhookConverterFn: func() *automock.WebhookConverter {
   235  				webhookConv := &automock.WebhookConverter{}
   236  				webhookConv.On("ToGraphQL", formationLifecycleSyncWebhook).Return(&formationLifecycleGQLWebhook, nil).Once()
   237  				return webhookConv
   238  			},
   239  			formationTemplateWebhooks: formationLifecycleSyncWebhooks,
   240  			expectedErrMsg:            testErr.Error(),
   241  		},
   242  	}
   243  
   244  	for _, testCase := range testCases {
   245  		t.Run(testCase.name, func(t *testing.T) {
   246  			constraintEngine := unusedConstraintEngine()
   247  			if testCase.constraintEngineFn != nil {
   248  				constraintEngine = testCase.constraintEngineFn()
   249  			}
   250  
   251  			webhookConv := unusedWebhookConverter()
   252  			if testCase.webhookConverterFn != nil {
   253  				webhookConv = testCase.webhookConverterFn()
   254  			}
   255  
   256  			defer mock.AssertExpectationsForObjects(t, constraintEngine, webhookConv)
   257  
   258  			builder := formation.NewNotificationsBuilder(webhookConv, constraintEngine, runtimeType, applicationType)
   259  			formationNotificationReqs, err := builder.BuildFormationNotificationRequests(ctx, formationNotificationDetails, formationInput, testCase.formationTemplateWebhooks)
   260  
   261  			if testCase.expectedErrMsg != "" {
   262  				require.Error(t, err)
   263  				require.Contains(t, err.Error(), testCase.expectedErrMsg)
   264  				require.Empty(t, formationNotificationReqs)
   265  			} else {
   266  				require.NoError(t, err)
   267  				require.ElementsMatch(t, formationNotificationReqs, testCase.expectedFormationNotificationReqs)
   268  			}
   269  		})
   270  	}
   271  }
   272  
   273  func TestNotificationBuilder_PrepareDetailsForConfigurationChangeNotificationGeneration(t *testing.T) {
   274  	applicationTemplate := &webhookdir.ApplicationTemplateWithLabels{
   275  		ApplicationTemplate: fixApplicationTemplateModel(),
   276  		Labels:              fixApplicationTemplateLabelsMap(),
   277  	}
   278  
   279  	application := &webhookdir.ApplicationWithLabels{
   280  		Application: fixApplicationModel(ApplicationID),
   281  		Labels: map[string]string{
   282  			applicationType: applicationType,
   283  		},
   284  	}
   285  
   286  	runtime := &webhookdir.RuntimeWithLabels{
   287  		Runtime: fixRuntimeModel(RuntimeContextRuntimeID),
   288  		Labels: map[string]string{
   289  			runtimeType: runtimeType,
   290  		},
   291  	}
   292  
   293  	applicationWithoutType := &webhookdir.ApplicationWithLabels{
   294  		Application: fixApplicationModel(ApplicationID),
   295  		Labels:      map[string]string{},
   296  	}
   297  
   298  	runtimeWithoutType := &webhookdir.RuntimeWithLabels{
   299  		Runtime: fixRuntimeModel(RuntimeContextRuntimeID),
   300  		Labels:  map[string]string{},
   301  	}
   302  
   303  	runtimeContext := &webhookdir.RuntimeContextWithLabels{
   304  		RuntimeContext: fixRuntimeContextModel(),
   305  		Labels:         fixRuntimeContextLabelsMap(),
   306  	}
   307  
   308  	testCases := []struct {
   309  		Name                        string
   310  		Operation                   model.FormationOperation
   311  		FormationID                 string
   312  		ApplicationTemplate         *webhookdir.ApplicationTemplateWithLabels
   313  		Application                 *webhookdir.ApplicationWithLabels
   314  		Runtime                     *webhookdir.RuntimeWithLabels
   315  		RuntimeContext              *webhookdir.RuntimeContextWithLabels
   316  		Assignment                  *webhookdir.FormationAssignment
   317  		ReverseAssignment           *webhookdir.FormationAssignment
   318  		TargetType                  model.ResourceType
   319  		ExpectedNotificationDetails *formationconstraint.GenerateFormationAssignmentNotificationOperationDetails
   320  		ExpectedErrMessage          string
   321  	}{
   322  		{
   323  			Name:                "success resource type application",
   324  			Operation:           model.AssignFormation,
   325  			FormationID:         fixUUID(),
   326  			ApplicationTemplate: applicationTemplate,
   327  			Application:         application,
   328  			Runtime:             runtime,
   329  			RuntimeContext:      runtimeContext,
   330  			Assignment:          emptyFormationAssignment,
   331  			ReverseAssignment:   emptyFormationAssignment,
   332  			TargetType:          model.ApplicationResourceType,
   333  			ExpectedNotificationDetails: &formationconstraint.GenerateFormationAssignmentNotificationOperationDetails{
   334  				Operation:           model.AssignFormation,
   335  				FormationID:         fixUUID(),
   336  				FormationTemplateID: FormationTemplateID,
   337  				ResourceType:        model.ApplicationResourceType,
   338  				ResourceSubtype:     applicationType,
   339  				ResourceID:          ApplicationID,
   340  				ApplicationTemplate: applicationTemplate,
   341  				Application:         application,
   342  				Runtime:             runtime,
   343  				RuntimeContext:      runtimeContext,
   344  				Assignment:          emptyFormationAssignment,
   345  				ReverseAssignment:   emptyFormationAssignment,
   346  				TenantID:            tenantID.String(),
   347  			},
   348  			ExpectedErrMessage: "",
   349  		},
   350  		{
   351  			Name:                "success resource type runtime",
   352  			Operation:           model.AssignFormation,
   353  			FormationID:         fixUUID(),
   354  			ApplicationTemplate: applicationTemplate,
   355  			Application:         application,
   356  			Runtime:             runtime,
   357  			RuntimeContext:      runtimeContext,
   358  			Assignment:          emptyFormationAssignment,
   359  			ReverseAssignment:   emptyFormationAssignment,
   360  			TargetType:          model.RuntimeResourceType,
   361  			ExpectedNotificationDetails: &formationconstraint.GenerateFormationAssignmentNotificationOperationDetails{
   362  				Operation:           model.AssignFormation,
   363  				FormationID:         fixUUID(),
   364  				FormationTemplateID: FormationTemplateID,
   365  				ResourceType:        model.RuntimeResourceType,
   366  				ResourceSubtype:     runtimeType,
   367  				ResourceID:          RuntimeContextRuntimeID,
   368  				ApplicationTemplate: applicationTemplate,
   369  				Application:         application,
   370  				Runtime:             runtime,
   371  				RuntimeContext:      runtimeContext,
   372  				Assignment:          emptyFormationAssignment,
   373  				ReverseAssignment:   emptyFormationAssignment,
   374  				TenantID:            tenantID.String(),
   375  			},
   376  			ExpectedErrMessage: "",
   377  		},
   378  		{
   379  			Name:                "success resource type runtime context",
   380  			Operation:           model.AssignFormation,
   381  			FormationID:         fixUUID(),
   382  			ApplicationTemplate: applicationTemplate,
   383  			Application:         application,
   384  			Runtime:             runtime,
   385  			RuntimeContext:      runtimeContext,
   386  			Assignment:          emptyFormationAssignment,
   387  			ReverseAssignment:   emptyFormationAssignment,
   388  			TargetType:          model.RuntimeContextResourceType,
   389  			ExpectedNotificationDetails: &formationconstraint.GenerateFormationAssignmentNotificationOperationDetails{
   390  				Operation:           model.AssignFormation,
   391  				FormationID:         fixUUID(),
   392  				FormationTemplateID: FormationTemplateID,
   393  				ResourceType:        model.RuntimeContextResourceType,
   394  				ResourceSubtype:     runtimeType,
   395  				ResourceID:          RuntimeContextID,
   396  				ApplicationTemplate: applicationTemplate,
   397  				Application:         application,
   398  				Runtime:             runtime,
   399  				RuntimeContext:      runtimeContext,
   400  				Assignment:          emptyFormationAssignment,
   401  				ReverseAssignment:   emptyFormationAssignment,
   402  				TenantID:            tenantID.String(),
   403  			},
   404  			ExpectedErrMessage: "",
   405  		},
   406  		{
   407  			Name:                "fail to determine application type",
   408  			Operation:           model.AssignFormation,
   409  			FormationID:         fixUUID(),
   410  			ApplicationTemplate: applicationTemplate,
   411  			Application:         applicationWithoutType,
   412  			Runtime:             runtime,
   413  			RuntimeContext:      runtimeContext,
   414  			Assignment:          emptyFormationAssignment,
   415  			ReverseAssignment:   emptyFormationAssignment,
   416  			TargetType:          model.ApplicationResourceType,
   417  			ExpectedErrMessage:  "While determining subtype for application with ID",
   418  		},
   419  		{
   420  			Name:                "fail to determine runtime type",
   421  			Operation:           model.AssignFormation,
   422  			FormationID:         fixUUID(),
   423  			ApplicationTemplate: applicationTemplate,
   424  			Application:         application,
   425  			Runtime:             runtimeWithoutType,
   426  			RuntimeContext:      runtimeContext,
   427  			Assignment:          emptyFormationAssignment,
   428  			ReverseAssignment:   emptyFormationAssignment,
   429  			TargetType:          model.RuntimeResourceType,
   430  			ExpectedErrMessage:  "While determining subtype for runtime with ID",
   431  		},
   432  		{
   433  			Name:                "fail to determine runtime context type",
   434  			Operation:           model.AssignFormation,
   435  			FormationID:         fixUUID(),
   436  			ApplicationTemplate: applicationTemplate,
   437  			Application:         application,
   438  			Runtime:             runtimeWithoutType,
   439  			RuntimeContext:      runtimeContext,
   440  			Assignment:          emptyFormationAssignment,
   441  			ReverseAssignment:   emptyFormationAssignment,
   442  			TargetType:          model.RuntimeContextResourceType,
   443  			ExpectedErrMessage:  "While determining subtype for runtime context with ID",
   444  		},
   445  		{
   446  			Name:                "unsupported resource type",
   447  			Operation:           model.AssignFormation,
   448  			FormationID:         fixUUID(),
   449  			ApplicationTemplate: applicationTemplate,
   450  			Application:         application,
   451  			Runtime:             runtimeWithoutType,
   452  			RuntimeContext:      runtimeContext,
   453  			Assignment:          emptyFormationAssignment,
   454  			ReverseAssignment:   emptyFormationAssignment,
   455  			TargetType:          model.FormationResourceType,
   456  			ExpectedErrMessage:  "Unsuported target resource subtype \"FORMATION\"",
   457  		},
   458  	}
   459  
   460  	for _, testCase := range testCases {
   461  		t.Run(testCase.Name, func(t *testing.T) {
   462  			// GIVEN
   463  			builder := formation.NewNotificationsBuilder(nil, nil, runtimeType, applicationType)
   464  
   465  			// WHEN
   466  			actual, err := builder.PrepareDetailsForConfigurationChangeNotificationGeneration(testCase.Operation, testCase.FormationID, FormationTemplateID, testCase.ApplicationTemplate, testCase.Application, testCase.Runtime, testCase.RuntimeContext, testCase.Assignment, testCase.ReverseAssignment, testCase.TargetType, nil, tenantID.String())
   467  
   468  			// THEN
   469  			if testCase.ExpectedErrMessage == "" {
   470  				require.NoError(t, err)
   471  				assert.Equal(t, testCase.ExpectedNotificationDetails, actual)
   472  			} else {
   473  				require.Error(t, err)
   474  				require.Contains(t, err.Error(), testCase.ExpectedErrMessage)
   475  				require.Nil(t, actual)
   476  			}
   477  		})
   478  	}
   479  }
   480  
   481  func TestNotificationBuilder_PrepareDetailsForApplicationTenantMappingNotificationGeneration(t *testing.T) {
   482  	applicationTemplate := &webhookdir.ApplicationTemplateWithLabels{
   483  		ApplicationTemplate: fixApplicationTemplateModel(),
   484  		Labels:              fixApplicationTemplateLabelsMap(),
   485  	}
   486  
   487  	application := &webhookdir.ApplicationWithLabels{
   488  		Application: fixApplicationModel(ApplicationID),
   489  		Labels: map[string]string{
   490  			applicationType: applicationType,
   491  		},
   492  	}
   493  
   494  	applicationWithoutType := &webhookdir.ApplicationWithLabels{
   495  		Application: fixApplicationModel(ApplicationID),
   496  		Labels:      map[string]string{},
   497  	}
   498  
   499  	testCases := []struct {
   500  		Name                        string
   501  		Operation                   model.FormationOperation
   502  		FormationID                 string
   503  		SourceApplicationTemplate   *webhookdir.ApplicationTemplateWithLabels
   504  		SourceApplication           *webhookdir.ApplicationWithLabels
   505  		TargetApplicationTemplate   *webhookdir.ApplicationTemplateWithLabels
   506  		TargetApplication           *webhookdir.ApplicationWithLabels
   507  		Assignment                  *webhookdir.FormationAssignment
   508  		ReverseAssignment           *webhookdir.FormationAssignment
   509  		TargetType                  model.ResourceType
   510  		ExpectedNotificationDetails *formationconstraint.GenerateFormationAssignmentNotificationOperationDetails
   511  		ExpectedErrMessage          string
   512  	}{
   513  		{
   514  			Name:                      "success resource type application",
   515  			Operation:                 model.AssignFormation,
   516  			FormationID:               fixUUID(),
   517  			SourceApplicationTemplate: applicationTemplate,
   518  			SourceApplication:         application,
   519  			TargetApplicationTemplate: applicationTemplate,
   520  			TargetApplication:         application,
   521  			Assignment:                emptyFormationAssignment,
   522  			ReverseAssignment:         emptyFormationAssignment,
   523  			TargetType:                model.ApplicationResourceType,
   524  			ExpectedNotificationDetails: &formationconstraint.GenerateFormationAssignmentNotificationOperationDetails{
   525  				Operation:                 model.AssignFormation,
   526  				FormationID:               fixUUID(),
   527  				FormationTemplateID:       FormationTemplateID,
   528  				ResourceType:              model.ApplicationResourceType,
   529  				ResourceSubtype:           applicationType,
   530  				ResourceID:                ApplicationID,
   531  				SourceApplicationTemplate: applicationTemplate,
   532  				SourceApplication:         application,
   533  				TargetApplicationTemplate: applicationTemplate,
   534  				TargetApplication:         application,
   535  				Assignment:                emptyFormationAssignment,
   536  				ReverseAssignment:         emptyFormationAssignment,
   537  				TenantID:                  tenantID.String(),
   538  			},
   539  			ExpectedErrMessage: "",
   540  		},
   541  		{
   542  			Name:                      "fail to determine target application type",
   543  			Operation:                 model.AssignFormation,
   544  			FormationID:               fixUUID(),
   545  			SourceApplicationTemplate: applicationTemplate,
   546  			SourceApplication:         application,
   547  			TargetApplicationTemplate: applicationTemplate,
   548  			TargetApplication:         applicationWithoutType,
   549  			Assignment:                emptyFormationAssignment,
   550  			ReverseAssignment:         emptyFormationAssignment,
   551  			TargetType:                model.ApplicationResourceType,
   552  			ExpectedErrMessage:        "While determining subtype for application with",
   553  		},
   554  	}
   555  
   556  	for _, testCase := range testCases {
   557  		t.Run(testCase.Name, func(t *testing.T) {
   558  			// GIVEN
   559  			builder := formation.NewNotificationsBuilder(nil, nil, runtimeType, applicationType)
   560  
   561  			// WHEN
   562  			actual, err := builder.PrepareDetailsForApplicationTenantMappingNotificationGeneration(testCase.Operation, testCase.FormationID, FormationTemplateID, testCase.SourceApplicationTemplate, testCase.SourceApplication, testCase.TargetApplicationTemplate, testCase.TargetApplication, testCase.Assignment, testCase.ReverseAssignment, nil, tenantID.String())
   563  
   564  			// THEN
   565  			if testCase.ExpectedErrMessage == "" {
   566  				require.NoError(t, err)
   567  				assert.Equal(t, testCase.ExpectedNotificationDetails, actual)
   568  			} else {
   569  				require.Error(t, err)
   570  				require.Contains(t, err.Error(), testCase.ExpectedErrMessage)
   571  				require.Nil(t, actual)
   572  			}
   573  		})
   574  	}
   575  }