kdnotify/handler/template_test.go

193 lines
4.3 KiB
Go
Raw Normal View History

/*
* 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))
}