Repository: kyverno/kyverno
Pull request #6219 (2023-02-03 15:41:14)

chore: add a few unit tests for jp functions
Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com>

## Explanation

This PR adds a few unit tests for jp functions.

NOTE: waiting for https://github.com/kyverno/go-jmespath/pull/8 to avoid unnecessary heap allocations and dereferencing

codecov[bot]: # [Codecov](https://codecov.io/gh/kyverno/kyverno/pull/6219?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=kyverno) Report
> Merging [#6219](https://codecov.io/gh/kyverno/kyverno/pull/6219?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=kyverno) (84cf903) into [main](https://codecov.io/gh/kyverno/kyverno/commit/9268b67cebb1358deaf38378307b85da4d6dbe56?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=kyverno) (9268b67) will **increase** coverage by `0.05%`.
> The diff coverage is `100.00%`.

```diff
@@            Coverage Diff             @@
##             main    #6219      +/-   ##
==========================================
+ Coverage   37.18%   37.23%   +0.05%     
==========================================
  Files         197      197              
  Lines       21046    21039       -7     
==========================================
+ Hits         7825     7833       +8     
+ Misses      12404    12394      -10     
+ Partials      817      812       -5     
```


| [Impacted Files](https://codecov.io/gh/kyverno/kyverno/pull/6219?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=kyverno) | Coverage Δ | |
|---|---|---|
| [pkg/engine/jmespath/functions.go](https://codecov.io/gh/kyverno/kyverno/pull/6219?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=kyverno#diff-cGtnL2VuZ2luZS9qbWVzcGF0aC9mdW5jdGlvbnMuZ28=) | `81.46% <100.00%> (+1.50%)` | :arrow_up: |

:mega: We’re building smart automated test selection to slash your CI/CD build times. [Learn more](https://about.codecov.io/iterative-testing/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=kyverno)


Patch:
diff --git a/cmd/cli/kubectl-kyverno/jp/function/function.go b/cmd/cli/kubectl-kyverno/jp/function/function.go
index c2eb6c78d..92b22e984 100644
--- a/cmd/cli/kubectl-kyverno/jp/function/function.go
+++ b/cmd/cli/kubectl-kyverno/jp/function/function.go
@@ -35,16 +35,15 @@ func Command() *cobra.Command {
 
 func printFunctions(names ...string) {
 	functions := jmespath.GetFunctions()
-	slices.SortFunc(functions, func(a, b *jmespath.FunctionEntry) bool {
+	slices.SortFunc(functions, func(a, b jmespath.FunctionEntry) bool {
 		return a.String() < b.String()
 	})
 	namesSet := sets.New(names...)
 	for _, function := range functions {
-		if len(namesSet) == 0 || namesSet.Has(function.Entry.Name) {
-			function := *function
+		if len(namesSet) == 0 || namesSet.Has(function.Name) {
 			note := function.Note
 			function.Note = ""
-			fmt.Println("Name:", function.Entry.Name)
+			fmt.Println("Name:", function.Name)
 			fmt.Println("  Signature:", function.String())
 			if note != "" {
 				fmt.Println("  Note:     ", note)
diff --git a/pkg/engine/jmespath/functionentry.go b/pkg/engine/jmespath/functionentry.go
new file mode 100644
index 000000000..27eea74d2
--- /dev/null
+++ b/pkg/engine/jmespath/functionentry.go
@@ -0,0 +1,52 @@
+package jmespath
+
+import (
+	"fmt"
+	"strings"
+
+	gojmespath "github.com/jmespath/go-jmespath"
+)
+
+var (
+	jpObject      = gojmespath.JpObject
+	jpString      = gojmespath.JpString
+	jpNumber      = gojmespath.JpNumber
+	jpArray       = gojmespath.JpArray
+	jpArrayString = gojmespath.JpArrayString
+	jpAny         = gojmespath.JpAny
+	jpBool        = gojmespath.JpType("bool")
+)
+
+type (
+	jpType  = gojmespath.JpType
+	argSpec = gojmespath.ArgSpec
+)
+
+type FunctionEntry struct {
+	gojmespath.FunctionEntry
+	Note       string
+	ReturnType []jpType
+}
+
+func (f FunctionEntry) String() string {
+	if f.Name == "" {
+		return ""
+	}
+	var args []string
+	for _, a := range f.Arguments {
+		var aTypes []string
+		for _, t := range a.Types {
+			aTypes = append(aTypes, string(t))
+		}
+		args = append(args, strings.Join(aTypes, "|"))
+	}
+	var returnArgs []string
+	for _, ra := range f.ReturnType {
+		returnArgs = append(returnArgs, string(ra))
+	}
+	output := fmt.Sprintf("%s(%s) %s", f.Name, strings.Join(args, ", "), strings.Join(returnArgs, ","))
+	if f.Note != "" {
+		output += fmt.Sprintf(" (%s)", f.Note)
+	}
+	return output
+}
diff --git a/pkg/engine/jmespath/functions.go b/pkg/engine/jmespath/functions.go
index 688b6d91e..c9a270557 100644
--- a/pkg/engine/jmespath/functions.go
+++ b/pkg/engine/jmespath/functions.go
@@ -28,21 +28,6 @@ import (
 	"sigs.k8s.io/yaml"
 )
 
-var (
-	JpObject      = gojmespath.JpObject
-	JpString      = gojmespath.JpString
-	JpNumber      = gojmespath.JpNumber
-	JpArray       = gojmespath.JpArray
-	JpArrayString = gojmespath.JpArrayString
-	JpAny         = gojmespath.JpAny
-	JpBool        = gojmespath.JpType("bool")
-)
-
-type (
-	JpType  = gojmespath.JpType
-	ArgSpec = gojmespath.ArgSpec
-)
-
 type PublicKey struct {
 	N string
 	E int
@@ -90,517 +75,449 @@ const (
 	nonIntModuloError        = errorPrefix + "Non-integer argument(s) passed for modulo"
 )
 
-type FunctionEntry struct {
-	Entry      *gojmespath.FunctionEntry
-	Note       string
-	ReturnType []JpType
-}
-
-func (f *FunctionEntry) String() string {
-	args := []string{}
-	for _, a := range f.Entry.Arguments {
-		aTypes := []string{}
-		for _, t := range a.Types {
-			aTypes = append(aTypes, string(t))
-		}
-		args = append(args, strings.Join(aTypes, "|"))
-	}
-	returnArgs := []string{}
-	for _, ra := range f.ReturnType {
-		returnArgs = append(returnArgs, string(ra))
-	}
-	output := fmt.Sprintf("%s(%s) %s", f.Entry.Name, strings.Join(args, ", "), strings.Join(returnArgs, ","))
-	if f.Note != "" {
-		output += fmt.Sprintf(" (%s)", f.Note)
-	}
-	return output
-}
-
-func GetFunctions() []*FunctionEntry {
-	return []*FunctionEntry{
-		{
-			Entry: &gojmespath.FunctionEntry{
-				Name: compare,
-				Arguments: []ArgSpec{
-					{Types: []JpType{JpString}},
-					{Types: []JpType{JpString}},
-				},
-				Handler: jpfCompare,
-			},
-			ReturnType: []JpType{JpNumber},
-			Note:       "compares two strings lexicographically",
-		},
-		{
-			Entry: &gojmespath.FunctionEntry{
-				Name: equalFold,
-				Arguments: []ArgSpec{
-					{Types: []JpType{JpString}},
-					{Types: []JpType{JpString}},
-				},
-				Handler: jpfEqualFold,
-			},
-			ReturnType: []JpType{JpBool},
-			Note:       "allows comparing two strings for equivalency where the only differences are letter cases",
-		},
-		{
-			Entry: &gojmespath.FunctionEntry{
-				Name: replace,
-				Arguments: []ArgSpec{
-					{Types: []JpType{JpString}},
-					{Types: []JpType{JpString}},
-					{Types: []JpType{JpString}},
-					{Types: []JpType{JpNumber}},
-				},
-				Handler: jpfReplace,
-			},
-			ReturnType: []JpType{JpString},
-			Note:       "replaces a specified number of instances of the source string with the replacement string in a parent ",
-		},
-		{
-			Entry: &gojmespath.FunctionEntry{
-				Name: replaceAll,
-				Arguments: []ArgSpec{
-					{Types: []JpType{JpString}},
-					{Types: []JpType{JpString}},
-					{Types: []JpType{JpString}},
-				},
-				Handler: jpfReplaceAll,
-			},
-			ReturnType: []JpType{JpString},
-			Note:       "replace all instances of one string with another in an overall parent string",
-		},
-		{
-			Entry: &gojmespath.FunctionEntry{
-				Name: toUpper,
-				Arguments: []ArgSpec{
-					{Types: []JpType{JpString}},
-				},
-				Handler: jpfToUpper,
-			},
-			ReturnType: []JpType{JpString},
-			Note:       "takes in a string and outputs the same string with all upper-case letters",
-		},
-		{
-			Entry: &gojmespath.FunctionEntry{
-				Name: toLower,
-				Arguments: []ArgSpec{
-					{Types: []JpType{JpString}},
-				},
-				Handler: jpfToLower,
-			},
-			ReturnType: []JpType{JpString},
-			Note:       "takes in a string and outputs the same string with all lower-case letters",
-		},
-		{
-			Entry: &gojmespath.FunctionEntry{
-				Name: trim,
-				Arguments: []ArgSpec{
-					{Types: []JpType{JpString}},
-					{Types: []JpType{JpString}},
-				},
-				Handler: jpfTrim,
-			},
-			ReturnType: []JpType{JpString},
-			Note:       "trims both ends of the source string by characters appearing in the second string",
-		},
-		{
-			Entry: &gojmespath.FunctionEntry{
-				Name: split,
-				Arguments: []ArgSpec{
-					{Types: []JpType{JpString}},
-					{Types: []JpType{JpString}},
-				},
-				Handler: jpfSplit,
-			},
-			ReturnType: []JpType{JpArrayString},
-			Note:       "splits the first string when the second string is found and converts it into an array ",
-		},
-		{
-			Entry: &gojmespath.FunctionEntry{
-				Name: regexReplaceAll,
-				Arguments: []ArgSpec{
-					{Types: []JpType{JpString}},
-					{Types: []JpType{JpString, JpNumber}},
-					{Types: []JpType{JpString, JpNumber}},
-				},
-				Handler: jpRegexReplaceAll,
-			},
-			ReturnType: []JpType{JpString},
-			Note:       "converts all parameters to string",
-		},
-		{
-			Entry: &gojmespath.FunctionEntry{
-				Name: regexReplaceAllLiteral,
-				Arguments: []ArgSpec{
-					{Types: []JpType{JpString}},
-					{Types: []JpType{JpString, JpNumber}},
-					{Types: []JpType{JpString, JpNumber}},
-				},
-				Handler: jpRegexReplaceAllLiteral,
-			},
-			ReturnType: []JpType{JpString},
-			Note:       "converts all parameters to string",
-		},
-		{
-			Entry: &gojmespath.FunctionEntry{
-				Name: regexMatch,
-				Arguments: []ArgSpec{
-					{Types: []JpType{JpString}},
-					{Types: []JpType{JpString, JpNumber}},
-				},
-				Handler: jpRegexMatch,
-			},
-			ReturnType: []JpType{JpBool},
-			Note:       "first string is the regular exression which is compared with second input which can be a number or string",
-		},
-		{
-			Entry: &gojmespath.FunctionEntry{
-				Name: patternMatch,
-				Arguments: []ArgSpec{
-					{Types: []JpType{JpString}},
-					{Types: []JpType{JpString, JpNumber}},
-				},
-				Handler: jpPatternMatch,
-			},
-			ReturnType: []JpType{JpBool},
-			Note:       "'*' matches zero or more alphanumeric characters, '?' matches a single alphanumeric character",
-		},
-		{
-			// Validates if label (param1) would match pod/host/etc labels (param2)
-			Entry: &gojmespath.FunctionEntry{
-				Name: labelMatch,
-				Arguments: []ArgSpec{
-					{Types: []JpType{JpObject}},
-					{Types: []JpType{JpObject}},
-				},
-				Handler: jpLabelMatch,
-			},
-			ReturnType: []JpType{JpBool},
-			Note:       "object arguments must be enclosed in backticks; ex. `{{request.object.spec.template.metadata.labels}}`",
-		},
-		{
-			Entry: &gojmespath.FunctionEntry{
-				Name: add,
-				Arguments: []ArgSpec{
-					{Types: []JpType{JpAny}},
-					{Types: []JpType{JpAny}},
-				},
-				Handler: jpAdd,
-			},
-			ReturnType: []JpType{JpAny},
-			Note:       "does arithmetic addition of two specified values of numbers, quantities, and durations",
-		},
-		{
-			Entry: &gojmespath.FunctionEntry{
-				Name: subtract,
-				Arguments: []ArgSpec{
-					{Types: []JpType{JpAny}},
-					{Types: []JpType{JpAny}},
-				},
-				Handler: jpSubtract,
-			},
-			ReturnType: []JpType{JpAny},
-			Note:       "does arithmetic subtraction of two specified values of numbers, quantities, and durations",
-		},
-		{
-			Entry: &gojmespath.FunctionEntry{
-				Name: multiply,
-				Arguments: []ArgSpec{
-					{Types: []JpType{JpAny}},
-					{Types: []JpType{JpAny}},
-				},
-				Handler: jpMultiply,
-			},
-			ReturnType: []JpType{JpAny},
-			Note:       "does arithmetic multiplication of two specified values of numbers, quantities, and durations",
-		},
-		{
-			Entry: &gojmespath.FunctionEntry{
-				Name: divide,
-				Arguments: []ArgSpec{
-					{Types: []JpType{JpAny}},
-					{Types: []JpType{JpAny}},
-				},
-				Handler: jpDivide,
-			},
-			ReturnType: []JpType{JpAny},
-			Note:       "divisor must be non zero",
-		},
-		{
-			Entry: &gojmespath.FunctionEntry{
-				Name: modulo,
-				Arguments: []ArgSpec{
-					{Types: []JpType{JpAny}},
-					{Types: []JpType{JpAny}},
-				},
-				Handler: jpModulo,
-			},
-			ReturnType: []JpType{JpAny},
-			Note:       "divisor must be non-zero, arguments must be integers",
-		},
-		{
-			Entry: &gojmespath.FunctionEntry{
-				Name: base64Decode,
-				Arguments: []ArgSpec{
-					{Types: []JpType{JpString}},
-				},
-				Handler: jpBase64Decode,
-			},
-			ReturnType: []JpType{JpString},
-			Note:       "decodes a base 64 string",
-		},
-		{
-			Entry: &gojmespath.FunctionEntry{
-				Name: base64Encode,
-				Arguments: []ArgSpec{
-					{Types: []JpType{JpString}},
-				},
-				Handler: jpBase64Encode,
-			},
-			ReturnType: []JpType{JpString},
-			Note:       "encodes a regular, plaintext and unencoded string to base64",
-		},
-		{
-			Entry: &gojmespath.FunctionEntry{
-				Name: timeSince,
-				Arguments: []ArgSpec{
-					{Types: []JpType{JpString}},
-					{Types: []JpType{JpString}},
-					{Types: []JpType{JpString}},
-				},
-				Handler: jpTimeSince,
-			},
-			ReturnType: []JpType{JpString},
-			Note:       "calculate the difference between a start and end period of time where the end may either be a static definition or the then-current time",
-		},
-		{
-			Entry: &gojmespath.FunctionEntry{
-				Name:    timeNow,
-				Handler: jpTimeNow,
-			},
-			ReturnType: []JpType{JpString},
-			Note:       "returns current time in RFC 3339 format",
-		},
-		{
-			Entry: &gojmespath.FunctionEntry{
-				Name:    timeNowUtc,
-				Handler: jpTimeNowUtc,
-			},
-			ReturnType: []JpType{JpString},
-			Note:       "returns current UTC time in RFC 3339 format",
-		},
-		{
-			Entry: &gojmespath.FunctionEntry{
-				Name: pathCanonicalize,
-				Arguments: []ArgSpec{
-					{Types: []JpType{JpString}},
-				},
-				Handler: jpPathCanonicalize,
-			},
-			ReturnType: []JpType{JpString},
-			Note:       "normalizes or canonicalizes a given path by removing excess slashes",
-		},
-		{
-			Entry: &gojmespath.FunctionEntry{
-				Name: truncate,
-				Arguments: []ArgSpec{
-					{Types: []JpType{JpString}},
-					{Types: []JpType{JpNumber}},
-				},
-				Handler: jpTruncate,
-			},
-			ReturnType: []JpType{JpString},
-			Note:       "length argument must be enclosed in backticks; ex. \"{{request.object.metadata.name | truncate(@, `9`)}}\"",
-		},
-		{
-			Entry: &gojmespath.FunctionEntry{
-				Name: semverCompare,
-				Arguments: []ArgSpec{
-					{Types: []JpType{JpString}},
-					{Types: []JpType{JpString}},
-				},
-				Handler: jpSemverCompare,
-			},
-			ReturnType: []JpType{JpBool},
-			Note:       "compares two strings which comply with the semantic versioning schema and outputs a boolean response as to the position of the second relative to the first",
-		},
-		{
-			Entry: &gojmespath.FunctionEntry{
-				Name: parseJson,
-				Arguments: []ArgSpec{
-					{Types: []JpType{JpString}},
-				},
-				Handler: jpParseJson,
-			},
-			ReturnType: []JpType{JpAny},
-			Note:       "decodes a valid JSON encoded string to the appropriate type. Opposite of `to_string` function",
-		},
-		{
-			Entry: &gojmespath.FunctionEntry{
-				Name: parseYAML,
-				Arguments: []ArgSpec{
-					{Types: []JpType{JpString}},
-				},
-				Handler: jpParseYAML,
-			},
-			ReturnType: []JpType{JpAny},
-			Note:       "decodes a valid YAML encoded string to the appropriate type provided it can be represented as JSON",
-		},
-		{
-			Entry: &gojmespath.FunctionEntry{
-				Name: items,
-				Arguments: []ArgSpec{
-					{Types: []JpType{JpObject, JpArray}},
-					{Types: []JpType{JpString}},
-					{Types: []JpType{JpString}},
-				},
-				Handler: jpItems,
-			},
-			ReturnType: []JpType{JpArray},
-			Note:       "converts a map or array to an array of objects where each key:value is an item in the array",
-		},
-		{
-			Entry: &gojmespath.FunctionEntry{
-				Name: objectFromLists,
-				Arguments: []ArgSpec{
-					{Types: []JpType{JpArray}},
-					{Types: []JpType{JpArray}},
-				},
-				Handler: jpObjectFromLists,
-			},
-			ReturnType: []JpType{JpObject},
-			Note:       "converts a pair of lists containing keys and values to an object",
-		},
-		{
-			Entry: &gojmespath.FunctionEntry{
-				Name: random,
-				Arguments: []ArgSpec{
-					{Types: []JpType{JpString}},
-				},
-				Handler: jpRandom,
-			},
-			ReturnType: []JpType{JpString},
-			Note:       "Generates a random sequence of characters",
-		},
-		{
-			Entry: &gojmespath.FunctionEntry{
-				Name: x509_decode,
-				Arguments: []ArgSpec{
-					{Types: []JpType{JpString}},
-				},
-				Handler: jpX509Decode,
-			},
-			ReturnType: []JpType{JpObject},
-			Note:       "decodes an x.509 certificate to an object. you may also use this in conjunction with `base64_decode` jmespath function to decode a base64-encoded certificate",
-		},
-		{
-			Entry: &gojmespath.FunctionEntry{
-				Name: timeToCron,
-				Arguments: []ArgSpec{
-					{Types: []JpType{JpString}},
-				},
-				Handler: jpTimeToCron,
-			},
-			ReturnType: []JpType{JpString},
-			Note:       "converts a time (RFC 3339) to a cron expression (string).",
-		},
-		{
-			Entry: &gojmespath.FunctionEntry{
-				Name: timeAdd,
-				Arguments: []ArgSpec{
-					{Types: []JpType{JpString}},
-					{Types: []JpType{JpString}},
-				},
-				Handler: jpTimeAdd,
-			},
-			ReturnType: []JpType{JpString},
-			Note:       "adds duration (second string) to a time value (first string)",
-		},
-		{
-			Entry: &gojmespath.FunctionEntry{
-				Name: timeParse,
-				Arguments: []ArgSpec{
-					{Types: []JpType{JpString}},
-					{Types: []JpType{JpString}},
-				},
-				Handler: jpTimeParse,
-			},
-			ReturnType: []JpType{JpString},
-			Note:       "changes a time value of a given layout to RFC 3339",
-		},
-		{
-			Entry: &gojmespath.FunctionEntry{
-				Name: timeUtc,
-				Arguments: []ArgSpec{
-					{Types: []JpType{JpString}},
-				},
-				Handler: jpTimeUtc,
-			},
-			ReturnType: []JpType{JpString},
-			Note:       "calcutes time in UTC from a given time in RFC 3339 format",
-		},
-		{
-			Entry: &gojmespath.FunctionEntry{
-				Name: timeDiff,
-				Arguments: []ArgSpec{
-					{Types: []JpType{JpString}},
-					{Types: []JpType{JpString}},
-				},
-				Handler: jpTimeDiff,
-			},
-			ReturnType: []JpType{JpString},
-			Note:       "calculate the difference between a start and end date in RFC3339 format",
-		},
-		{
-			Entry: &gojmespath.FunctionEntry{
-				Name: timeBefore,
-				Arguments: []ArgSpec{
-					{Types: []JpType{JpString}},
-					{Types: []JpType{JpString}},
-				},
-				Handler: jpTimeBefore,
-			},
-			ReturnType: []JpType{JpBool},
-			Note:       "checks if a time is before another time, both in RFC3339 format",
-		},
-		{
-			Entry: &gojmespath.FunctionEntry{
-				Name: timeAfter,
-				Arguments: []ArgSpec{
-					{Types: []JpType{JpString}},
-					{Types: []JpType{JpString}},
-				},
-				Handler: jpTimeAfter,
-			},
-			ReturnType: []JpType{JpBool},
-			Note:       "checks if a time is after another time, both in RFC3339 format",
-		},
-		{
-			Entry: &gojmespath.FunctionEntry{
-				Name: timeBetween,
-				Arguments: []ArgSpec{
-					{Types: []JpType{JpString}},
-					{Types: []JpType{JpString}},
-					{Types: []JpType{JpString}},
-				},
-				Handler: jpTimeBetween,
-			},
-			ReturnType: []JpType{JpBool},
-			Note:       "checks if a time is between a start and end time, all in RFC3339 format",
-		},
-		{
-			Entry: &gojmespath.FunctionEntry{
-				Name: timeTruncate,
-				Arguments: []ArgSpec{
-					{Types: []JpType{JpString}},
-					{Types: []JpType{JpString}},
-				},
-				Handler: jpTimeTruncate,
-			},
-			ReturnType: []JpType{JpString},
-			Note:       "returns the result of rounding time down to a multiple of duration",
-		},
-	}
+func GetFunctions() []FunctionEntry {
+	return []FunctionEntry{{
+		FunctionEntry: gojmespath.FunctionEntry{
+			Name: compare,
+			Arguments: []argSpec{
+				{Types: []jpType{jpString}},
+				{Types: []jpType{jpString}},
+			},
+			Handler: jpfCompare,
+		},
+		ReturnType: []jpType{jpNumber},
+		Note:       "compares two strings lexicographically",
+	}, {
+		FunctionEntry: gojmespath.FunctionEntry{
+			Name: equalFold,
+			Arguments: []argSpec{
+				{Types: []jpType{jpString}},
+				{Types: []jpType{jpString}},
+			},
+			Handler: jpfEqualFold,
+		},
+		ReturnType: []jpType{jpBool},
+		Note:       "allows comparing two strings for equivalency where the only differences are letter cases",
+	}, {
+		FunctionEntry: gojmespath.FunctionEntry{
+			Name: replace,
+			Arguments: []argSpec{
+				{Types: []jpType{jpString}},
+				{Types: []jpType{jpString}},
+				{Types: []jpType{jpString}},
+				{Types: []jpType{jpNumber}},
+			},
+			Handler: jpfReplace,
+		},
+		ReturnType: []jpType{jpString},
+		Note:       "replaces a specified number of instances of the source string with the replacement string in a parent ",
+	}, {
+		FunctionEntry: gojmespath.FunctionEntry{
+			Name: replaceAll,
+			Arguments: []argSpec{
+				{Types: []jpType{jpString}},
+				{Types: []jpType{jpString}},
+				{Types: []jpType{jpString}},
+			},
+			Handler: jpfReplaceAll,
+		},
+		ReturnType: []jpType{jpString},
+		Note:       "replace all instances of one string with another in an overall parent string",
+	}, {
+		FunctionEntry: gojmespath.FunctionEntry{
+			Name: toUpper,
+			Arguments: []argSpec{
+				{Types: []jpType{jpString}},
+			},
+			Handler: jpfToUpper,
+		},
+		ReturnType: []jpType{jpString},
+		Note:       "takes in a string and outputs the same string with all upper-case letters",
+	}, {
+		FunctionEntry: gojmespath.FunctionEntry{
+			Name: toLower,
+			Arguments: []argSpec{
+				{Types: []jpType{jpString}},
+			},
+			Handler: jpfToLower,
+		},
+		ReturnType: []jpType{jpString},
+		Note:       "takes in a string and outputs the same string with all lower-case letters",
+	}, {
+		FunctionEntry: gojmespath.FunctionEntry{
+			Name: trim,
+			Arguments: []argSpec{
+				{Types: []jpType{jpString}},
+				{Types: []jpType{jpString}},
+			},
+			Handler: jpfTrim,
+		},
+		ReturnType: []jpType{jpString},
+		Note:       "trims both ends of the source string by characters appearing in the second string",
+	}, {
+		FunctionEntry: gojmespath.FunctionEntry{
+			Name: split,
+			Arguments: []argSpec{
+				{Types: []jpType{jpString}},
+				{Types: []jpType{jpString}},
+			},
+			Handler: jpfSplit,
+		},
+		ReturnType: []jpType{jpArrayString},
+		Note:       "splits the first string when the second string is found and converts it into an array ",
+	}, {
+		FunctionEntry: gojmespath.FunctionEntry{
+			Name: regexReplaceAll,
+			Arguments: []argSpec{
+				{Types: []jpType{jpString}},
+				{Types: []jpType{jpString, jpNumber}},
+				{Types: []jpType{jpString, jpNumber}},
+			},
+			Handler: jpRegexReplaceAll,
+		},
+		ReturnType: []jpType{jpString},
+		Note:       "converts all parameters to string",
+	}, {
+		FunctionEntry: gojmespath.FunctionEntry{
+			Name: regexReplaceAllLiteral,
+			Arguments: []argSpec{
+				{Types: []jpType{jpString}},
+				{Types: []jpType{jpString, jpNumber}},
+				{Types: []jpType{jpString, jpNumber}},
+			},
+			Handler: jpRegexReplaceAllLiteral,
+		},
+		ReturnType: []jpType{jpString},
+		Note:       "converts all parameters to string",
+	}, {
+		FunctionEntry: gojmespath.FunctionEntry{
+			Name: regexMatch,
+			Arguments: []argSpec{
+				{Types: []jpType{jpString}},
+				{Types: []jpType{jpString, jpNumber}},
+			},
+			Handler: jpRegexMatch,
+		},
+		ReturnType: []jpType{jpBool},
+		Note:       "first string is the regular exression which is compared with second input which can be a number or string",
+	}, {
+		FunctionEntry: gojmespath.FunctionEntry{
+			Name: patternMatch,
+			Arguments: []argSpec{
+				{Types: []jpType{jpString}},
+				{Types: []jpType{jpString, jpNumber}},
+			},
+			Handler: jpPatternMatch,
+		},
+		ReturnType: []jpType{jpBool},
+		Note:       "'*' matches zero or more alphanumeric characters, '?' matches a single alphanumeric character",
+	}, {
+		// Validates if label (param1) would match pod/host/etc labels (param2)
+		FunctionEntry: gojmespath.FunctionEntry{
+			Name: labelMatch,
+			Arguments: []argSpec{
+				{Types: []jpType{jpObject}},
+				{Types: []jpType{jpObject}},
+			},
+			Handler: jpLabelMatch,
+		},
+		ReturnType: []jpType{jpBool},
+		Note:       "object arguments must be enclosed in backticks; ex. `{{request.object.spec.template.metadata.labels}}`",
+	}, {
+		FunctionEntry: gojmespath.FunctionEntry{
+			Name: add,
+			Arguments: []argSpec{
+				{Types: []jpType{jpAny}},
+				{Types: []jpType{jpAny}},
+			},
+			Handler: jpAdd,
+		},
+		ReturnType: []jpType{jpAny},
+		Note:       "does arithmetic addition of two specified values of numbers, quantities, and durations",
+	}, {
+		FunctionEntry: gojmespath.FunctionEntry{
+			Name: subtract,
+			Arguments: []argSpec{
+				{Types: []jpType{jpAny}},
+				{Types: []jpType{jpAny}},
+			},
+			Handler: jpSubtract,
+		},
+		ReturnType: []jpType{jpAny},
+		Note:       "does arithmetic subtraction of two specified values of numbers, quantities, and durations",
+	}, {
+		FunctionEntry: gojmespath.FunctionEntry{
+			Name: multiply,
+			Arguments: []argSpec{
+				{Types: []jpType{jpAny}},
+				{Types: []jpType{jpAny}},
+			},
+			Handler: jpMultiply,
+		},
+		ReturnType: []jpType{jpAny},
+		Note:       "does arithmetic multiplication of two specified values of numbers, quantities, and durations",
+	}, {
+		FunctionEntry: gojmespath.FunctionEntry{
+			Name: divide,
+			Arguments: []argSpec{
+				{Types: []jpType{jpAny}},
+				{Types: []jpType{jpAny}},
+			},
+			Handler: jpDivide,
+		},
+		ReturnType: []jpType{jpAny},
+		Note:       "divisor must be non zero",
+	}, {
+		FunctionEntry: gojmespath.FunctionEntry{
+			Name: modulo,
+			Arguments: []argSpec{
+				{Types: []jpType{jpAny}},
+				{Types: []jpType{jpAny}},
+			},
+			Handler: jpModulo,
+		},
+		ReturnType: []jpType{jpAny},
+		Note:       "divisor must be non-zero, arguments must be integers",
+	}, {
+		FunctionEntry: gojmespath.FunctionEntry{
+			Name: base64Decode,
+			Arguments: []argSpec{
+				{Types: []jpType{jpString}},
+			},
+			Handler: jpBase64Decode,
+		},
+		ReturnType: []jpType{jpString},
+		Note:       "decodes a base 64 string",
+	}, {
+		FunctionEntry: gojmespath.FunctionEntry{
+			Name: base64Encode,
+			Arguments: []argSpec{
+				{Types: []jpType{jpString}},
+			},
+			Handler: jpBase64Encode,
+		},
+		ReturnType: []jpType{jpString},
+		Note:       "encodes a regular, plaintext and unencoded string to base64",
+	}, {
+		FunctionEntry: gojmespath.FunctionEntry{
+			Name: timeSince,
+			Arguments: []argSpec{
+				{Types: []jpType{jpString}},
+				{Types: []jpType{jpString}},
+				{Types: []jpType{jpString}},
+			},
+			Handler: jpTimeSince,
+		},
+		ReturnType: []jpType{jpString},
+		Note:       "calculate the difference between a start and end period of time where the end may either be a static definition or the then-current time",
+	}, {
+		FunctionEntry: gojmespath.FunctionEntry{
+			Name:    timeNow,
+			Handler: jpTimeNow,
+		},
+		ReturnType: []jpType{jpString},
+		Note:       "returns current time in RFC 3339 format",
+	}, {
+		FunctionEntry: gojmespath.FunctionEntry{
+			Name:    timeNowUtc,
+			Handler: jpTimeNowUtc,
+		},
+		ReturnType: []jpType{jpString},
+		Note:       "returns current UTC time in RFC 3339 format",
+	}, {
+		FunctionEntry: gojmespath.FunctionEntry{
+			Name: pathCanonicalize,
+			Arguments: []argSpec{
+				{Types: []jpType{jpString}},
+			},
+			Handler: jpPathCanonicalize,
+		},
+		ReturnType: []jpType{jpString},
+		Note:       "normalizes or canonicalizes a given path by removing excess slashes",
+	}, {
+		FunctionEntry: gojmespath.FunctionEntry{
+			Name: truncate,
+			Arguments: []argSpec{
+				{Types: []jpType{jpString}},
+				{Types: []jpType{jpNumber}},
+			},
+			Handler: jpTruncate,
+		},
+		ReturnType: []jpType{jpString},
+		Note:       "length argument must be enclosed in backticks; ex. \"{{request.object.metadata.name | truncate(@, `9`)}}\"",
+	}, {
+		FunctionEntry: gojmespath.FunctionEntry{
+			Name: semverCompare,
+			Arguments: []argSpec{
+				{Types: []jpType{jpString}},
+				{Types: []jpType{jpString}},
+			},
+			Handler: jpSemverCompare,
+		},
+		ReturnType: []jpType{jpBool},
+		Note:       "compares two strings which comply with the semantic versioning schema and outputs a boolean response as to the position of the second relative to the first",
+	}, {
+		FunctionEntry: gojmespath.FunctionEntry{
+			Name: parseJson,
+			Arguments: []argSpec{
+				{Types: []jpType{jpString}},
+			},
+			Handler: jpParseJson,
+		},
+		ReturnType: []jpType{jpAny},
+		Note:       "decodes a valid JSON encoded string to the appropriate type. Opposite of `to_string` function",
+	}, {
+		FunctionEntry: gojmespath.FunctionEntry{
+			Name: parseYAML,
+			Arguments: []argSpec{
+				{Types: []jpType{jpString}},
+			},
+			Handler: jpParseYAML,
+		},
+		ReturnType: []jpType{jpAny},
+		Note:       "decodes a valid YAML encoded string to the appropriate type provided it can be represented as JSON",
+	}, {
+		FunctionEntry: gojmespath.FunctionEntry{
+			Name: items,
+			Arguments: []argSpec{
+				{Types: []jpType{jpObject, jpArray}},
+				{Types: []jpType{jpString}},
+				{Types: []jpType{jpString}},
+			},
+			Handler: jpItems,
+		},
+		ReturnType: []jpType{jpArray},
+		Note:       "converts a map or array to an array of objects where each key:value is an item in the array",
+	}, {
+		FunctionEntry: gojmespath.FunctionEntry{
+			Name: objectFromLists,
+			Arguments: []argSpec{
+				{Types: []jpType{jpArray}},
+				{Types: []jpType{jpArray}},
+			},
+			Handler: jpObjectFromLists,
+		},
+		ReturnType: []jpType{jpObject},
+		Note:       "converts a pair of lists containing keys and values to an object",
+	}, {
+		FunctionEntry: gojmespath.FunctionEntry{
+			Name: random,
+			Arguments: []argSpec{
+				{Types: []jpType{jpString}},
+			},
+			Handler: jpRandom,
+		},
+		ReturnType: []jpType{jpString},
+		Note:       "Generates a random sequence of characters",
+	}, {
+		FunctionEntry: gojmespath.FunctionEntry{
+			Name: x509_decode,
+			Arguments: []argSpec{
+				{Types: []jpType{jpString}},
+			},
+			Handler: jpX509Decode,
+		},
+		ReturnType: []jpType{jpObject},
+		Note:       "decodes an x.509 certificate to an object. you may also use this in conjunction with `base64_decode` jmespath function to decode a base64-encoded certificate",
+	}, {
+		FunctionEntry: gojmespath.FunctionEntry{
+			Name: timeToCron,
+			Arguments: []argSpec{
+				{Types: []jpType{jpString}},
+			},
+			Handler: jpTimeToCron,
+		},
+		ReturnType: []jpType{jpString},
+		Note:       "converts a time (RFC 3339) to a cron expression (string).",
+	}, {
+		FunctionEntry: gojmespath.FunctionEntry{
+			Name: timeAdd,
+			Arguments: []argSpec{
+				{Types: []jpType{jpString}},
+				{Types: []jpType{jpString}},
+			},
+			Handler: jpTimeAdd,
+		},
+		ReturnType: []jpType{jpString},
+		Note:       "adds duration (second string) to a time value (first string)",
+	}, {
+		FunctionEntry: gojmespath.FunctionEntry{
+			Name: timeParse,
+			Arguments: []argSpec{
+				{Types: []jpType{jpString}},
+				{Types: []jpType{jpString}},
+			},
+			Handler: jpTimeParse,
+		},
+		ReturnType: []jpType{jpString},
+		Note:       "changes a time value of a given layout to RFC 3339",
+	}, {
+		FunctionEntry: gojmespath.FunctionEntry{
+			Name: timeUtc,
+			Arguments: []argSpec{
+				{Types: []jpType{jpString}},
+			},
+			Handler: jpTimeUtc,
+		},
+		ReturnType: []jpType{jpString},
+		Note:       "calcutes time in UTC from a given time in RFC 3339 format",
+	}, {
+		FunctionEntry: gojmespath.FunctionEntry{
+			Name: timeDiff,
+			Arguments: []argSpec{
+				{Types: []jpType{jpString}},
+				{Types: []jpType{jpString}},
+			},
+			Handler: jpTimeDiff,
+		},
+		ReturnType: []jpType{jpString},
+		Note:       "calculate the difference between a start and end date in RFC3339 format",
+	}, {
+		FunctionEntry: gojmespath.FunctionEntry{
+			Name: timeBefore,
+			Arguments: []argSpec{
+				{Types: []jpType{jpString}},
+				{Types: []jpType{jpString}},
+			},
+			Handler: jpTimeBefore,
+		},
+		ReturnType: []jpType{jpBool},
+		Note:       "checks if a time is before another time, both in RFC3339 format",
+	}, {
+		FunctionEntry: gojmespath.FunctionEntry{
+			Name: timeAfter,
+			Arguments: []argSpec{
+				{Types: []jpType{jpString}},
+				{Types: []jpType{jpString}},
+			},
+			Handler: jpTimeAfter,
+		},
+		ReturnType: []jpType{jpBool},
+		Note:       "checks if a time is after another time, both in RFC3339 format",
+	}, {
+		FunctionEntry: gojmespath.FunctionEntry{
+			Name: timeBetween,
+			Arguments: []argSpec{
+				{Types: []jpType{jpString}},
+				{Types: []jpType{jpString}},
+				{Types: []jpType{jpString}},
+			},
+			Handler: jpTimeBetween,
+		},
+		ReturnType: []jpType{jpBool},
+		Note:       "checks if a time is between a start and end time, all in RFC3339 format",
+	}, {
+		FunctionEntry: gojmespath.FunctionEntry{
+			Name: timeTruncate,
+			Arguments: []argSpec{
+				{Types: []jpType{jpString}},
+				{Types: []jpType{jpString}},
+			},
+			Handler: jpTimeTruncate,
+		},
+		ReturnType: []jpType{jpString},
+		Note:       "returns the result of rounding time down to a multiple of duration",
+	}}
 }
 
 func jpfCompare(arguments []interface{}) (interface{}, error) {
@@ -638,43 +555,31 @@ func jpfReplace(arguments []interface{}) (interface{}, error) {
 }
 
 func jpfReplaceAll(arguments []interface{}) (interface{}, error) {
-	var err error
-	str, err := validateArg(replaceAll, arguments, 0, reflect.String)
-	if err != nil {
+	if str, err := validateArg(replaceAll, arguments, 0, reflect.String); err != nil {
 		return nil, err
-	}
-
-	old, err := validateArg(replaceAll, arguments, 1, reflect.String)
-	if err != nil {
+	} else if old, err := validateArg(replaceAll, arguments, 1, reflect.String); err != nil {
 		return nil, err
-	}
-
-	new, err := validateArg(replaceAll, arguments, 2, reflect.String)
-	if err != nil {
+	} else if new, err := validateArg(replaceAll, arguments, 2, reflect.String); err != nil {
 		return nil, err
+	} else {
+		return strings.ReplaceAll(str.String(), old.String(), new.String()), nil
 	}
-
-	return strings.ReplaceAll(str.String(), old.String(), new.String()), nil
 }
 
 func jpfToUpper(arguments []interface{}) (interface{}, error) {
-	var err error
-	str, err := validateArg(toUpper, arguments, 0, reflect.String)
-	if err != nil {
+	if str, err := validateArg(toUpper, arguments, 0, reflect.String); err != nil {
 		return nil, err
+	} else {
+		return strings.ToUpper(str.String()), nil
 	}
-
-	return strings.ToUpper(str.String()), nil
 }
 
 func jpfToLower(arguments []interface{}) (interface{}, error) {
-	var err error
-	str, err := validateArg(toLower, arguments, 0, reflect.String)
-	if err != nil {
+	if str, err := validateArg(toLower, arguments, 0, reflect.String); err != nil {
 		return nil, err
+	} else {
+		return strings.ToLower(str.String()), nil
 	}
-
-	return strings.ToLower(str.String()), nil
 }
 
 func jpfTrim(arguments []interface{}) (interface{}, error) {
@@ -1046,14 +951,6 @@ func ifaceToString(iface interface{}) (string, error) {
 	}
 }
 
-func validateArg(f string, arguments []interface{}, index int, expectedType reflect.Kind) (reflect.Value, error) {
-	arg := reflect.ValueOf(arguments[index])
-	if arg.Type().Kind() != expectedType {
-		return reflect.Value{}, fmt.Errorf(invalidArgumentTypeError, f, index+1, expectedType.String())
-	}
-	return arg, nil
-}
-
 func jpRandom(arguments []interface{}) (interface{}, error) {
 	pattern := arguments[0].(string)
 	if pattern == "" {
diff --git a/pkg/engine/jmespath/new.go b/pkg/engine/jmespath/new.go
index 96a51225a..d60c08207 100644
--- a/pkg/engine/jmespath/new.go
+++ b/pkg/engine/jmespath/new.go
@@ -10,7 +10,7 @@ func New(query string) (*gojmespath.JMESPath, error) {
 		return nil, err
 	}
 	for _, function := range GetFunctions() {
-		jp.Register(function.Entry)
+		jp.Register(&function.FunctionEntry)
 	}
 	return jp, nil
 }
diff --git a/pkg/engine/jmespath/utils.go b/pkg/engine/jmespath/utils.go
new file mode 100644
index 000000000..e28fad7c4
--- /dev/null
+++ b/pkg/engine/jmespath/utils.go
@@ -0,0 +1,14 @@
+package jmespath
+
+import (
+	"fmt"
+	"reflect"
+)
+
+func validateArg(f string, arguments []interface{}, index int, expectedType reflect.Kind) (reflect.Value, error) {
+	arg := reflect.ValueOf(arguments[index])
+	if arg.Type().Kind() != expectedType {
+		return reflect.Value{}, fmt.Errorf(invalidArgumentTypeError, f, index+1, expectedType.String())
+	}
+	return arg, nil
+}


Test patch:
diff --git a/pkg/engine/jmespath/functionentry_test.go b/pkg/engine/jmespath/functionentry_test.go
new file mode 100644
index 000000000..364aff6bc
--- /dev/null
+++ b/pkg/engine/jmespath/functionentry_test.go
@@ -0,0 +1,64 @@
+package jmespath
+
+import (
+	"testing"
+
+	gojmespath "github.com/jmespath/go-jmespath"
+)
+
+func TestFunctionEntry_String(t *testing.T) {
+	type fields struct {
+		FunctionEntry gojmespath.FunctionEntry
+		Note          string
+		ReturnType    []jpType
+	}
+	tests := []struct {
+		name   string
+		fields fields
+		want   string
+	}{{
+		fields: fields{
+			FunctionEntry: gojmespath.FunctionEntry{
+				Name: compare,
+				Arguments: []argSpec{
+					{Types: []jpType{jpString}},
+					{Types: []jpType{jpString}},
+				},
+				Handler: jpfCompare,
+			},
+			ReturnType: []jpType{jpNumber},
+			Note:       "compares two strings lexicographically",
+		},
+		want: "compare(string, string) number (compares two strings lexicographically)",
+	}, {
+		fields: fields{
+			Note: "compares two strings lexicographically",
+		},
+		want: "",
+	}, {
+		fields: fields{
+			FunctionEntry: gojmespath.FunctionEntry{
+				Name: compare,
+				Arguments: []argSpec{
+					{Types: []jpType{jpString}},
+					{Types: []jpType{jpString}},
+				},
+				Handler: jpfCompare,
+			},
+			ReturnType: []jpType{jpNumber},
+		},
+		want: "compare(string, string) number",
+	}}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			f := FunctionEntry{
+				FunctionEntry: tt.fields.FunctionEntry,
+				Note:          tt.fields.Note,
+				ReturnType:    tt.fields.ReturnType,
+			}
+			if got := f.String(); got != tt.want {
+				t.Errorf("FunctionEntry.String() = %v, want %v", got, tt.want)
+			}
+		})
+	}
+}
diff --git a/pkg/engine/jmespath/functions_test.go b/pkg/engine/jmespath/functions_test.go
index a8c2f3a0b..9769ada5c 100644
--- a/pkg/engine/jmespath/functions_test.go
+++ b/pkg/engine/jmespath/functions_test.go
@@ -1480,36 +1480,28 @@ UFOZZVoELaasWS559wy8og39Eq21dDMynb8Bndn/
 	testCases := []struct {
 		jmesPath       string
 		expectedResult map[string]interface{}
-	}{
-		{
-			jmesPath:       "x509_decode(base64_decode('" + certs[0] + "'))",
-			expectedResult: resExpected[0],
-		},
-		{
-			jmesPath:       "x509_decode(base64_decode('" + certs[1] + "'))",
-			expectedResult: resExpected[1],
-		},
-		{
-			jmesPath:       "x509_decode('" + certs[2] + "')",
-			expectedResult: resExpected[0],
-		},
-		{
-			jmesPath:       "x509_decode('" + certs[3] + "')",
-			expectedResult: resExpected[1],
-		},
-		{
-			jmesPath:       "x509_decode('" + certs[4] + "')",
-			expectedResult: resExpected[0],
-		},
-		{
-			jmesPath:       "x509_decode('" + certs[5] + "')",
-			expectedResult: resExpected[1],
-		},
-		{
-			jmesPath:       "x509_decode('xyz')",
-			expectedResult: map[string]interface{}{},
-		},
-	}
+	}{{
+		jmesPath:       "x509_decode(base64_decode('" + certs[0] + "'))",
+		expectedResult: resExpected[0],
+	}, {
+		jmesPath:       "x509_decode(base64_decode('" + certs[1] + "'))",
+		expectedResult: resExpected[1],
+	}, {
+		jmesPath:       "x509_decode('" + certs[2] + "')",
+		expectedResult: resExpected[0],
+	}, {
+		jmesPath:       "x509_decode('" + certs[3] + "')",
+		expectedResult: resExpected[1],
+	}, {
+		jmesPath:       "x509_decode('" + certs[4] + "')",
+		expectedResult: resExpected[0],
+	}, {
+		jmesPath:       "x509_decode('" + certs[5] + "')",
+		expectedResult: resExpected[1],
+	}, {
+		jmesPath:       "x509_decode('xyz')",
+		expectedResult: map[string]interface{}{},
+	}}
 	for _, tc := range testCases {
 		t.Run(tc.jmesPath, func(t *testing.T) {
 			jp, err := New(tc.jmesPath)
@@ -1536,53 +1528,185 @@ func Test_jpfCompare(t *testing.T) {
 		args    args
 		want    interface{}
 		wantErr bool
-	}{
-		{
-			args: args{
-				arguments: []interface{}{"a", "b"},
+	}{{
+		args: args{
+			arguments: []interface{}{"a", "b"},
+		},
+		want: -1,
+	}, {
+		args: args{
+			arguments: []interface{}{"b", "a"},
+		},
+		want: 1,
+	}, {
+		args: args{
+			arguments: []interface{}{"b", "b"},
+		},
+		want: 0,
+	}, {
+		args: args{
+			arguments: []interface{}{1, "b"},
+		},
+		wantErr: true,
+	}, {
+		args: args{
+			arguments: []interface{}{"a", 1},
+		},
+		wantErr: true,
+	}}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			got, err := jpfCompare(tt.args.arguments)
+			if (err != nil) != tt.wantErr {
+				t.Errorf("jpfCompare() error = %v, wantErr %v", err, tt.wantErr)
+				return
+			}
+			if !reflect.DeepEqual(got, tt.want) {
+				t.Errorf("jpfCompare() = %v, want %v", got, tt.want)
+			}
+		})
+	}
+}
+
+func Test_jpfEqualFold(t *testing.T) {
+	type args struct {
+		arguments []interface{}
+	}
+	tests := []struct {
+		name    string
+		args    args
+		want    interface{}
+		wantErr bool
+	}{{
+		args: args{
+			arguments: []interface{}{"Go", "go"},
+		},
+		want: true,
+	}, {
+		args: args{
+			arguments: []interface{}{"a", "b"},
+		},
+		want: false,
+	}, {
+		args: args{
+			arguments: []interface{}{1, "b"},
+		},
+		wantErr: true,
+	}, {
+		args: args{
+			arguments: []interface{}{"a", 1},
+		},
+		wantErr: true,
+	}}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			got, err := jpfEqualFold(tt.args.arguments)
+			if (err != nil) != tt.wantErr {
+				t.Errorf("jpfEqualFold() error = %v, wantErr %v", err, tt.wantErr)
+				return
+			}
+			if !reflect.DeepEqual(got, tt.want) {
+				t.Errorf("jpfEqualFold() = %v, want %v", got, tt.want)
+			}
+		})
+	}
+}
+
+func Test_jpfReplace(t *testing.T) {
+	type args struct {
+		arguments []interface{}
+	}
+	tests := []struct {
+		name    string
+		args    args
+		want    interface{}
+		wantErr bool
+	}{{
+		args: args{
+			arguments: []interface{}{
+				"Lorem ipsum dolor sit amet",
+				"ipsum",
+				"muspi",
+				-1.0,
 			},
-			want: -1,
 		},
-		{
-			args: args{
-				arguments: []interface{}{"b", "a"},
+		want: "Lorem muspi dolor sit amet",
+	}, {
+		args: args{
+			arguments: []interface{}{
+				"Lorem ipsum ipsum ipsum dolor sit amet",
+				"ipsum",
+				"muspi",
+				-1.0,
 			},
-			want: 1,
 		},
-		{
-			args: args{
-				arguments: []interface{}{"b", "b"},
+		want: "Lorem muspi muspi muspi dolor sit amet",
+	}, {
+		args: args{
+			arguments: []interface{}{
+				"Lorem ipsum ipsum ipsum dolor sit amet",
+				"ipsum",
+				"muspi",
+				1.0,
 			},
-			want: 0,
 		},
-		{
-			args: args{
-				arguments: []interface{}{1, "b"},
+		want: "Lorem muspi ipsum ipsum dolor sit amet",
+	}, {
+		args: args{
+			arguments: []interface{}{
+				1.0,
+				"ipsum",
+				"muspi",
+				1.0,
 			},
-			wantErr: true,
 		},
-		{
-			args: args{
-				arguments: []interface{}{"a", 1},
+		wantErr: true,
+	}, {
+		args: args{
+			arguments: []interface{}{
+				"Lorem ipsum ipsum ipsum dolor sit amet",
+				1.0,
+				"muspi",
+				1.0,
 			},
-			wantErr: true,
 		},
-	}
+		wantErr: true,
+	}, {
+		args: args{
+			arguments: []interface{}{
+				"Lorem ipsum ipsum ipsum dolor sit amet",
+				"ipsum",
+				false,
+				1.0,
+			},
+		},
+		wantErr: true,
+	}, {
+		args: args{
+			arguments: []interface{}{
+				"Lorem ipsum ipsum ipsum dolor sit amet",
+				"ipsum",
+				"muspi",
+				true,
+			},
+		},
+		wantErr: true,
+	}}
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
-			got, err := jpfCompare(tt.args.arguments)
+			got, err := jpfReplace(tt.args.arguments)
 			if (err != nil) != tt.wantErr {
-				t.Errorf("jpfCompare() error = %v, wantErr %v", err, tt.wantErr)
+				t.Errorf("jpfReplace() error = %v, wantErr %v", err, tt.wantErr)
 				return
 			}
 			if !reflect.DeepEqual(got, tt.want) {
-				t.Errorf("jpfCompare() = %v, want %v", got, tt.want)
+				t.Errorf("jpfReplace() = %v, want %v", got, tt.want)
 			}
 		})
 	}
 }
 
-func Test_jpfEqualFold(t *testing.T) {
+func Test_jpfReplaceAll(t *testing.T) {
 	type args struct {
 		arguments []interface{}
 	}
@@ -1591,47 +1715,67 @@ func Test_jpfEqualFold(t *testing.T) {
 		args    args
 		want    interface{}
 		wantErr bool
-	}{
-		{
-			args: args{
-				arguments: []interface{}{"Go", "go"},
+	}{{
+		args: args{
+			arguments: []interface{}{
+				"Lorem ipsum dolor sit amet",
+				"ipsum",
+				"muspi",
 			},
-			want: true,
 		},
-		{
-			args: args{
-				arguments: []interface{}{"a", "b"},
+		want: "Lorem muspi dolor sit amet",
+	}, {
+		args: args{
+			arguments: []interface{}{
+				"Lorem ipsum ipsum ipsum dolor sit amet",
+				"ipsum",
+				"muspi",
 			},
-			want: false,
 		},
-		{
-			args: args{
-				arguments: []interface{}{1, "b"},
+		want: "Lorem muspi muspi muspi dolor sit amet",
+	}, {
+		args: args{
+			arguments: []interface{}{
+				1.0,
+				"ipsum",
+				"muspi",
 			},
-			wantErr: true,
 		},
-		{
-			args: args{
-				arguments: []interface{}{"a", 1},
+		wantErr: true,
+	}, {
+		args: args{
+			arguments: []interface{}{
+				"Lorem ipsum ipsum ipsum dolor sit amet",
+				1.0,
+				"muspi",
 			},
-			wantErr: true,
 		},
-	}
+		wantErr: true,
+	}, {
+		args: args{
+			arguments: []interface{}{
+				"Lorem ipsum ipsum ipsum dolor sit amet",
+				"ipsum",
+				false,
+			},
+		},
+		wantErr: true,
+	}}
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
-			got, err := jpfEqualFold(tt.args.arguments)
+			got, err := jpfReplaceAll(tt.args.arguments)
 			if (err != nil) != tt.wantErr {
-				t.Errorf("jpfEqualFold() error = %v, wantErr %v", err, tt.wantErr)
+				t.Errorf("jpfReplaceAll() error = %v, wantErr %v", err, tt.wantErr)
 				return
 			}
 			if !reflect.DeepEqual(got, tt.want) {
-				t.Errorf("jpfEqualFold() = %v, want %v", got, tt.want)
+				t.Errorf("jpfReplaceAll() = %v, want %v", got, tt.want)
 			}
 		})
 	}
 }
 
-func Test_jpfReplace(t *testing.T) {
+func Test_jpfToUpper(t *testing.T) {
 	type args struct {
 		arguments []interface{}
 	}
@@ -1640,94 +1784,96 @@ func Test_jpfReplace(t *testing.T) {
 		args    args
 		want    interface{}
 		wantErr bool
-	}{
-		{
-			args: args{
-				arguments: []interface{}{
-					"Lorem ipsum dolor sit amet",
-					"ipsum",
-					"muspi",
-					-1.0,
-				},
+	}{{
+		args: args{
+			arguments: []interface{}{
+				"abc",
 			},
-			want: "Lorem muspi dolor sit amet",
 		},
-		{
-			args: args{
-				arguments: []interface{}{
-					"Lorem ipsum ipsum ipsum dolor sit amet",
-					"ipsum",
-					"muspi",
-					-1.0,
-				},
+		want: "ABC",
+	}, {
+		args: args{
+			arguments: []interface{}{
+				"123",
 			},
-			want: "Lorem muspi muspi muspi dolor sit amet",
 		},
-		{
-			args: args{
-				arguments: []interface{}{
-					"Lorem ipsum ipsum ipsum dolor sit amet",
-					"ipsum",
-					"muspi",
-					1.0,
-				},
+		want: "123",
+	}, {
+		args: args{
+			arguments: []interface{}{
+				"a#%&123Bc",
 			},
-			want: "Lorem muspi ipsum ipsum dolor sit amet",
 		},
-		{
-			args: args{
-				arguments: []interface{}{
-					1.0,
-					"ipsum",
-					"muspi",
-					1.0,
-				},
+		want: "A#%&123BC",
+	}, {
+		args: args{
+			arguments: []interface{}{
+				32.0,
 			},
-			wantErr: true,
 		},
-		{
-			args: args{
-				arguments: []interface{}{
-					"Lorem ipsum ipsum ipsum dolor sit amet",
-					1.0,
-					"muspi",
-					1.0,
-				},
+		wantErr: true,
+	}}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			got, err := jpfToUpper(tt.args.arguments)
+			if (err != nil) != tt.wantErr {
+				t.Errorf("jpfToUpper() error = %v, wantErr %v", err, tt.wantErr)
+				return
+			}
+			if !reflect.DeepEqual(got, tt.want) {
+				t.Errorf("jpfToUpper() = %v, want %v", got, tt.want)
+			}
+		})
+	}
+}
+
+func Test_jpfToLower(t *testing.T) {
+	type args struct {
+		arguments []interface{}
+	}
+	tests := []struct {
+		name    string
+		args    args
+		want    interface{}
+		wantErr bool
+	}{{
+		args: args{
+			arguments: []interface{}{
+				"ABC",
 			},
-			wantErr: true,
 		},
-		{
-			args: args{
-				arguments: []interface{}{
-					"Lorem ipsum ipsum ipsum dolor sit amet",
-					"ipsum",
-					false,
-					1.0,
-				},
+		want: "abc",
+	}, {
+		args: args{
+			arguments: []interface{}{
+				"123",
 			},
-			wantErr: true,
 		},
-		{
-			args: args{
-				arguments: []interface{}{
-					"Lorem ipsum ipsum ipsum dolor sit amet",
-					"ipsum",
-					"muspi",
-					true,
-				},
+		want: "123",
+	}, {
+		args: args{
+			arguments: []interface{}{
+				"A#%&123BC",
 			},
-			wantErr: true,
 		},
-	}
+		want: "a#%&123bc",
+	}, {
+		args: args{
+			arguments: []interface{}{
+				32.0,
+			},
+		},
+		wantErr: true,
+	}}
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
-			got, err := jpfReplace(tt.args.arguments)
+			got, err := jpfToLower(tt.args.arguments)
 			if (err != nil) != tt.wantErr {
-				t.Errorf("jpfReplace() error = %v, wantErr %v", err, tt.wantErr)
+				t.Errorf("jpfToLower() error = %v, wantErr %v", err, tt.wantErr)
 				return
 			}
 			if !reflect.DeepEqual(got, tt.want) {
-				t.Errorf("jpfReplace() = %v, want %v", got, tt.want)
+				t.Errorf("jpfToLower() = %v, want %v", got, tt.want)
 			}
 		})
 	}