github.com/crossplane/upjet@v1.3.0/pkg/config/externalname_test.go (about)

     1  // SPDX-FileCopyrightText: 2023 The Crossplane Authors <https://crossplane.io>
     2  //
     3  // SPDX-License-Identifier: Apache-2.0
     4  
     5  package config
     6  
     7  import (
     8  	"context"
     9  	"testing"
    10  
    11  	"github.com/crossplane/crossplane-runtime/pkg/errors"
    12  	"github.com/crossplane/crossplane-runtime/pkg/test"
    13  	"github.com/google/go-cmp/cmp"
    14  )
    15  
    16  func TestGetExternalNameFromTemplated(t *testing.T) {
    17  	type args struct {
    18  		tmpl string
    19  		val  string
    20  	}
    21  	type want struct {
    22  		name string
    23  		err  error
    24  	}
    25  	cases := map[string]struct {
    26  		reason string
    27  		args
    28  		want
    29  	}{
    30  		"OnlyExternalName": {
    31  			reason: "Should work with bare external name.",
    32  			args: args{
    33  				tmpl: "{{ .external_name }}",
    34  				val:  "myname",
    35  			},
    36  			want: want{
    37  				name: "myname",
    38  			},
    39  		},
    40  		"ExternalNameWithPrefix": {
    41  			reason: "Should work with prefixed external names.",
    42  			args: args{
    43  				tmpl: "/some:other/prefix:{{ .external_name }}",
    44  				val:  "/some:other/prefix:myname",
    45  			},
    46  			want: want{
    47  				name: "myname",
    48  			},
    49  		},
    50  		"ExternalNameWithSuffix": {
    51  			reason: "Should work with suffixed external name.",
    52  			args: args{
    53  				tmpl: "{{ .external_name }}/olala:{{ .another }}/ola",
    54  				val:  "myname/olala:omama/ola",
    55  			},
    56  			want: want{
    57  				name: "myname",
    58  			},
    59  		},
    60  		"ExternalNameInTheMiddle": {
    61  			reason: "Should work with external name that is both prefixed and suffixed.",
    62  			args: args{
    63  				tmpl: "olala:{{ .external_name }}:omama:{{ .someOther }}",
    64  				val:  "olala:myname:omama:okaka",
    65  			},
    66  			want: want{
    67  				name: "myname",
    68  			},
    69  		},
    70  
    71  		"ExternalNameInTheMiddleWithLessSpaceInTemplateVar": {
    72  			reason: "Should work with external name that is both prefixed and suffixed.",
    73  			args: args{
    74  				tmpl: "olala:{{.external_name}}:omama:{{ .someOther }}",
    75  				val:  "olala:myname:omama:okaka",
    76  			},
    77  			want: want{
    78  				name: "myname",
    79  			},
    80  		},
    81  		"NoExternalNameInTemplate": {
    82  			reason: "Should return the ID intact if there's no {{ .external_name }} variable in the template",
    83  			args: args{
    84  				tmpl: "olala:{{ .another }}:omama:{{ .someOther }}",
    85  				val:  "olala:val1:omama:val2",
    86  			},
    87  			want: want{
    88  				name: "olala:val1:omama:val2",
    89  			},
    90  		},
    91  	}
    92  	for name, tc := range cases {
    93  		t.Run(name, func(t *testing.T) {
    94  			n, err := GetExternalNameFromTemplated(tc.args.tmpl, tc.args.val)
    95  			if diff := cmp.Diff(tc.want.err, err); diff != "" {
    96  				t.Errorf("\n%s\nGetExternalNameFromTemplated(...): -want, +got:\n%s", tc.reason, diff)
    97  			}
    98  			if diff := cmp.Diff(tc.want.name, n); diff != "" {
    99  				t.Errorf("\n%s\nGetExternalNameFromTemplated(...): -want, +got:\n%s", tc.reason, diff)
   100  			}
   101  		})
   102  	}
   103  }
   104  
   105  func TestTemplatedSetIdentifierArgumentFn(t *testing.T) {
   106  	type args struct {
   107  		nameFieldPath string
   108  		base          map[string]any
   109  		externalName  string
   110  	}
   111  	type want struct {
   112  		base map[string]any
   113  	}
   114  	cases := map[string]struct {
   115  		reason string
   116  		args   args
   117  		want   want
   118  	}{
   119  		"NoNameField": {
   120  			reason: "Should be no-op if no name fieldpath given.",
   121  			args: args{
   122  				nameFieldPath: "",
   123  				base:          map[string]any{},
   124  				externalName:  "myname",
   125  			},
   126  			want: want{
   127  				base: map[string]any{},
   128  			},
   129  		},
   130  		"TopLevelSetIdentifier": {
   131  			reason: "Should set top level identifier in arguments.",
   132  			args: args{
   133  				nameFieldPath: "cluster_name",
   134  				base:          map[string]any{},
   135  				externalName:  "myname",
   136  			},
   137  			want: want{
   138  				base: map[string]any{
   139  					"cluster_name": "myname",
   140  				},
   141  			},
   142  		},
   143  		"LeafNodeSetIdentifier": {
   144  			reason: "Should set identifier in arguments even when it is in leaf nodes.",
   145  			args: args{
   146  				nameFieldPath: "cluster_settings.cluster_name",
   147  				base:          map[string]any{},
   148  				externalName:  "myname",
   149  			},
   150  			want: want{
   151  				base: map[string]any{
   152  					"cluster_settings": map[string]any{
   153  						"cluster_name": "myname",
   154  					},
   155  				},
   156  			},
   157  		},
   158  	}
   159  	for n, tc := range cases {
   160  		t.Run(n, func(t *testing.T) {
   161  			TemplatedStringAsIdentifier(tc.args.nameFieldPath, "{{ .external_name }}").SetIdentifierArgumentFn(tc.args.base, tc.args.externalName)
   162  			if diff := cmp.Diff(tc.want.base, tc.args.base); diff != "" {
   163  				t.Fatalf("TemplatedStringAsIdentifier.SetIdentifierArgumentFn(...): -want, +got: %s", diff)
   164  			}
   165  		})
   166  	}
   167  }
   168  
   169  func TestTemplatedGetIDFn(t *testing.T) {
   170  	type args struct {
   171  		tmpl         string
   172  		externalName string
   173  		parameters   map[string]any
   174  		setup        map[string]any
   175  	}
   176  	type want struct {
   177  		id  string
   178  		err error
   179  	}
   180  	cases := map[string]struct {
   181  		reason string
   182  		args   args
   183  		want   want
   184  	}{
   185  		"NoExternalName": {
   186  			reason: "Should work when only external_name is used.",
   187  			args: args{
   188  				tmpl: "olala/{{ .parameters.somethingElse }}",
   189  				parameters: map[string]any{
   190  					"somethingElse": "otherthing",
   191  				},
   192  			},
   193  			want: want{
   194  				id: "olala/otherthing",
   195  			},
   196  		},
   197  		"OnlyExternalName": {
   198  			reason: "Should work when only external_name is used.",
   199  			args: args{
   200  				tmpl:         "olala/{{ .external_name }}",
   201  				externalName: "myname",
   202  			},
   203  			want: want{
   204  				id: "olala/myname",
   205  			},
   206  		},
   207  		"MultipleParameters": {
   208  			reason: "Should work when parameters and terraformProviderConfig are used as well.",
   209  			args: args{
   210  				tmpl:         "olala/{{ .parameters.ola }}:{{ .external_name }}/{{ .setup.configuration.oma }}",
   211  				externalName: "myname",
   212  				parameters: map[string]any{
   213  					"ola": "paramval",
   214  				},
   215  				setup: map[string]any{
   216  					"configuration": map[string]any{
   217  						"oma": "configval",
   218  					},
   219  				},
   220  			},
   221  			want: want{
   222  				id: "olala/paramval:myname/configval",
   223  			},
   224  		},
   225  		"TemplateFunctionToLower": {
   226  			reason: "Should work with a call of ToLower.",
   227  			args: args{
   228  				tmpl:         "olala/{{ .parameters.ola | ToLower }}:{{ .external_name }}/{{ .setup.configuration.oma | ToLower }}",
   229  				externalName: "myname",
   230  				parameters: map[string]any{
   231  					"ola": "ALL_CAPITAL",
   232  				},
   233  				setup: map[string]any{
   234  					"configuration": map[string]any{
   235  						"oma": "CamelCase",
   236  					},
   237  				},
   238  			},
   239  			want: want{
   240  				id: "olala/all_capital:myname/camelcase",
   241  			},
   242  		},
   243  		"TemplateFunctionToUpper": {
   244  			reason: "Should work with a call of ToUpper.",
   245  			args: args{
   246  				tmpl:         "olala/{{ .parameters.ola | ToUpper }}:{{ .external_name }}/{{ .setup.configuration.oma | ToUpper }}",
   247  				externalName: "myname",
   248  				parameters: map[string]any{
   249  					"ola": "all_small",
   250  				},
   251  				setup: map[string]any{
   252  					"configuration": map[string]any{
   253  						"oma": "CamelCase",
   254  					},
   255  				},
   256  			},
   257  			want: want{
   258  				id: "olala/ALL_SMALL:myname/CAMELCASE",
   259  			},
   260  		},
   261  	}
   262  	for n, tc := range cases {
   263  		t.Run(n, func(t *testing.T) {
   264  			id, err := TemplatedStringAsIdentifier("", tc.args.tmpl).
   265  				GetIDFn(context.TODO(),
   266  					tc.args.externalName,
   267  					tc.args.parameters,
   268  					tc.args.setup,
   269  				)
   270  			if diff := cmp.Diff(tc.want.err, err); diff != "" {
   271  				t.Fatalf("TemplatedStringAsIdentifier.GetIDFn(...): -want, +got: %s", diff)
   272  			}
   273  			if diff := cmp.Diff(tc.want.id, id); diff != "" {
   274  				t.Fatalf("TemplatedStringAsIdentifier.GetIDFn(...): -want, +got: %s", diff)
   275  			}
   276  		})
   277  	}
   278  }
   279  
   280  func TestTemplatedGetExternalNameFn(t *testing.T) {
   281  	type args struct {
   282  		tmpl    string
   283  		tfstate map[string]any
   284  	}
   285  	type want struct {
   286  		name string
   287  		err  error
   288  	}
   289  	cases := map[string]struct {
   290  		reason string
   291  		args   args
   292  		want   want
   293  	}{
   294  		"NoExternalName": {
   295  			reason: "Should work when no external_name is used.",
   296  			args: args{
   297  				tmpl: "olala/{{ .parameters.somethingElse }}",
   298  				tfstate: map[string]any{
   299  					"id": "olala/otherthing",
   300  				},
   301  			},
   302  			want: want{
   303  				name: "olala/otherthing",
   304  			},
   305  		},
   306  		"BareExternalName": {
   307  			reason: "Should work when only external_name is used in template.",
   308  			args: args{
   309  				tmpl: "{{ .external_name }}",
   310  				tfstate: map[string]any{
   311  					"id": "myname",
   312  				},
   313  			},
   314  			want: want{
   315  				name: "myname",
   316  			},
   317  		},
   318  		"ExternalNameSpaces": {
   319  			reason: "Should work when external_name variable has random space characters.",
   320  			args: args{
   321  				tmpl: "another/thing:{{  .external_name         }}/something",
   322  				tfstate: map[string]any{
   323  					"id": "another/thing:myname/something",
   324  				},
   325  			},
   326  			want: want{
   327  				name: "myname",
   328  			},
   329  		},
   330  		"DifferentLeftRightSeparators": {
   331  			reason: "Should work when external_name has different left and right separators.",
   332  			args: args{
   333  				tmpl: "another/{{ .parameters.another }}:{{ .external_name }}/somethingelse",
   334  				tfstate: map[string]any{
   335  					"id": "another/thing:myname/somethingelse",
   336  				},
   337  			},
   338  			want: want{
   339  				name: "myname",
   340  			},
   341  		},
   342  		"NoID": {
   343  			reason: "Should not work when ID cannot be found.",
   344  			args: args{
   345  				tmpl: "{{ .external_name }}",
   346  				tfstate: map[string]any{
   347  					"another": "myname",
   348  				},
   349  			},
   350  			want: want{
   351  				err: errors.New(errIDNotFoundInTFState),
   352  			},
   353  		},
   354  	}
   355  	for name, tc := range cases {
   356  		t.Run(name, func(t *testing.T) {
   357  			n, err := TemplatedStringAsIdentifier("", tc.args.tmpl).
   358  				GetExternalNameFn(tc.args.tfstate)
   359  			if diff := cmp.Diff(tc.want.err, err, test.EquateErrors()); diff != "" {
   360  				t.Fatalf("TemplatedStringAsIdentifier.GetExternalNameFn(...): -want, +got: %s", diff)
   361  			}
   362  			if diff := cmp.Diff(tc.want.name, n); diff != "" {
   363  				t.Fatalf("TemplatedStringAsIdentifier.GetExternalNameFn(...): -want, +got: %s", diff)
   364  			}
   365  		})
   366  	}
   367  }