/* * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ package handler import ( "fmt" "os/exec" "testing" "text/template" "github.com/stretchr/testify/assert" ) func Test_runTemplate(t *testing.T) { t.Parallel() cases := []struct { Name string Template *template.Template Env TemplateEnv Expected string ShouldError bool HandlerFunc func(t *testing.T, expected, actual string) bool }{ { Name: "Empty", Template: s2t(""), }, { Name: "TextOnly", Template: s2t("hello, this is a text only template"), Expected: "hello, this is a text only template", }, { Name: "WithEvent", Template: s2t("{{.Event.Instance}}:{{.Event.State}}:{{.Event.Type}}"), Expected: "foo_instance:MASTER:INSTANCE", Env: TemplateEnv{ Event: MessageEvent{ Instance: "foo_instance", Type: "INSTANCE", State: "MASTER", }, }, }, { Name: "WithCxt", Template: s2t("{{.Cxt.key1}}:{{.Cxt.key2.subkey1}}:{{.Cxt.UpCase.Key}}"), Expected: "value1:value2:value3", Env: TemplateEnv{ Cxt: map[string]any{ "key1": "value1", "key2": map[string]string{ "subkey1": "value2", }, "UpCase": map[string]string{ "Key": "value3", }, }, }, }, { Name: "WithCxtAndEvent", Template: s2t("{{.Event.Instance}}->{{.Cxt.key1}}:{{.Cxt.key2.subkey1}}:{{.Cxt.UpCase.Key}}"), Expected: "baz_instance->value1:value2:value3", Env: TemplateEnv{ Cxt: map[string]any{ "key1": "value1", "key2": map[string]string{ "subkey1": "value2", }, "UpCase": map[string]string{ "Key": "value3", }, }, Event: MessageEvent{ Instance: "baz_instance", }, }, }, } for i, test := range cases { test := test // Avoid closure capture by ref tname := fmt.Sprintf("%s@%d:%d", test.Name, i+1, len(cases)) t.Run(tname, func(t *testing.T) { t.Parallel() if test.HandlerFunc == nil { test.HandlerFunc = Noop[string] if test.Expected != "" { test.HandlerFunc = ExpectEqual[string] if test.ShouldError { test.HandlerFunc = ExpectNotEqual[string] } } } actual, err := runTemplate(test.Template, &test.Env) if test.ShouldError && err != nil { return } else if assert.NoError(t, err) { test.HandlerFunc(t, test.Expected, actual) } }) } } func Test_mkCmd(t *testing.T) { t.Parallel() cases := []struct { Name string Cmdline string Expected *exec.Cmd ShouldError bool HandlerFunc func(t *testing.T, expected, actual *exec.Cmd) bool }{ { Name: "Empty", Cmdline: "", ShouldError: true, }, { Name: "CmdOnly", Cmdline: "/bin/echo", Expected: exec.Command("/bin/echo"), }, { Name: "WithArgsSimple", Cmdline: "/bin/echo hello, world!", Expected: exec.Command("/bin/echo", "hello,", "world!"), }, { Name: "WithArgsEscape", Cmdline: `/bin/echo \$ESCAPED $VAR`, Expected: exec.Command("/bin/echo", `$ESCAPED`, "$VAR"), }, { Name: "WithArgsQuoted", Cmdline: `/bin/echo "hello, world" ... 'these have spaces'`, Expected: exec.Command("/bin/echo", "hello, world", "...", "these have spaces"), }, } for i, test := range cases { test := test // Avoid closure capture by ref tname := fmt.Sprintf("%s@%d:%d", test.Name, i+1, len(cases)) t.Run(tname, func(t *testing.T) { t.Parallel() if test.HandlerFunc == nil { test.HandlerFunc = Noop[*exec.Cmd] if test.Expected != nil { test.HandlerFunc = ExpectEqual[*exec.Cmd] if test.ShouldError { test.HandlerFunc = ExpectNotEqual[*exec.Cmd] } } } actual, err := mkCmd(test.Cmdline) if test.ShouldError && err != nil { return } else if assert.NoError(t, err) { test.HandlerFunc(t, test.Expected, actual) } }) } } func ExpectEqual[T any](t *testing.T, expected, actual T) bool { return assert.Equal(t, expected, actual) } func ExpectNotEqual[T any](t *testing.T, expected, actual T) bool { return assert.NotEqual(t, expected, actual) } func Noop[T any](_ *testing.T, _, _ T) bool { return true } func s2t(s string) *template.Template { return template.Must(template.New("TestHandler").Parse(s)) }