github.com/stefanmcshane/helm@v0.0.0-20221213002717-88a4a2c6e77d/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 "github.com/stefanmcshane/helm/pkg/chart" 26 "github.com/stefanmcshane/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", "lookup"} 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 TestRenderRefsOrdering(t *testing.T) { 130 parentChart := &chart.Chart{ 131 Metadata: &chart.Metadata{ 132 Name: "parent", 133 Version: "1.2.3", 134 }, 135 Templates: []*chart.File{ 136 {Name: "templates/_helpers.tpl", Data: []byte(`{{- define "test" -}}parent value{{- end -}}`)}, 137 {Name: "templates/test.yaml", Data: []byte(`{{ tpl "{{ include \"test\" . }}" . }}`)}, 138 }, 139 } 140 childChart := &chart.Chart{ 141 Metadata: &chart.Metadata{ 142 Name: "child", 143 Version: "1.2.3", 144 }, 145 Templates: []*chart.File{ 146 {Name: "templates/_helpers.tpl", Data: []byte(`{{- define "test" -}}child value{{- end -}}`)}, 147 }, 148 } 149 parentChart.AddDependency(childChart) 150 151 expect := map[string]string{ 152 "parent/templates/test.yaml": "parent value", 153 } 154 155 for i := 0; i < 100; i++ { 156 out, err := Render(parentChart, chartutil.Values{}) 157 if err != nil { 158 t.Fatalf("Failed to render templates: %s", err) 159 } 160 161 for name, data := range expect { 162 if out[name] != data { 163 t.Fatalf("Expected %q, got %q (iteration %d)", data, out[name], i+1) 164 } 165 } 166 } 167 } 168 169 func TestRenderInternals(t *testing.T) { 170 // Test the internals of the rendering tool. 171 172 vals := chartutil.Values{"Name": "one", "Value": "two"} 173 tpls := map[string]renderable{ 174 "one": {tpl: `Hello {{title .Name}}`, vals: vals}, 175 "two": {tpl: `Goodbye {{upper .Value}}`, vals: vals}, 176 // Test whether a template can reliably reference another template 177 // without regard for ordering. 178 "three": {tpl: `{{template "two" dict "Value" "three"}}`, vals: vals}, 179 } 180 181 out, err := new(Engine).render(tpls) 182 if err != nil { 183 t.Fatalf("Failed template rendering: %s", err) 184 } 185 186 if len(out) != 3 { 187 t.Fatalf("Expected 3 templates, got %d", len(out)) 188 } 189 190 if out["one"] != "Hello One" { 191 t.Errorf("Expected 'Hello One', got %q", out["one"]) 192 } 193 194 if out["two"] != "Goodbye TWO" { 195 t.Errorf("Expected 'Goodbye TWO'. got %q", out["two"]) 196 } 197 198 if out["three"] != "Goodbye THREE" { 199 t.Errorf("Expected 'Goodbye THREE'. got %q", out["two"]) 200 } 201 } 202 203 func TestParallelRenderInternals(t *testing.T) { 204 // Make sure that we can use one Engine to run parallel template renders. 205 e := new(Engine) 206 var wg sync.WaitGroup 207 for i := 0; i < 20; i++ { 208 wg.Add(1) 209 go func(i int) { 210 tt := fmt.Sprintf("expect-%d", i) 211 tpls := map[string]renderable{ 212 "t": { 213 tpl: `{{.val}}`, 214 vals: map[string]interface{}{"val": tt}, 215 }, 216 } 217 out, err := e.render(tpls) 218 if err != nil { 219 t.Errorf("Failed to render %s: %s", tt, err) 220 } 221 if out["t"] != tt { 222 t.Errorf("Expected %q, got %q", tt, out["t"]) 223 } 224 wg.Done() 225 }(i) 226 } 227 wg.Wait() 228 } 229 230 func TestParseErrors(t *testing.T) { 231 vals := chartutil.Values{"Values": map[string]interface{}{}} 232 233 tplsUndefinedFunction := map[string]renderable{ 234 "undefined_function": {tpl: `{{foo}}`, vals: vals}, 235 } 236 _, err := new(Engine).render(tplsUndefinedFunction) 237 if err == nil { 238 t.Fatalf("Expected failures while rendering: %s", err) 239 } 240 expected := `parse error at (undefined_function:1): function "foo" not defined` 241 if err.Error() != expected { 242 t.Errorf("Expected '%s', got %q", expected, err.Error()) 243 } 244 } 245 246 func TestExecErrors(t *testing.T) { 247 vals := chartutil.Values{"Values": map[string]interface{}{}} 248 cases := []struct { 249 name string 250 tpls map[string]renderable 251 expected string 252 }{ 253 { 254 name: "MissingRequired", 255 tpls: map[string]renderable{ 256 "missing_required": {tpl: `{{required "foo is required" .Values.foo}}`, vals: vals}, 257 }, 258 expected: `execution error at (missing_required:1:2): foo is required`, 259 }, 260 { 261 name: "MissingRequiredWithColons", 262 tpls: map[string]renderable{ 263 "missing_required_with_colons": {tpl: `{{required ":this: message: has many: colons:" .Values.foo}}`, vals: vals}, 264 }, 265 expected: `execution error at (missing_required_with_colons:1:2): :this: message: has many: colons:`, 266 }, 267 { 268 name: "Issue6044", 269 tpls: map[string]renderable{ 270 "issue6044": { 271 vals: vals, 272 tpl: `{{ $someEmptyValue := "" }} 273 {{ $myvar := "abc" }} 274 {{- required (printf "%s: something is missing" $myvar) $someEmptyValue | repeat 0 }}`, 275 }, 276 }, 277 expected: `execution error at (issue6044:3:4): abc: something is missing`, 278 }, 279 { 280 name: "MissingRequiredWithNewlines", 281 tpls: map[string]renderable{ 282 "issue9981": {tpl: `{{required "foo is required\nmore info after the break" .Values.foo}}`, vals: vals}, 283 }, 284 expected: `execution error at (issue9981:1:2): foo is required 285 more info after the break`, 286 }, 287 { 288 name: "FailWithNewlines", 289 tpls: map[string]renderable{ 290 "issue9981": {tpl: `{{fail "something is wrong\nlinebreak"}}`, vals: vals}, 291 }, 292 expected: `execution error at (issue9981:1:2): something is wrong 293 linebreak`, 294 }, 295 } 296 297 for _, tt := range cases { 298 t.Run(tt.name, func(t *testing.T) { 299 _, err := new(Engine).render(tt.tpls) 300 if err == nil { 301 t.Fatalf("Expected failures while rendering: %s", err) 302 } 303 if err.Error() != tt.expected { 304 t.Errorf("Expected %q, got %q", tt.expected, err.Error()) 305 } 306 }) 307 } 308 } 309 310 func TestFailErrors(t *testing.T) { 311 vals := chartutil.Values{"Values": map[string]interface{}{}} 312 313 failtpl := `All your base are belong to us{{ fail "This is an error" }}` 314 tplsFailed := map[string]renderable{ 315 "failtpl": {tpl: failtpl, vals: vals}, 316 } 317 _, err := new(Engine).render(tplsFailed) 318 if err == nil { 319 t.Fatalf("Expected failures while rendering: %s", err) 320 } 321 expected := `execution error at (failtpl:1:33): This is an error` 322 if err.Error() != expected { 323 t.Errorf("Expected '%s', got %q", expected, err.Error()) 324 } 325 326 var e Engine 327 e.LintMode = true 328 out, err := e.render(tplsFailed) 329 if err != nil { 330 t.Fatal(err) 331 } 332 333 expectStr := "All your base are belong to us" 334 if gotStr := out["failtpl"]; gotStr != expectStr { 335 t.Errorf("Expected %q, got %q (%v)", expectStr, gotStr, out) 336 } 337 } 338 339 func TestAllTemplates(t *testing.T) { 340 ch1 := &chart.Chart{ 341 Metadata: &chart.Metadata{Name: "ch1"}, 342 Templates: []*chart.File{ 343 {Name: "templates/foo", Data: []byte("foo")}, 344 {Name: "templates/bar", Data: []byte("bar")}, 345 }, 346 } 347 dep1 := &chart.Chart{ 348 Metadata: &chart.Metadata{Name: "laboratory mice"}, 349 Templates: []*chart.File{ 350 {Name: "templates/pinky", Data: []byte("pinky")}, 351 {Name: "templates/brain", Data: []byte("brain")}, 352 }, 353 } 354 ch1.AddDependency(dep1) 355 356 dep2 := &chart.Chart{ 357 Metadata: &chart.Metadata{Name: "same thing we do every night"}, 358 Templates: []*chart.File{ 359 {Name: "templates/innermost", Data: []byte("innermost")}, 360 }, 361 } 362 dep1.AddDependency(dep2) 363 364 tpls := allTemplates(ch1, chartutil.Values{}) 365 if len(tpls) != 5 { 366 t.Errorf("Expected 5 charts, got %d", len(tpls)) 367 } 368 } 369 370 func TestChartValuesContainsIsRoot(t *testing.T) { 371 ch1 := &chart.Chart{ 372 Metadata: &chart.Metadata{Name: "parent"}, 373 Templates: []*chart.File{ 374 {Name: "templates/isroot", Data: []byte("{{.Chart.IsRoot}}")}, 375 }, 376 } 377 dep1 := &chart.Chart{ 378 Metadata: &chart.Metadata{Name: "child"}, 379 Templates: []*chart.File{ 380 {Name: "templates/isroot", Data: []byte("{{.Chart.IsRoot}}")}, 381 }, 382 } 383 ch1.AddDependency(dep1) 384 385 out, err := Render(ch1, chartutil.Values{}) 386 if err != nil { 387 t.Fatalf("failed to render templates: %s", err) 388 } 389 expects := map[string]string{ 390 "parent/charts/child/templates/isroot": "false", 391 "parent/templates/isroot": "true", 392 } 393 for file, expect := range expects { 394 if out[file] != expect { 395 t.Errorf("Expected %q, got %q", expect, out[file]) 396 } 397 } 398 } 399 400 func TestRenderDependency(t *testing.T) { 401 deptpl := `{{define "myblock"}}World{{end}}` 402 toptpl := `Hello {{template "myblock"}}` 403 ch := &chart.Chart{ 404 Metadata: &chart.Metadata{Name: "outerchart"}, 405 Templates: []*chart.File{ 406 {Name: "templates/outer", Data: []byte(toptpl)}, 407 }, 408 } 409 ch.AddDependency(&chart.Chart{ 410 Metadata: &chart.Metadata{Name: "innerchart"}, 411 Templates: []*chart.File{ 412 {Name: "templates/inner", Data: []byte(deptpl)}, 413 }, 414 }) 415 416 out, err := Render(ch, map[string]interface{}{}) 417 if err != nil { 418 t.Fatalf("failed to render chart: %s", err) 419 } 420 421 if len(out) != 2 { 422 t.Errorf("Expected 2, got %d", len(out)) 423 } 424 425 expect := "Hello World" 426 if out["outerchart/templates/outer"] != expect { 427 t.Errorf("Expected %q, got %q", expect, out["outer"]) 428 } 429 430 } 431 432 func TestRenderNestedValues(t *testing.T) { 433 innerpath := "templates/inner.tpl" 434 outerpath := "templates/outer.tpl" 435 // Ensure namespacing rules are working. 436 deepestpath := "templates/inner.tpl" 437 checkrelease := "templates/release.tpl" 438 // Ensure subcharts scopes are working. 439 subchartspath := "templates/subcharts.tpl" 440 441 deepest := &chart.Chart{ 442 Metadata: &chart.Metadata{Name: "deepest"}, 443 Templates: []*chart.File{ 444 {Name: deepestpath, Data: []byte(`And this same {{.Values.what}} that smiles {{.Values.global.when}}`)}, 445 {Name: checkrelease, Data: []byte(`Tomorrow will be {{default "happy" .Release.Name }}`)}, 446 }, 447 Values: map[string]interface{}{"what": "milkshake", "where": "here"}, 448 } 449 450 inner := &chart.Chart{ 451 Metadata: &chart.Metadata{Name: "herrick"}, 452 Templates: []*chart.File{ 453 {Name: innerpath, Data: []byte(`Old {{.Values.who}} is still a-flyin'`)}, 454 }, 455 Values: map[string]interface{}{"who": "Robert", "what": "glasses"}, 456 } 457 inner.AddDependency(deepest) 458 459 outer := &chart.Chart{ 460 Metadata: &chart.Metadata{Name: "top"}, 461 Templates: []*chart.File{ 462 {Name: outerpath, Data: []byte(`Gather ye {{.Values.what}} while ye may`)}, 463 {Name: subchartspath, Data: []byte(`The glorious Lamp of {{.Subcharts.herrick.Subcharts.deepest.Values.where}}, the {{.Subcharts.herrick.Values.what}}`)}, 464 }, 465 Values: map[string]interface{}{ 466 "what": "stinkweed", 467 "who": "me", 468 "herrick": map[string]interface{}{ 469 "who": "time", 470 "what": "Sun", 471 }, 472 }, 473 } 474 outer.AddDependency(inner) 475 476 injValues := map[string]interface{}{ 477 "what": "rosebuds", 478 "herrick": map[string]interface{}{ 479 "deepest": map[string]interface{}{ 480 "what": "flower", 481 "where": "Heaven", 482 }, 483 }, 484 "global": map[string]interface{}{ 485 "when": "to-day", 486 }, 487 } 488 489 tmp, err := chartutil.CoalesceValues(outer, injValues) 490 if err != nil { 491 t.Fatalf("Failed to coalesce values: %s", err) 492 } 493 494 inject := chartutil.Values{ 495 "Values": tmp, 496 "Chart": outer.Metadata, 497 "Release": chartutil.Values{ 498 "Name": "dyin", 499 }, 500 } 501 502 t.Logf("Calculated values: %v", inject) 503 504 out, err := Render(outer, inject) 505 if err != nil { 506 t.Fatalf("failed to render templates: %s", err) 507 } 508 509 fullouterpath := "top/" + outerpath 510 if out[fullouterpath] != "Gather ye rosebuds while ye may" { 511 t.Errorf("Unexpected outer: %q", out[fullouterpath]) 512 } 513 514 fullinnerpath := "top/charts/herrick/" + innerpath 515 if out[fullinnerpath] != "Old time is still a-flyin'" { 516 t.Errorf("Unexpected inner: %q", out[fullinnerpath]) 517 } 518 519 fulldeepestpath := "top/charts/herrick/charts/deepest/" + deepestpath 520 if out[fulldeepestpath] != "And this same flower that smiles to-day" { 521 t.Errorf("Unexpected deepest: %q", out[fulldeepestpath]) 522 } 523 524 fullcheckrelease := "top/charts/herrick/charts/deepest/" + checkrelease 525 if out[fullcheckrelease] != "Tomorrow will be dyin" { 526 t.Errorf("Unexpected release: %q", out[fullcheckrelease]) 527 } 528 529 fullchecksubcharts := "top/" + subchartspath 530 if out[fullchecksubcharts] != "The glorious Lamp of Heaven, the Sun" { 531 t.Errorf("Unexpected subcharts: %q", out[fullchecksubcharts]) 532 } 533 } 534 535 func TestRenderBuiltinValues(t *testing.T) { 536 inner := &chart.Chart{ 537 Metadata: &chart.Metadata{Name: "Latium"}, 538 Templates: []*chart.File{ 539 {Name: "templates/Lavinia", Data: []byte(`{{.Template.Name}}{{.Chart.Name}}{{.Release.Name}}`)}, 540 {Name: "templates/From", Data: []byte(`{{.Files.author | printf "%s"}} {{.Files.Get "book/title.txt"}}`)}, 541 }, 542 Files: []*chart.File{ 543 {Name: "author", Data: []byte("Virgil")}, 544 {Name: "book/title.txt", Data: []byte("Aeneid")}, 545 }, 546 } 547 548 outer := &chart.Chart{ 549 Metadata: &chart.Metadata{Name: "Troy"}, 550 Templates: []*chart.File{ 551 {Name: "templates/Aeneas", Data: []byte(`{{.Template.Name}}{{.Chart.Name}}{{.Release.Name}}`)}, 552 {Name: "templates/Amata", Data: []byte(`{{.Subcharts.Latium.Chart.Name}} {{.Subcharts.Latium.Files.author | printf "%s"}}`)}, 553 }, 554 } 555 outer.AddDependency(inner) 556 557 inject := chartutil.Values{ 558 "Values": "", 559 "Chart": outer.Metadata, 560 "Release": chartutil.Values{ 561 "Name": "Aeneid", 562 }, 563 } 564 565 t.Logf("Calculated values: %v", outer) 566 567 out, err := Render(outer, inject) 568 if err != nil { 569 t.Fatalf("failed to render templates: %s", err) 570 } 571 572 expects := map[string]string{ 573 "Troy/charts/Latium/templates/Lavinia": "Troy/charts/Latium/templates/LaviniaLatiumAeneid", 574 "Troy/templates/Aeneas": "Troy/templates/AeneasTroyAeneid", 575 "Troy/templates/Amata": "Latium Virgil", 576 "Troy/charts/Latium/templates/From": "Virgil Aeneid", 577 } 578 for file, expect := range expects { 579 if out[file] != expect { 580 t.Errorf("Expected %q, got %q", expect, out[file]) 581 } 582 } 583 584 } 585 586 func TestAlterFuncMap_include(t *testing.T) { 587 c := &chart.Chart{ 588 Metadata: &chart.Metadata{Name: "conrad"}, 589 Templates: []*chart.File{ 590 {Name: "templates/quote", Data: []byte(`{{include "conrad/templates/_partial" . | indent 2}} dead.`)}, 591 {Name: "templates/_partial", Data: []byte(`{{.Release.Name}} - he`)}, 592 }, 593 } 594 595 // Check nested reference in include FuncMap 596 d := &chart.Chart{ 597 Metadata: &chart.Metadata{Name: "nested"}, 598 Templates: []*chart.File{ 599 {Name: "templates/quote", Data: []byte(`{{include "nested/templates/quote" . | indent 2}} dead.`)}, 600 {Name: "templates/_partial", Data: []byte(`{{.Release.Name}} - he`)}, 601 }, 602 } 603 604 v := chartutil.Values{ 605 "Values": "", 606 "Chart": c.Metadata, 607 "Release": chartutil.Values{ 608 "Name": "Mistah Kurtz", 609 }, 610 } 611 612 out, err := Render(c, v) 613 if err != nil { 614 t.Fatal(err) 615 } 616 617 expect := " Mistah Kurtz - he dead." 618 if got := out["conrad/templates/quote"]; got != expect { 619 t.Errorf("Expected %q, got %q (%v)", expect, got, out) 620 } 621 622 _, err = Render(d, v) 623 expectErrName := "nested/templates/quote" 624 if err == nil { 625 t.Errorf("Expected err of nested reference name: %v", expectErrName) 626 } 627 } 628 629 func TestAlterFuncMap_require(t *testing.T) { 630 c := &chart.Chart{ 631 Metadata: &chart.Metadata{Name: "conan"}, 632 Templates: []*chart.File{ 633 {Name: "templates/quote", Data: []byte(`All your base are belong to {{ required "A valid 'who' is required" .Values.who }}`)}, 634 {Name: "templates/bases", Data: []byte(`All {{ required "A valid 'bases' is required" .Values.bases }} of them!`)}, 635 }, 636 } 637 638 v := chartutil.Values{ 639 "Values": chartutil.Values{ 640 "who": "us", 641 "bases": 2, 642 }, 643 "Chart": c.Metadata, 644 "Release": chartutil.Values{ 645 "Name": "That 90s meme", 646 }, 647 } 648 649 out, err := Render(c, v) 650 if err != nil { 651 t.Fatal(err) 652 } 653 654 expectStr := "All your base are belong to us" 655 if gotStr := out["conan/templates/quote"]; gotStr != expectStr { 656 t.Errorf("Expected %q, got %q (%v)", expectStr, gotStr, out) 657 } 658 expectNum := "All 2 of them!" 659 if gotNum := out["conan/templates/bases"]; gotNum != expectNum { 660 t.Errorf("Expected %q, got %q (%v)", expectNum, gotNum, out) 661 } 662 663 // test required without passing in needed values with lint mode on 664 // verifies lint replaces required with an empty string (should not fail) 665 lintValues := chartutil.Values{ 666 "Values": chartutil.Values{ 667 "who": "us", 668 }, 669 "Chart": c.Metadata, 670 "Release": chartutil.Values{ 671 "Name": "That 90s meme", 672 }, 673 } 674 var e Engine 675 e.LintMode = true 676 out, err = e.Render(c, lintValues) 677 if err != nil { 678 t.Fatal(err) 679 } 680 681 expectStr = "All your base are belong to us" 682 if gotStr := out["conan/templates/quote"]; gotStr != expectStr { 683 t.Errorf("Expected %q, got %q (%v)", expectStr, gotStr, out) 684 } 685 expectNum = "All of them!" 686 if gotNum := out["conan/templates/bases"]; gotNum != expectNum { 687 t.Errorf("Expected %q, got %q (%v)", expectNum, gotNum, out) 688 } 689 } 690 691 func TestAlterFuncMap_tpl(t *testing.T) { 692 c := &chart.Chart{ 693 Metadata: &chart.Metadata{Name: "TplFunction"}, 694 Templates: []*chart.File{ 695 {Name: "templates/base", Data: []byte(`Evaluate tpl {{tpl "Value: {{ .Values.value}}" .}}`)}, 696 }, 697 } 698 699 v := chartutil.Values{ 700 "Values": chartutil.Values{ 701 "value": "myvalue", 702 }, 703 "Chart": c.Metadata, 704 "Release": chartutil.Values{ 705 "Name": "TestRelease", 706 }, 707 } 708 709 out, err := Render(c, v) 710 if err != nil { 711 t.Fatal(err) 712 } 713 714 expect := "Evaluate tpl Value: myvalue" 715 if got := out["TplFunction/templates/base"]; got != expect { 716 t.Errorf("Expected %q, got %q (%v)", expect, got, out) 717 } 718 } 719 720 func TestAlterFuncMap_tplfunc(t *testing.T) { 721 c := &chart.Chart{ 722 Metadata: &chart.Metadata{Name: "TplFunction"}, 723 Templates: []*chart.File{ 724 {Name: "templates/base", Data: []byte(`Evaluate tpl {{tpl "Value: {{ .Values.value | quote}}" .}}`)}, 725 }, 726 } 727 728 v := chartutil.Values{ 729 "Values": chartutil.Values{ 730 "value": "myvalue", 731 }, 732 "Chart": c.Metadata, 733 "Release": chartutil.Values{ 734 "Name": "TestRelease", 735 }, 736 } 737 738 out, err := Render(c, v) 739 if err != nil { 740 t.Fatal(err) 741 } 742 743 expect := "Evaluate tpl Value: \"myvalue\"" 744 if got := out["TplFunction/templates/base"]; got != expect { 745 t.Errorf("Expected %q, got %q (%v)", expect, got, out) 746 } 747 } 748 749 func TestAlterFuncMap_tplinclude(t *testing.T) { 750 c := &chart.Chart{ 751 Metadata: &chart.Metadata{Name: "TplFunction"}, 752 Templates: []*chart.File{ 753 {Name: "templates/base", Data: []byte(`{{ tpl "{{include ` + "`" + `TplFunction/templates/_partial` + "`" + ` . | quote }}" .}}`)}, 754 {Name: "templates/_partial", Data: []byte(`{{.Template.Name}}`)}, 755 }, 756 } 757 v := chartutil.Values{ 758 "Values": chartutil.Values{ 759 "value": "myvalue", 760 }, 761 "Chart": c.Metadata, 762 "Release": chartutil.Values{ 763 "Name": "TestRelease", 764 }, 765 } 766 767 out, err := Render(c, v) 768 if err != nil { 769 t.Fatal(err) 770 } 771 772 expect := "\"TplFunction/templates/base\"" 773 if got := out["TplFunction/templates/base"]; got != expect { 774 t.Errorf("Expected %q, got %q (%v)", expect, got, out) 775 } 776 777 } 778 779 func TestRenderRecursionLimit(t *testing.T) { 780 // endless recursion should produce an error 781 c := &chart.Chart{ 782 Metadata: &chart.Metadata{Name: "bad"}, 783 Templates: []*chart.File{ 784 {Name: "templates/base", Data: []byte(`{{include "recursion" . }}`)}, 785 {Name: "templates/recursion", Data: []byte(`{{define "recursion"}}{{include "recursion" . }}{{end}}`)}, 786 }, 787 } 788 v := chartutil.Values{ 789 "Values": "", 790 "Chart": c.Metadata, 791 "Release": chartutil.Values{ 792 "Name": "TestRelease", 793 }, 794 } 795 expectErr := "rendering template has a nested reference name: recursion: unable to execute template" 796 797 _, err := Render(c, v) 798 if err == nil || !strings.HasSuffix(err.Error(), expectErr) { 799 t.Errorf("Expected err with suffix: %s", expectErr) 800 } 801 802 // calling the same function many times is ok 803 times := 4000 804 phrase := "All work and no play makes Jack a dull boy" 805 printFunc := `{{define "overlook"}}{{printf "` + phrase + `\n"}}{{end}}` 806 var repeatedIncl string 807 for i := 0; i < times; i++ { 808 repeatedIncl += `{{include "overlook" . }}` 809 } 810 811 d := &chart.Chart{ 812 Metadata: &chart.Metadata{Name: "overlook"}, 813 Templates: []*chart.File{ 814 {Name: "templates/quote", Data: []byte(repeatedIncl)}, 815 {Name: "templates/_function", Data: []byte(printFunc)}, 816 }, 817 } 818 819 out, err := Render(d, v) 820 if err != nil { 821 t.Fatal(err) 822 } 823 824 var expect string 825 for i := 0; i < times; i++ { 826 expect += phrase + "\n" 827 } 828 if got := out["overlook/templates/quote"]; got != expect { 829 t.Errorf("Expected %q, got %q (%v)", expect, got, out) 830 } 831 832 }