github.com/umeshredd/helm@v3.0.0-alpha.1+incompatible/pkg/engine/engine_test.go (about) 1 /* 2 Copyright The Helm Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package engine 18 19 import ( 20 "fmt" 21 "strings" 22 "sync" 23 "testing" 24 25 "helm.sh/helm/pkg/chart" 26 "helm.sh/helm/pkg/chartutil" 27 ) 28 29 func TestSortTemplates(t *testing.T) { 30 tpls := map[string]renderable{ 31 "/mychart/templates/foo.tpl": {}, 32 "/mychart/templates/charts/foo/charts/bar/templates/foo.tpl": {}, 33 "/mychart/templates/bar.tpl": {}, 34 "/mychart/templates/charts/foo/templates/bar.tpl": {}, 35 "/mychart/templates/_foo.tpl": {}, 36 "/mychart/templates/charts/foo/templates/foo.tpl": {}, 37 "/mychart/templates/charts/bar/templates/foo.tpl": {}, 38 } 39 got := sortTemplates(tpls) 40 if len(got) != len(tpls) { 41 t.Fatal("Sorted results are missing templates") 42 } 43 44 expect := []string{ 45 "/mychart/templates/charts/foo/charts/bar/templates/foo.tpl", 46 "/mychart/templates/charts/foo/templates/foo.tpl", 47 "/mychart/templates/charts/foo/templates/bar.tpl", 48 "/mychart/templates/charts/bar/templates/foo.tpl", 49 "/mychart/templates/foo.tpl", 50 "/mychart/templates/bar.tpl", 51 "/mychart/templates/_foo.tpl", 52 } 53 for i, e := range expect { 54 if got[i] != e { 55 t.Fatalf("\n\tExp:\n%s\n\tGot:\n%s", 56 strings.Join(expect, "\n"), 57 strings.Join(got, "\n"), 58 ) 59 } 60 } 61 } 62 63 func TestFuncMap(t *testing.T) { 64 fns := funcMap() 65 forbidden := []string{"env", "expandenv"} 66 for _, f := range forbidden { 67 if _, ok := fns[f]; ok { 68 t.Errorf("Forbidden function %s exists in FuncMap.", f) 69 } 70 } 71 72 // Test for Engine-specific template functions. 73 expect := []string{"include", "required", "tpl", "toYaml", "fromYaml", "toToml", "toJson", "fromJson"} 74 for _, f := range expect { 75 if _, ok := fns[f]; !ok { 76 t.Errorf("Expected add-on function %q", f) 77 } 78 } 79 } 80 81 func TestRender(t *testing.T) { 82 c := &chart.Chart{ 83 Metadata: &chart.Metadata{ 84 Name: "moby", 85 Version: "1.2.3", 86 }, 87 Templates: []*chart.File{ 88 {Name: "templates/test1", Data: []byte("{{.Values.outer | title }} {{.Values.inner | title}}")}, 89 {Name: "templates/test2", Data: []byte("{{.Values.global.callme | lower }}")}, 90 {Name: "templates/test3", Data: []byte("{{.noValue}}")}, 91 {Name: "templates/test4", Data: []byte("{{toJson .Values}}")}, 92 }, 93 Values: map[string]interface{}{"outer": "DEFAULT", "inner": "DEFAULT"}, 94 } 95 96 vals := map[string]interface{}{ 97 "Values": map[string]interface{}{ 98 "outer": "spouter", 99 "inner": "inn", 100 "global": map[string]interface{}{ 101 "callme": "Ishmael", 102 }, 103 }, 104 } 105 106 v, err := chartutil.CoalesceValues(c, vals) 107 if err != nil { 108 t.Fatalf("Failed to coalesce values: %s", err) 109 } 110 out, err := Render(c, v) 111 if err != nil { 112 t.Errorf("Failed to render templates: %s", err) 113 } 114 115 expect := map[string]string{ 116 "moby/templates/test1": "Spouter Inn", 117 "moby/templates/test2": "ishmael", 118 "moby/templates/test3": "", 119 "moby/templates/test4": `{"global":{"callme":"Ishmael"},"inner":"inn","outer":"spouter"}`, 120 } 121 122 for name, data := range expect { 123 if out[name] != data { 124 t.Errorf("Expected %q, got %q", data, out[name]) 125 } 126 } 127 } 128 129 func TestRenderInternals(t *testing.T) { 130 // Test the internals of the rendering tool. 131 132 vals := chartutil.Values{"Name": "one", "Value": "two"} 133 tpls := map[string]renderable{ 134 "one": {tpl: `Hello {{title .Name}}`, vals: vals}, 135 "two": {tpl: `Goodbye {{upper .Value}}`, vals: vals}, 136 // Test whether a template can reliably reference another template 137 // without regard for ordering. 138 "three": {tpl: `{{template "two" dict "Value" "three"}}`, vals: vals}, 139 } 140 141 out, err := new(Engine).render(tpls) 142 if err != nil { 143 t.Fatalf("Failed template rendering: %s", err) 144 } 145 146 if len(out) != 3 { 147 t.Fatalf("Expected 3 templates, got %d", len(out)) 148 } 149 150 if out["one"] != "Hello One" { 151 t.Errorf("Expected 'Hello One', got %q", out["one"]) 152 } 153 154 if out["two"] != "Goodbye TWO" { 155 t.Errorf("Expected 'Goodbye TWO'. got %q", out["two"]) 156 } 157 158 if out["three"] != "Goodbye THREE" { 159 t.Errorf("Expected 'Goodbye THREE'. got %q", out["two"]) 160 } 161 } 162 163 func TestParallelRenderInternals(t *testing.T) { 164 // Make sure that we can use one Engine to run parallel template renders. 165 e := new(Engine) 166 var wg sync.WaitGroup 167 for i := 0; i < 20; i++ { 168 wg.Add(1) 169 go func(i int) { 170 tt := fmt.Sprintf("expect-%d", i) 171 tpls := map[string]renderable{ 172 "t": { 173 tpl: `{{.val}}`, 174 vals: map[string]interface{}{"val": tt}, 175 }, 176 } 177 out, err := e.render(tpls) 178 if err != nil { 179 t.Errorf("Failed to render %s: %s", tt, err) 180 } 181 if out["t"] != tt { 182 t.Errorf("Expected %q, got %q", tt, out["t"]) 183 } 184 wg.Done() 185 }(i) 186 } 187 wg.Wait() 188 } 189 190 func TestRenderErrors(t *testing.T) { 191 vals := chartutil.Values{"Values": map[string]interface{}{}} 192 193 tplsMissingRequired := map[string]renderable{ 194 "missing_required": {tpl: `{{required "foo is required" .Values.foo}}`, vals: vals}, 195 } 196 _, err := new(Engine).render(tplsMissingRequired) 197 if err == nil { 198 t.Fatalf("Expected failures while rendering: %s", err) 199 } 200 expected := `render error at (missing_required:1:2): foo is required` 201 if err.Error() != expected { 202 t.Errorf("Expected '%s', got %q", expected, err.Error()) 203 } 204 205 tplsUndefinedFunction := map[string]renderable{ 206 "undefined_function": {tpl: `{{foo}}`, vals: vals}, 207 } 208 _, err = new(Engine).render(tplsUndefinedFunction) 209 if err == nil { 210 t.Fatalf("Expected failures while rendering: %s", err) 211 } 212 expected = `render error at (undefined_function:1): function "foo" not defined` 213 if err.Error() != expected { 214 t.Errorf("Expected '%s', got %q", expected, err.Error()) 215 } 216 } 217 func TestAllTemplates(t *testing.T) { 218 ch1 := &chart.Chart{ 219 Metadata: &chart.Metadata{Name: "ch1"}, 220 Templates: []*chart.File{ 221 {Name: "templates/foo", Data: []byte("foo")}, 222 {Name: "templates/bar", Data: []byte("bar")}, 223 }, 224 } 225 dep1 := &chart.Chart{ 226 Metadata: &chart.Metadata{Name: "laboratory mice"}, 227 Templates: []*chart.File{ 228 {Name: "templates/pinky", Data: []byte("pinky")}, 229 {Name: "templates/brain", Data: []byte("brain")}, 230 }, 231 } 232 ch1.AddDependency(dep1) 233 234 dep2 := &chart.Chart{ 235 Metadata: &chart.Metadata{Name: "same thing we do every night"}, 236 Templates: []*chart.File{ 237 {Name: "templates/innermost", Data: []byte("innermost")}, 238 }, 239 } 240 dep1.AddDependency(dep2) 241 242 tpls := allTemplates(ch1, chartutil.Values{}) 243 if len(tpls) != 5 { 244 t.Errorf("Expected 5 charts, got %d", len(tpls)) 245 } 246 } 247 248 func TestRenderDependency(t *testing.T) { 249 deptpl := `{{define "myblock"}}World{{end}}` 250 toptpl := `Hello {{template "myblock"}}` 251 ch := &chart.Chart{ 252 Metadata: &chart.Metadata{Name: "outerchart"}, 253 Templates: []*chart.File{ 254 {Name: "templates/outer", Data: []byte(toptpl)}, 255 }, 256 } 257 ch.AddDependency(&chart.Chart{ 258 Metadata: &chart.Metadata{Name: "innerchart"}, 259 Templates: []*chart.File{ 260 {Name: "templates/inner", Data: []byte(deptpl)}, 261 }, 262 }) 263 264 out, err := Render(ch, map[string]interface{}{}) 265 if err != nil { 266 t.Fatalf("failed to render chart: %s", err) 267 } 268 269 if len(out) != 2 { 270 t.Errorf("Expected 2, got %d", len(out)) 271 } 272 273 expect := "Hello World" 274 if out["outerchart/templates/outer"] != expect { 275 t.Errorf("Expected %q, got %q", expect, out["outer"]) 276 } 277 278 } 279 280 func TestRenderNestedValues(t *testing.T) { 281 innerpath := "templates/inner.tpl" 282 outerpath := "templates/outer.tpl" 283 // Ensure namespacing rules are working. 284 deepestpath := "templates/inner.tpl" 285 checkrelease := "templates/release.tpl" 286 287 deepest := &chart.Chart{ 288 Metadata: &chart.Metadata{Name: "deepest"}, 289 Templates: []*chart.File{ 290 {Name: deepestpath, Data: []byte(`And this same {{.Values.what}} that smiles {{.Values.global.when}}`)}, 291 {Name: checkrelease, Data: []byte(`Tomorrow will be {{default "happy" .Release.Name }}`)}, 292 }, 293 Values: map[string]interface{}{"what": "milkshake"}, 294 } 295 296 inner := &chart.Chart{ 297 Metadata: &chart.Metadata{Name: "herrick"}, 298 Templates: []*chart.File{ 299 {Name: innerpath, Data: []byte(`Old {{.Values.who}} is still a-flyin'`)}, 300 }, 301 Values: map[string]interface{}{"who": "Robert"}, 302 } 303 inner.AddDependency(deepest) 304 305 outer := &chart.Chart{ 306 Metadata: &chart.Metadata{Name: "top"}, 307 Templates: []*chart.File{ 308 {Name: outerpath, Data: []byte(`Gather ye {{.Values.what}} while ye may`)}, 309 }, 310 Values: map[string]interface{}{ 311 "what": "stinkweed", 312 "who": "me", 313 "herrick": map[string]interface{}{ 314 "who": "time", 315 }, 316 }, 317 } 318 outer.AddDependency(inner) 319 320 injValues := map[string]interface{}{ 321 "what": "rosebuds", 322 "herrick": map[string]interface{}{ 323 "deepest": map[string]interface{}{ 324 "what": "flower", 325 }, 326 }, 327 "global": map[string]interface{}{ 328 "when": "to-day", 329 }, 330 } 331 332 tmp, err := chartutil.CoalesceValues(outer, injValues) 333 if err != nil { 334 t.Fatalf("Failed to coalesce values: %s", err) 335 } 336 337 inject := chartutil.Values{ 338 "Values": tmp, 339 "Chart": outer.Metadata, 340 "Release": chartutil.Values{ 341 "Name": "dyin", 342 }, 343 } 344 345 t.Logf("Calculated values: %v", inject) 346 347 out, err := Render(outer, inject) 348 if err != nil { 349 t.Fatalf("failed to render templates: %s", err) 350 } 351 352 fullouterpath := "top/" + outerpath 353 if out[fullouterpath] != "Gather ye rosebuds while ye may" { 354 t.Errorf("Unexpected outer: %q", out[fullouterpath]) 355 } 356 357 fullinnerpath := "top/charts/herrick/" + innerpath 358 if out[fullinnerpath] != "Old time is still a-flyin'" { 359 t.Errorf("Unexpected inner: %q", out[fullinnerpath]) 360 } 361 362 fulldeepestpath := "top/charts/herrick/charts/deepest/" + deepestpath 363 if out[fulldeepestpath] != "And this same flower that smiles to-day" { 364 t.Errorf("Unexpected deepest: %q", out[fulldeepestpath]) 365 } 366 367 fullcheckrelease := "top/charts/herrick/charts/deepest/" + checkrelease 368 if out[fullcheckrelease] != "Tomorrow will be dyin" { 369 t.Errorf("Unexpected release: %q", out[fullcheckrelease]) 370 } 371 } 372 373 func TestRenderBuiltinValues(t *testing.T) { 374 inner := &chart.Chart{ 375 Metadata: &chart.Metadata{Name: "Latium"}, 376 Templates: []*chart.File{ 377 {Name: "templates/Lavinia", Data: []byte(`{{.Template.Name}}{{.Chart.Name}}{{.Release.Name}}`)}, 378 {Name: "templates/From", Data: []byte(`{{.Files.author | printf "%s"}} {{.Files.Get "book/title.txt"}}`)}, 379 }, 380 Files: []*chart.File{ 381 {Name: "author", Data: []byte("Virgil")}, 382 {Name: "book/title.txt", Data: []byte("Aeneid")}, 383 }, 384 } 385 386 outer := &chart.Chart{ 387 Metadata: &chart.Metadata{Name: "Troy"}, 388 Templates: []*chart.File{ 389 {Name: "templates/Aeneas", Data: []byte(`{{.Template.Name}}{{.Chart.Name}}{{.Release.Name}}`)}, 390 }, 391 } 392 outer.AddDependency(inner) 393 394 inject := chartutil.Values{ 395 "Values": "", 396 "Chart": outer.Metadata, 397 "Release": chartutil.Values{ 398 "Name": "Aeneid", 399 }, 400 } 401 402 t.Logf("Calculated values: %v", outer) 403 404 out, err := Render(outer, inject) 405 if err != nil { 406 t.Fatalf("failed to render templates: %s", err) 407 } 408 409 expects := map[string]string{ 410 "Troy/charts/Latium/templates/Lavinia": "Troy/charts/Latium/templates/LaviniaLatiumAeneid", 411 "Troy/templates/Aeneas": "Troy/templates/AeneasTroyAeneid", 412 "Troy/charts/Latium/templates/From": "Virgil Aeneid", 413 } 414 for file, expect := range expects { 415 if out[file] != expect { 416 t.Errorf("Expected %q, got %q", expect, out[file]) 417 } 418 } 419 420 } 421 422 func TestAlterFuncMap_include(t *testing.T) { 423 c := &chart.Chart{ 424 Metadata: &chart.Metadata{Name: "conrad"}, 425 Templates: []*chart.File{ 426 {Name: "templates/quote", Data: []byte(`{{include "conrad/templates/_partial" . | indent 2}} dead.`)}, 427 {Name: "templates/_partial", Data: []byte(`{{.Release.Name}} - he`)}, 428 }, 429 } 430 431 v := chartutil.Values{ 432 "Values": "", 433 "Chart": c.Metadata, 434 "Release": chartutil.Values{ 435 "Name": "Mistah Kurtz", 436 }, 437 } 438 439 out, err := Render(c, v) 440 if err != nil { 441 t.Fatal(err) 442 } 443 444 expect := " Mistah Kurtz - he dead." 445 if got := out["conrad/templates/quote"]; got != expect { 446 t.Errorf("Expected %q, got %q (%v)", expect, got, out) 447 } 448 } 449 450 func TestAlterFuncMap_require(t *testing.T) { 451 c := &chart.Chart{ 452 Metadata: &chart.Metadata{Name: "conan"}, 453 Templates: []*chart.File{ 454 {Name: "templates/quote", Data: []byte(`All your base are belong to {{ required "A valid 'who' is required" .Values.who }}`)}, 455 {Name: "templates/bases", Data: []byte(`All {{ required "A valid 'bases' is required" .Values.bases }} of them!`)}, 456 }, 457 } 458 459 v := chartutil.Values{ 460 "Values": chartutil.Values{ 461 "who": "us", 462 "bases": 2, 463 }, 464 "Chart": c.Metadata, 465 "Release": chartutil.Values{ 466 "Name": "That 90s meme", 467 }, 468 } 469 470 out, err := Render(c, v) 471 if err != nil { 472 t.Fatal(err) 473 } 474 475 expectStr := "All your base are belong to us" 476 if gotStr := out["conan/templates/quote"]; gotStr != expectStr { 477 t.Errorf("Expected %q, got %q (%v)", expectStr, gotStr, out) 478 } 479 expectNum := "All 2 of them!" 480 if gotNum := out["conan/templates/bases"]; gotNum != expectNum { 481 t.Errorf("Expected %q, got %q (%v)", expectNum, gotNum, out) 482 } 483 } 484 485 func TestAlterFuncMap_tpl(t *testing.T) { 486 c := &chart.Chart{ 487 Metadata: &chart.Metadata{Name: "TplFunction"}, 488 Templates: []*chart.File{ 489 {Name: "templates/base", Data: []byte(`Evaluate tpl {{tpl "Value: {{ .Values.value}}" .}}`)}, 490 }, 491 } 492 493 v := chartutil.Values{ 494 "Values": chartutil.Values{ 495 "value": "myvalue", 496 }, 497 "Chart": c.Metadata, 498 "Release": chartutil.Values{ 499 "Name": "TestRelease", 500 }, 501 } 502 503 out, err := Render(c, v) 504 if err != nil { 505 t.Fatal(err) 506 } 507 508 expect := "Evaluate tpl Value: myvalue" 509 if got := out["TplFunction/templates/base"]; got != expect { 510 t.Errorf("Expected %q, got %q (%v)", expect, got, out) 511 } 512 } 513 514 func TestAlterFuncMap_tplfunc(t *testing.T) { 515 c := &chart.Chart{ 516 Metadata: &chart.Metadata{Name: "TplFunction"}, 517 Templates: []*chart.File{ 518 {Name: "templates/base", Data: []byte(`Evaluate tpl {{tpl "Value: {{ .Values.value | quote}}" .}}`)}, 519 }, 520 } 521 522 v := chartutil.Values{ 523 "Values": chartutil.Values{ 524 "value": "myvalue", 525 }, 526 "Chart": c.Metadata, 527 "Release": chartutil.Values{ 528 "Name": "TestRelease", 529 }, 530 } 531 532 out, err := Render(c, v) 533 if err != nil { 534 t.Fatal(err) 535 } 536 537 expect := "Evaluate tpl Value: \"myvalue\"" 538 if got := out["TplFunction/templates/base"]; got != expect { 539 t.Errorf("Expected %q, got %q (%v)", expect, got, out) 540 } 541 } 542 543 func TestAlterFuncMap_tplinclude(t *testing.T) { 544 c := &chart.Chart{ 545 Metadata: &chart.Metadata{Name: "TplFunction"}, 546 Templates: []*chart.File{ 547 {Name: "templates/base", Data: []byte(`{{ tpl "{{include ` + "`" + `TplFunction/templates/_partial` + "`" + ` . | quote }}" .}}`)}, 548 {Name: "templates/_partial", Data: []byte(`{{.Template.Name}}`)}, 549 }, 550 } 551 v := chartutil.Values{ 552 "Values": chartutil.Values{ 553 "value": "myvalue", 554 }, 555 "Chart": c.Metadata, 556 "Release": chartutil.Values{ 557 "Name": "TestRelease", 558 }, 559 } 560 561 out, err := Render(c, v) 562 if err != nil { 563 t.Fatal(err) 564 } 565 566 expect := "\"TplFunction/templates/base\"" 567 if got := out["TplFunction/templates/base"]; got != expect { 568 t.Errorf("Expected %q, got %q (%v)", expect, got, out) 569 } 570 571 }