github.com/kyma-incubator/compass/components/director@v0.0.0-20230623144113-d764f56ff805/internal/tenantfetchersvc/resync/event_page_test.go (about)

     1  package resync_test
     2  
     3  import (
     4  	"bytes"
     5  	"context"
     6  	"fmt"
     7  	"testing"
     8  	"text/template"
     9  
    10  	"github.com/kyma-incubator/compass/components/director/internal/tenantfetchersvc/resync"
    11  	"github.com/kyma-incubator/compass/components/director/pkg/tenant"
    12  
    13  	"github.com/kyma-incubator/compass/components/director/internal/model"
    14  	"github.com/stretchr/testify/assert"
    15  )
    16  
    17  func Test_getMovedSubaccounts(t *testing.T) {
    18  	ctx := context.TODO()
    19  
    20  	idField := "id"
    21  	entityTypeField := "type"
    22  	nameField := "name"
    23  	subdomainField := "subdomain"
    24  	regionField := "region"
    25  	parentID := "parent"
    26  	globalAccountKey := "gaID"
    27  	labelFieldMappingValue := "moved-label"
    28  	sourceTenantField := "source-tenant"
    29  	targetTenantField := "target-tenant"
    30  	expectedRuntime := model.MovedSubaccountMappingInput{
    31  		SubaccountID: "label-value",
    32  		SourceTenant: "123",
    33  		TargetTenant: "456",
    34  		TenantMappingInput: model.BusinessTenantMappingInput{
    35  			Name:           "subaccount-name",
    36  			ExternalTenant: "label-value",
    37  			Parent:         "parent",
    38  			Subdomain:      "subdomain",
    39  			Region:         "region",
    40  			Type:           string(tenant.Subaccount),
    41  			Provider:       "",
    42  		},
    43  	}
    44  	fieldMapping := resync.TenantFieldMapping{
    45  		IDField:          idField,
    46  		NameField:        nameField,
    47  		SubdomainField:   subdomainField,
    48  		EntityTypeField:  entityTypeField,
    49  		RegionField:      regionField,
    50  		GlobalAccountKey: globalAccountKey,
    51  		EventsField:      "events",
    52  		DetailsField:     "details",
    53  	}
    54  	tests := []struct {
    55  		name               string
    56  		detailsPairs       [][]Pair
    57  		errorFunc          func(*testing.T, error)
    58  		assertRuntimesFunc func(*testing.T, []model.MovedSubaccountMappingInput)
    59  	}{
    60  		{
    61  			name: "successfully gets MovedSubaccountsMappingInputs for correct eventPage format",
    62  			detailsPairs: [][]Pair{
    63  				{
    64  					{labelFieldMappingValue, "label-value"},
    65  					{sourceTenantField, "123"},
    66  					{targetTenantField, "456"},
    67  					{nameField, "subaccount-name"},
    68  					{subdomainField, "subdomain"},
    69  					{regionField, "region"},
    70  				},
    71  			},
    72  			assertRuntimesFunc: func(t *testing.T, runtimes []model.MovedSubaccountMappingInput) {
    73  				assert.Equal(t, 1, len(runtimes))
    74  				assert.Equal(t, expectedRuntime, runtimes[0])
    75  			},
    76  			errorFunc: func(t *testing.T, err error) {
    77  				assert.NoError(t, err)
    78  			},
    79  		},
    80  		{
    81  			name: "empty mappings for get MovedSubaccountMappingInput when id field is invalid",
    82  			detailsPairs: [][]Pair{
    83  				{
    84  					{"wrong", "label-value"},
    85  					{sourceTenantField, "123"},
    86  					{targetTenantField, "456"},
    87  				},
    88  			},
    89  			errorFunc: func(t *testing.T, err error) {
    90  				assert.NoError(t, err)
    91  			},
    92  			assertRuntimesFunc: func(t *testing.T, inputs []model.MovedSubaccountMappingInput) {
    93  				assert.Len(t, inputs, 0)
    94  			},
    95  		},
    96  		{
    97  			name: "empty mappings for get MovedSubaccountMappingInput when sourceTenant field is invalid",
    98  			detailsPairs: [][]Pair{
    99  				{
   100  					{labelFieldMappingValue, "label-value"},
   101  					{"wrong", "123"},
   102  					{targetTenantField, "456"},
   103  				},
   104  			},
   105  			errorFunc: func(t *testing.T, err error) {
   106  				assert.NoError(t, err)
   107  			},
   108  			assertRuntimesFunc: func(t *testing.T, inputs []model.MovedSubaccountMappingInput) {
   109  				assert.Len(t, inputs, 0)
   110  			},
   111  		},
   112  		{
   113  			name: "empty mappings for get MovedSubaccountMappingInput when targetTenant field is invalid",
   114  			detailsPairs: [][]Pair{
   115  				{
   116  					{labelFieldMappingValue, "label-value"},
   117  					{sourceTenantField, "123"},
   118  					{"wrong", "456"},
   119  				},
   120  			},
   121  			errorFunc: func(t *testing.T, err error) {
   122  				assert.NoError(t, err)
   123  			},
   124  			assertRuntimesFunc: func(t *testing.T, inputs []model.MovedSubaccountMappingInput) {
   125  				assert.Len(t, inputs, 0)
   126  			},
   127  		},
   128  		{
   129  			name: "events are skipped if some of the fields are invalid",
   130  			detailsPairs: [][]Pair{
   131  				{
   132  					{labelFieldMappingValue, "label-value"},
   133  					{sourceTenantField, "123"},
   134  					{nameField, "name"},
   135  					{regionField, "region"},
   136  					{"wrong", "456"},
   137  				},
   138  				{
   139  					{labelFieldMappingValue, "label-value"},
   140  					{sourceTenantField, "123"},
   141  					{targetTenantField, "456"},
   142  					{nameField, "name"},
   143  					{regionField, "region"},
   144  				},
   145  			},
   146  			errorFunc: func(t *testing.T, err error) {
   147  				assert.NoError(t, err)
   148  			},
   149  			assertRuntimesFunc: func(t *testing.T, inputs []model.MovedSubaccountMappingInput) {
   150  				assert.Len(t, inputs, 1)
   151  			},
   152  		},
   153  	}
   154  
   155  	for _, test := range tests {
   156  		t.Run(test.name, func(t *testing.T) {
   157  			events := make([][]byte, 0, len(test.detailsPairs))
   158  			for i, detailPair := range test.detailsPairs {
   159  				events = append(events, fixEventWithDetails(fmt.Sprintf("id%d", i), fmt.Sprintf("foo%d", i), "GlobalAccount", parentID, constructJSONObject(detailPair...), fieldMapping))
   160  			}
   161  			page := resync.EventsPage{
   162  				FieldMapping: fieldMapping,
   163  				MovedSubaccountsFieldMapping: resync.MovedSubaccountsFieldMapping{
   164  					SubaccountID: labelFieldMappingValue,
   165  					SourceTenant: sourceTenantField,
   166  					TargetTenant: targetTenantField,
   167  				},
   168  				Payload: []byte(fixTenantEventsResponseBytes(eventsToJSONArray(events...), len(test.detailsPairs), 1)),
   169  			}
   170  
   171  			runtimes := page.GetMovedSubaccounts(ctx)
   172  			test.assertRuntimesFunc(t, runtimes)
   173  		})
   174  	}
   175  }
   176  
   177  func Test_getTenantMappings(t *testing.T) {
   178  	ctx := context.TODO()
   179  
   180  	idField := "id"
   181  	globalAccountGUIDField := "globalAccountGUID"
   182  	globalAccountKey := "gaID"
   183  	id := "1"
   184  	nameField := "name"
   185  	name := "test-name"
   186  	discriminatorField := "discriminator"
   187  	subdomainField := "subdomain"
   188  	subdomain := "test-subdomain"
   189  	providerName := "test-provider"
   190  	entityTypeField := "type"
   191  	entityType := "account"
   192  
   193  	expectedTenantMapping := model.BusinessTenantMappingInput{
   194  		ExternalTenant: id,
   195  		Name:           name,
   196  		Subdomain:      subdomain,
   197  		Type:           entityType,
   198  		Provider:       providerName,
   199  	}
   200  
   201  	tests := []struct {
   202  		name                    string
   203  		detailsPairs            [][]Pair
   204  		fieldMapping            resync.TenantFieldMapping
   205  		errorFunc               func(*testing.T, error)
   206  		assertTenantMappingFunc func(*testing.T, []model.BusinessTenantMappingInput)
   207  	}{
   208  		{
   209  			name: "successfully gets businessTenantMappingInputs for correct eventPage format",
   210  			fieldMapping: resync.TenantFieldMapping{
   211  				NameField:              nameField,
   212  				IDField:                idField,
   213  				SubdomainField:         subdomainField,
   214  				EventsField:            "events",
   215  				DetailsField:           "details",
   216  				EntityTypeField:        entityTypeField,
   217  				GlobalAccountGUIDField: globalAccountGUIDField,
   218  				GlobalAccountKey:       globalAccountKey,
   219  			},
   220  			errorFunc: func(t *testing.T, err error) {
   221  				assert.NoError(t, err)
   222  			},
   223  			assertTenantMappingFunc: func(t *testing.T, tenantMappings []model.BusinessTenantMappingInput) {
   224  				assert.Equal(t, 1, len(tenantMappings))
   225  				assert.Equal(t, expectedTenantMapping, tenantMappings[0])
   226  			},
   227  			detailsPairs: [][]Pair{
   228  				{
   229  					{idField, id},
   230  					{nameField, name},
   231  					{subdomainField, subdomain},
   232  				},
   233  			},
   234  		},
   235  		{
   236  			name: "successfully gets businessTenantMappingInputs for correct eventPage format with discriminator field",
   237  			fieldMapping: resync.TenantFieldMapping{
   238  				NameField:              nameField,
   239  				IDField:                idField,
   240  				EventsField:            "events",
   241  				DetailsField:           "details",
   242  				SubdomainField:         "subdomain",
   243  				DiscriminatorField:     discriminatorField,
   244  				DiscriminatorValue:     "discriminator-value",
   245  				EntityTypeField:        entityTypeField,
   246  				GlobalAccountGUIDField: globalAccountGUIDField,
   247  				GlobalAccountKey:       globalAccountKey,
   248  			},
   249  			errorFunc: func(t *testing.T, err error) {
   250  				assert.NoError(t, err)
   251  			},
   252  			assertTenantMappingFunc: func(t *testing.T, tenantMappings []model.BusinessTenantMappingInput) {
   253  				assert.Equal(t, 1, len(tenantMappings))
   254  				assert.Equal(t, expectedTenantMapping, tenantMappings[0])
   255  			},
   256  			detailsPairs: [][]Pair{
   257  				{
   258  					{idField, id},
   259  					{nameField, name},
   260  					{discriminatorField, "discriminator-value"},
   261  					{subdomainField, subdomain},
   262  					{entityTypeField, tenant.TypeToStr(tenant.Account)},
   263  				},
   264  			},
   265  		},
   266  		{
   267  			name: "successfully gets businessTenantMappingInputs for eventPage with missing tenant names",
   268  			fieldMapping: resync.TenantFieldMapping{
   269  				NameField:          nameField,
   270  				IDField:            idField,
   271  				EventsField:        "events",
   272  				DetailsField:       "details",
   273  				DiscriminatorField: discriminatorField,
   274  				DiscriminatorValue: "discriminator-value",
   275  			},
   276  			errorFunc: func(t *testing.T, err error) {
   277  				assert.NoError(t, err)
   278  			},
   279  			assertTenantMappingFunc: func(t *testing.T, tenantMappings []model.BusinessTenantMappingInput) {
   280  				assert.Len(t, tenantMappings, 1)
   281  				assert.Equal(t, expectedTenantMapping.ExternalTenant, tenantMappings[0].ExternalTenant)
   282  				assert.Empty(t, tenantMappings[0].Name)
   283  			},
   284  			detailsPairs: [][]Pair{
   285  				{
   286  					{idField, id},
   287  					{"wrong", name},
   288  					{discriminatorField, "discriminator-value"},
   289  				},
   290  			},
   291  		},
   292  		{
   293  			name: "empty mappings for get businessTenantMappingInputs when id field is wrong",
   294  			fieldMapping: resync.TenantFieldMapping{
   295  				NameField:          nameField,
   296  				IDField:            idField,
   297  				EventsField:        "events",
   298  				DetailsField:       "details",
   299  				DiscriminatorField: discriminatorField,
   300  				DiscriminatorValue: "discriminator-value",
   301  				EntityTypeField:    entityTypeField,
   302  				GlobalAccountKey:   globalAccountKey,
   303  			},
   304  			errorFunc: func(t *testing.T, err error) {
   305  				assert.NoError(t, err)
   306  			},
   307  			assertTenantMappingFunc: func(t *testing.T, tenantMappings []model.BusinessTenantMappingInput) {
   308  				assert.Len(t, tenantMappings, 0)
   309  			},
   310  			detailsPairs: [][]Pair{
   311  				{
   312  					{"wrong", id},
   313  					{nameField, name},
   314  					{discriminatorField, "discriminator-value"},
   315  				},
   316  			},
   317  		},
   318  		{
   319  			name: "empty mappings for get businessTenantMappingInputs when discriminator field is wrong",
   320  			fieldMapping: resync.TenantFieldMapping{
   321  				NameField:          nameField,
   322  				IDField:            idField,
   323  				EventsField:        "events",
   324  				DetailsField:       "details",
   325  				DiscriminatorField: discriminatorField,
   326  				DiscriminatorValue: "discriminator-value",
   327  			},
   328  			errorFunc: func(t *testing.T, err error) {
   329  				assert.NoError(t, err)
   330  			},
   331  			assertTenantMappingFunc: func(t *testing.T, tenantMappings []model.BusinessTenantMappingInput) {
   332  				assert.Len(t, tenantMappings, 0)
   333  			},
   334  			detailsPairs: [][]Pair{
   335  				{
   336  					{idField, id},
   337  					{nameField, name},
   338  					{"wrong", "discriminator-value"},
   339  				},
   340  			},
   341  		},
   342  	}
   343  
   344  	for _, test := range tests {
   345  		t.Run(test.name, func(t *testing.T) {
   346  			events := make([][]byte, 0, len(test.detailsPairs))
   347  			for i, detailPair := range test.detailsPairs {
   348  				events = append(events, fixEventWithDetails(fmt.Sprintf("id%d", i), fmt.Sprintf("foo%d", i), "GlobalAccount", fmt.Sprintf("gaID%d", i), constructJSONObject(detailPair...), test.fieldMapping))
   349  			}
   350  			page := resync.EventsPage{
   351  				FieldMapping: test.fieldMapping,
   352  				ProviderName: providerName,
   353  				Payload:      []byte(fixTenantEventsResponseBytes(eventsToJSONArray(events...), len(test.detailsPairs), 1)),
   354  			}
   355  			tenantMappings := page.GetTenantMappings(ctx, resync.CreatedAccountType)
   356  			test.assertTenantMappingFunc(t, tenantMappings)
   357  		})
   358  	}
   359  }
   360  
   361  type Pair struct {
   362  	Key   string
   363  	Value string
   364  }
   365  
   366  func constructJSONObject(pairs ...Pair) string {
   367  	var (
   368  		templateName       = "jsonObject"
   369  		jsonObjectTemplate = `{
   370  		{{ $n := (len .) }}
   371  		{{range $i, $e := .}}
   372    		"{{$e.Key}}": "{{$e.Value}}"{{if ne (plus1 $i) $n }},{{end}}
   373  		{{end}}
   374  	}`
   375  		funcMap = template.FuncMap{
   376  			"plus1": func(i int) int {
   377  				return i + 1
   378  			},
   379  		}
   380  		t      = template.Must(template.New(templateName).Funcs(funcMap).Parse(jsonObjectTemplate))
   381  		buffer = bytes.NewBufferString("")
   382  	)
   383  
   384  	template.Must(t, t.ExecuteTemplate(buffer, templateName, pairs))
   385  	return buffer.String()
   386  }
   387  
   388  func fixEventWithDetails(id, name, entityType, globalAccountGUID, details string, fieldMapping resync.TenantFieldMapping) []byte {
   389  	return []byte(fmt.Sprintf(`{"%s":"%s", "%s":"%s", "%s":"%s","%s":"%s","%s":%s}`, fieldMapping.IDField, id, fieldMapping.NameField, name, fieldMapping.EntityTypeField, entityType, fieldMapping.GlobalAccountGUIDField, globalAccountGUID, fieldMapping.DetailsField, details))
   390  }
   391  
   392  func fixTenantEventsResponseBytes(events []byte, total, pages int) resync.TenantEventsResponse {
   393  	return resync.TenantEventsResponse(fmt.Sprintf(`{
   394  		"events":       %s,
   395  		"total": %d,
   396  		"pages":   %d,
   397  	}`, string(events), total, pages))
   398  }
   399  
   400  func eventsToJSONArrayBytes(events ...[]byte) []byte {
   401  	return []byte(fmt.Sprintf(`[%s]`, bytes.Join(events, []byte(","))))
   402  }