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

     1  package application_test
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"testing"
     7  
     8  	"github.com/google/uuid"
     9  	"github.com/kyma-incubator/compass/components/director/internal/repo"
    10  	"github.com/kyma-incubator/compass/components/director/pkg/str"
    11  	"github.com/pkg/errors"
    12  
    13  	"github.com/stretchr/testify/require"
    14  
    15  	"github.com/kyma-incubator/compass/components/director/internal/repo/testdb"
    16  
    17  	"github.com/kyma-incubator/compass/components/director/internal/domain/application"
    18  	"github.com/kyma-incubator/compass/components/director/internal/domain/application/automock"
    19  	"github.com/kyma-incubator/compass/components/director/internal/model"
    20  	"github.com/kyma-incubator/compass/components/director/pkg/graphql"
    21  	"github.com/stretchr/testify/assert"
    22  )
    23  
    24  func TestConverter_ToGraphQL(t *testing.T) {
    25  	// GIVEN
    26  	testCases := []struct {
    27  		Name     string
    28  		Input    *model.Application
    29  		Expected *graphql.Application
    30  	}{
    31  		{
    32  			Name:     "All properties given",
    33  			Input:    fixDetailedModelApplication(t, givenID(), givenTenant(), "Foo", "Lorem ipsum"),
    34  			Expected: fixDetailedGQLApplication(t, givenID(), "Foo", "Lorem ipsum"),
    35  		},
    36  		{
    37  			Name:  "Empty",
    38  			Input: &model.Application{BaseEntity: &model.BaseEntity{}},
    39  			Expected: &graphql.Application{
    40  				Status: &graphql.ApplicationStatus{
    41  					Condition: graphql.ApplicationStatusConditionInitial,
    42  				},
    43  				BaseEntity: &graphql.BaseEntity{},
    44  			},
    45  		},
    46  		{
    47  			Name:     "Nil",
    48  			Input:    nil,
    49  			Expected: nil,
    50  		},
    51  	}
    52  
    53  	for _, testCase := range testCases {
    54  		t.Run(testCase.Name, func(t *testing.T) {
    55  			// WHEN
    56  			converter := application.NewConverter(nil, nil)
    57  			res := converter.ToGraphQL(testCase.Input)
    58  
    59  			// then
    60  			assert.Equal(t, testCase.Expected, res)
    61  		})
    62  	}
    63  }
    64  
    65  func TestConverter_MultipleToGraphQL(t *testing.T) {
    66  	// GIVEN
    67  	input := []*model.Application{
    68  		fixModelApplication("foo", givenTenant(), "Foo", "Lorem ipsum"),
    69  		fixModelApplication("bar", givenTenant(), "Bar", "Dolor sit amet"),
    70  		{BaseEntity: &model.BaseEntity{}},
    71  		nil,
    72  	}
    73  	expected := []*graphql.Application{
    74  		fixGQLApplication("foo", "Foo", "Lorem ipsum"),
    75  		fixGQLApplication("bar", "Bar", "Dolor sit amet"),
    76  		{
    77  			BaseEntity: &graphql.BaseEntity{},
    78  			Status: &graphql.ApplicationStatus{
    79  				Condition: graphql.ApplicationStatusConditionInitial,
    80  			},
    81  		},
    82  	}
    83  
    84  	// WHEN
    85  	converter := application.NewConverter(nil, nil)
    86  	res := converter.MultipleToGraphQL(input)
    87  
    88  	// then
    89  	assert.Equal(t, expected, res)
    90  }
    91  
    92  func TestConverter_CreateInputFromGraphQL(t *testing.T) {
    93  	allPropsInput := fixGQLApplicationRegisterInput("foo", "Lorem ipsum")
    94  	allPropsExpected := fixModelApplicationRegisterInput("foo", "Lorem ipsum")
    95  
    96  	webhooksErr := errors.New("wh err")
    97  	bndlsErr := errors.New("bndl err")
    98  
    99  	// GIVEN
   100  	testCases := []struct {
   101  		Name               string
   102  		Input              graphql.ApplicationRegisterInput
   103  		Expected           model.ApplicationRegisterInput
   104  		ExpectedErr        error
   105  		WebhookConverterFn func() *automock.WebhookConverter
   106  		BundleConverterFn  func() *automock.BundleConverter
   107  	}{
   108  		{
   109  			Name:     "Succeeds when all properties are given",
   110  			Input:    allPropsInput,
   111  			Expected: allPropsExpected,
   112  			WebhookConverterFn: func() *automock.WebhookConverter {
   113  				conv := &automock.WebhookConverter{}
   114  				conv.On("MultipleInputFromGraphQL", allPropsInput.Webhooks).Return(allPropsExpected.Webhooks, nil)
   115  				return conv
   116  			},
   117  			BundleConverterFn: func() *automock.BundleConverter {
   118  				conv := &automock.BundleConverter{}
   119  				conv.On("MultipleCreateInputFromGraphQL", allPropsInput.Bundles).Return(allPropsExpected.Bundles, nil)
   120  				return conv
   121  			},
   122  		},
   123  		{
   124  			Name:     "Succeeds when empty",
   125  			Input:    graphql.ApplicationRegisterInput{},
   126  			Expected: model.ApplicationRegisterInput{},
   127  			WebhookConverterFn: func() *automock.WebhookConverter {
   128  				conv := &automock.WebhookConverter{}
   129  				conv.On("MultipleInputFromGraphQL", []*graphql.WebhookInput(nil)).Return(nil, nil)
   130  				return conv
   131  			},
   132  			BundleConverterFn: func() *automock.BundleConverter {
   133  				conv := &automock.BundleConverter{}
   134  				conv.On("MultipleCreateInputFromGraphQL", []*graphql.BundleCreateInput(nil)).Return(nil, nil)
   135  				return conv
   136  			},
   137  		},
   138  		{
   139  			Name:        "Returns error when webhook conversion fails",
   140  			Input:       graphql.ApplicationRegisterInput{},
   141  			ExpectedErr: webhooksErr,
   142  
   143  			WebhookConverterFn: func() *automock.WebhookConverter {
   144  				conv := &automock.WebhookConverter{}
   145  				conv.On("MultipleInputFromGraphQL", []*graphql.WebhookInput(nil)).Return(nil, webhooksErr)
   146  				return conv
   147  			},
   148  			BundleConverterFn: func() *automock.BundleConverter { return &automock.BundleConverter{} },
   149  		},
   150  		{
   151  			Name:        "Returns error when bundles conversion fails",
   152  			Input:       graphql.ApplicationRegisterInput{},
   153  			ExpectedErr: bndlsErr,
   154  			WebhookConverterFn: func() *automock.WebhookConverter {
   155  				conv := &automock.WebhookConverter{}
   156  				conv.On("MultipleInputFromGraphQL", []*graphql.WebhookInput(nil)).Return(nil, nil)
   157  				return conv
   158  			},
   159  			BundleConverterFn: func() *automock.BundleConverter {
   160  				conv := &automock.BundleConverter{}
   161  				conv.On("MultipleCreateInputFromGraphQL", []*graphql.BundleCreateInput(nil)).Return(nil, bndlsErr)
   162  				return conv
   163  			},
   164  		},
   165  	}
   166  
   167  	for _, testCase := range testCases {
   168  		t.Run(testCase.Name, func(t *testing.T) {
   169  			// WHEN
   170  			converter := application.NewConverter(
   171  				testCase.WebhookConverterFn(),
   172  				testCase.BundleConverterFn(),
   173  			)
   174  			res, err := converter.CreateInputFromGraphQL(context.TODO(), testCase.Input)
   175  
   176  			// then
   177  			if testCase.ExpectedErr != nil {
   178  				assert.Error(t, err)
   179  				assert.ErrorIs(t, err, testCase.ExpectedErr)
   180  			} else {
   181  				assert.NoError(t, err)
   182  				assert.Equal(t, testCase.Expected, res)
   183  			}
   184  		})
   185  	}
   186  }
   187  
   188  func TestConverter_UpdateInputFromGraphQL_StatusCondition(t *testing.T) {
   189  	testCases := []struct {
   190  		Name           string
   191  		CondtionGQL    graphql.ApplicationStatusCondition
   192  		ConditionModel model.ApplicationStatusCondition
   193  	}{
   194  		{
   195  			Name:           "When status condition is FAILED",
   196  			CondtionGQL:    graphql.ApplicationStatusConditionFailed,
   197  			ConditionModel: model.ApplicationStatusConditionFailed,
   198  		},
   199  		{
   200  			Name:           "When status condition is CONNECTED",
   201  			CondtionGQL:    graphql.ApplicationStatusConditionConnected,
   202  			ConditionModel: model.ApplicationStatusConditionConnected,
   203  		},
   204  		{
   205  			Name:           "When status condition is INITIAL",
   206  			CondtionGQL:    graphql.ApplicationStatusConditionInitial,
   207  			ConditionModel: model.ApplicationStatusConditionInitial,
   208  		},
   209  	}
   210  
   211  	for _, testCase := range testCases {
   212  		t.Run(testCase.Name, func(t *testing.T) {
   213  			gqlApp := graphql.ApplicationUpdateInput{StatusCondition: &testCase.CondtionGQL}
   214  
   215  			converter := application.NewConverter(nil, nil)
   216  			modelApp := converter.UpdateInputFromGraphQL(gqlApp)
   217  
   218  			require.Equal(t, &testCase.ConditionModel, modelApp.StatusCondition)
   219  		})
   220  	}
   221  }
   222  
   223  func TestConverter_UpdateInputFromGraphQL(t *testing.T) {
   224  	allPropsInput := fixGQLApplicationUpdateInput("foo", "Lorem ipsum", testURL, "baseUrl", "ns", graphql.ApplicationStatusConditionConnected)
   225  	allPropsExpected := fixModelApplicationUpdateInput("foo", "Lorem ipsum", testURL, "baseUrl", "ns", model.ApplicationStatusConditionConnected)
   226  
   227  	// GIVEN
   228  	testCases := []struct {
   229  		Name     string
   230  		Input    graphql.ApplicationUpdateInput
   231  		Expected model.ApplicationUpdateInput
   232  	}{
   233  		{
   234  			Name:     "All properties given",
   235  			Input:    allPropsInput,
   236  			Expected: allPropsExpected,
   237  		},
   238  		{
   239  			Name:     "Empty",
   240  			Input:    graphql.ApplicationUpdateInput{},
   241  			Expected: model.ApplicationUpdateInput{},
   242  		},
   243  	}
   244  
   245  	for _, testCase := range testCases {
   246  		t.Run(testCase.Name, func(t *testing.T) {
   247  			// WHEN
   248  			converter := application.NewConverter(nil, nil)
   249  			res := converter.UpdateInputFromGraphQL(testCase.Input)
   250  
   251  			// then
   252  			assert.Equal(t, testCase.Expected, res)
   253  		})
   254  	}
   255  }
   256  
   257  func TestConverter_ToEntity(t *testing.T) {
   258  	conv := application.NewConverter(nil, nil)
   259  
   260  	t.Run("All properties given", func(t *testing.T) {
   261  		// GIVEN
   262  		appModel := fixDetailedModelApplication(t, givenID(), givenTenant(), "app-name", "app-description")
   263  
   264  		// WHEN
   265  		appEntity, err := conv.ToEntity(appModel)
   266  
   267  		// THEN
   268  		assert.NoError(t, err)
   269  		assertApplicationDefinition(t, appModel, appEntity)
   270  	})
   271  
   272  	t.Run("Nil", func(t *testing.T) {
   273  		// WHEN
   274  		appEntity, err := conv.ToEntity(nil)
   275  
   276  		// THEN
   277  		assert.NoError(t, err)
   278  		assert.Nil(t, appEntity)
   279  	})
   280  
   281  	t.Run("Empty", func(t *testing.T) {
   282  		// GIVEN
   283  		appModel := &model.Application{}
   284  
   285  		// WHEN
   286  		appEntity, err := conv.ToEntity(appModel)
   287  
   288  		// THEN
   289  		if err != nil {
   290  			assert.Contains(t, err.Error(), "invalid input model")
   291  		} else {
   292  			assertApplicationDefinition(t, appModel, appEntity)
   293  		}
   294  	})
   295  }
   296  
   297  func TestConverter_FromEntity(t *testing.T) {
   298  	conv := application.NewConverter(nil, nil)
   299  
   300  	t.Run("All properties given", func(t *testing.T) {
   301  		// GIVEN
   302  		appEntity := fixDetailedEntityApplication(t, givenID(), givenTenant(), "app-name", "app-description")
   303  
   304  		// WHEN
   305  		appModel := conv.FromEntity(appEntity)
   306  
   307  		// THEN
   308  		assertApplicationDefinition(t, appModel, appEntity)
   309  	})
   310  
   311  	t.Run("Nil", func(t *testing.T) {
   312  		// WHEN
   313  		appModel := conv.FromEntity(nil)
   314  
   315  		// THEN
   316  		assert.Nil(t, appModel)
   317  	})
   318  
   319  	t.Run("Empty", func(t *testing.T) {
   320  		// GIVEN
   321  		appEntity := &application.Entity{BaseEntity: &repo.BaseEntity{}}
   322  
   323  		// WHEN
   324  		appModel := conv.FromEntity(appEntity)
   325  
   326  		// THEN
   327  		assertApplicationDefinition(t, appModel, appEntity)
   328  	})
   329  }
   330  
   331  func TestConverter_CreateRegisterInputJSONToGQL(t *testing.T) {
   332  	// GIVEN
   333  	conv := application.NewConverter(nil, nil)
   334  
   335  	t.Run("Successful two-way conversion", func(t *testing.T) {
   336  		inputGQL := fixGQLApplicationRegisterInput("name", "description")
   337  		inputGQL.Labels = graphql.Labels{"test": "test"}
   338  
   339  		// WHEN
   340  		// GQL -> JSON
   341  		json, err := conv.CreateRegisterInputGQLToJSON(&inputGQL)
   342  		require.NoError(t, err)
   343  
   344  		// JSON -> GQL
   345  		outputGQL, err := conv.CreateRegisterInputJSONToGQL(json)
   346  		require.NoError(t, err)
   347  
   348  		// THEN
   349  		require.Equal(t, inputGQL, outputGQL)
   350  	})
   351  
   352  	t.Run("Error while JSON to GQL conversion", func(t *testing.T) {
   353  		// WHEN
   354  		expectedErr := "invalid character 'a' looking for beginning of value"
   355  		_, err := conv.CreateRegisterInputJSONToGQL("ad[sd")
   356  
   357  		// THEN
   358  		require.Error(t, err)
   359  		require.Contains(t, err.Error(), expectedErr)
   360  	})
   361  }
   362  
   363  func TestConverter_CreateJSONInputJSONToGQL(t *testing.T) {
   364  	// GIVEN
   365  	conv := application.NewConverter(nil, nil)
   366  
   367  	t.Run("Successful two-way conversion", func(t *testing.T) {
   368  		inputGQL := fixGQLApplicationJSONInput("name", "description")
   369  		inputGQL.Labels = graphql.Labels{"test": "test"}
   370  
   371  		// WHEN
   372  		// GQL -> JSON
   373  		json, err := conv.CreateJSONInputGQLToJSON(&inputGQL)
   374  		require.NoError(t, err)
   375  
   376  		// JSON -> GQL
   377  		outputGQL, err := conv.CreateJSONInputJSONToGQL(json)
   378  		require.NoError(t, err)
   379  
   380  		// THEN
   381  		require.Equal(t, inputGQL, outputGQL)
   382  	})
   383  
   384  	t.Run("Error while JSON to GQL conversion", func(t *testing.T) {
   385  		// WHEN
   386  		expectedErr := "invalid character 'a' looking for beginning of value"
   387  		_, err := conv.CreateJSONInputJSONToGQL("ad[sd")
   388  
   389  		// THEN
   390  		require.Error(t, err)
   391  		require.Contains(t, err.Error(), expectedErr)
   392  	})
   393  }
   394  
   395  func TestConverter_CreateInputJSONToModelL(t *testing.T) {
   396  	t.Run("Successful conversion", func(t *testing.T) {
   397  		// GIVEN
   398  		cond := model.ApplicationStatusConditionInitial
   399  		appInput := model.ApplicationRegisterInput{
   400  			Name:            "test",
   401  			Description:     str.Ptr("test description"),
   402  			StatusCondition: &cond,
   403  			Labels: map[string]interface{}{
   404  				"key": "value",
   405  			},
   406  		}
   407  		appInputJSON := fmt.Sprintf(`{"name": "%s","description": "%s","statusCondition": "%s","labels": {"key": "value"}}`,
   408  			appInput.Name, *appInput.Description, string(cond))
   409  
   410  		conv := application.NewConverter(nil, nil)
   411  
   412  		// WHEN
   413  		outputModel, err := conv.CreateInputJSONToModel(context.TODO(), appInputJSON)
   414  
   415  		// THEN
   416  		require.NoError(t, err)
   417  		require.Equal(t, appInput, outputModel)
   418  	})
   419  	t.Run("Error while JSON to GQL conversion", func(t *testing.T) {
   420  		// GIVEN
   421  		conv := application.NewConverter(nil, nil)
   422  
   423  		// WHEN
   424  		_, err := conv.CreateInputJSONToModel(context.TODO(), "test")
   425  
   426  		// THEN
   427  		require.Error(t, err)
   428  	})
   429  }
   430  
   431  func TestConverter_ConvertToModel(t *testing.T) {
   432  	conv := application.NewConverter(nil, nil)
   433  
   434  	t.Run("Successful full model", func(t *testing.T) {
   435  		tenantID := uuid.New().String()
   436  		appGraphql := fixGQLApplication(uuid.New().String(), "app", "desc")
   437  
   438  		// WHEN
   439  		appModel := conv.GraphQLToModel(appGraphql, tenantID)
   440  		outputGraphql := conv.ToGraphQL(appModel)
   441  
   442  		// THEN
   443  		assert.Equal(t, appGraphql, outputGraphql)
   444  	})
   445  
   446  	t.Run("Success empty model", func(t *testing.T) {
   447  		// GIVEN
   448  		appGraphql := &graphql.Application{BaseEntity: &graphql.BaseEntity{}}
   449  
   450  		// WHEN
   451  		appModel := conv.GraphQLToModel(appGraphql, uuid.New().String())
   452  		outputGraphql := conv.ToGraphQL(appModel)
   453  
   454  		// THEN
   455  		appGraphql.Status = &graphql.ApplicationStatus{Condition: graphql.ApplicationStatusConditionInitial}
   456  		assert.Equal(t, appGraphql, outputGraphql)
   457  	})
   458  
   459  	t.Run("Nil model", func(t *testing.T) {
   460  		// WHEN
   461  		output := conv.GraphQLToModel(nil, uuid.New().String())
   462  		// THEN
   463  		require.Nil(t, output)
   464  	})
   465  }
   466  
   467  func assertApplicationDefinition(t *testing.T, appModel *model.Application, entity *application.Entity) {
   468  	assert.Equal(t, appModel.ID, entity.ID)
   469  	assert.Equal(t, appModel.Name, entity.Name)
   470  
   471  	if appModel.Status != nil {
   472  		assert.Equal(t, appModel.Status.Condition, model.ApplicationStatusCondition(entity.StatusCondition))
   473  		assert.Equal(t, appModel.Status.Timestamp, entity.StatusTimestamp)
   474  	} else {
   475  		assert.Equal(t, string(model.ApplicationStatusConditionInitial), entity.StatusCondition)
   476  	}
   477  
   478  	testdb.AssertSQLNullStringEqualTo(t, entity.Description, appModel.Description)
   479  	testdb.AssertSQLNullStringEqualTo(t, entity.HealthCheckURL, appModel.HealthCheckURL)
   480  	testdb.AssertSQLNullStringEqualTo(t, entity.ProviderName, appModel.ProviderName)
   481  }
   482  
   483  func givenID() string {
   484  	return "bd0646fa-3c30-4255-84f8-182f57742aa1"
   485  }
   486  
   487  func givenTenant() string {
   488  	return "8f237125-50be-4bb4-96ce-389e2b931f46"
   489  }
   490  
   491  func givenTenantAsUUID() uuid.UUID {
   492  	parse, err := uuid.Parse("8f237125-50be-4bb4-96ce-389e2b931f46")
   493  	if err != nil {
   494  		panic(err)
   495  	}
   496  
   497  	return parse
   498  }