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 }