Repository: kyverno/kyverno
Pull request #6240 (2023-02-06 14:15:05)

fix: add tests for jp arithmetic funcs
## Explanation

This PR add tests for jp arithmetic funcs.


codecov[bot]: # [Codecov](https://codecov.io/gh/kyverno/kyverno/pull/6240?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=kyverno) Report
> Merging [#6240](https://codecov.io/gh/kyverno/kyverno/pull/6240?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=kyverno) (591bfe1) into [main](https://codecov.io/gh/kyverno/kyverno/commit/dd35ae201ba1d1736cbed335a860549f84c398f2?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=kyverno) (dd35ae2) will **decrease** coverage by `0.01%`.
> The diff coverage is `100.00%`.

```diff
@@            Coverage Diff             @@
##             main    #6240      +/-   ##
==========================================
- Coverage   37.49%   37.49%   -0.01%     
==========================================
  Files         201      201              
  Lines       20992    20981      -11     
==========================================
- Hits         7871     7866       -5     
+ Misses      12313    12306       -7     
- Partials      808      809       +1     
```


| [Impacted Files](https://codecov.io/gh/kyverno/kyverno/pull/6240?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=kyverno) | Coverage Δ | |
|---|---|---|
| [pkg/engine/jmespath/error.go](https://codecov.io/gh/kyverno/kyverno/pull/6240?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=kyverno#diff-cGtnL2VuZ2luZS9qbWVzcGF0aC9lcnJvci5nbw==) | `100.00% <ø> (ø)` | |
| [pkg/engine/jmespath/arithmetic.go](https://codecov.io/gh/kyverno/kyverno/pull/6240?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=kyverno#diff-cGtnL2VuZ2luZS9qbWVzcGF0aC9hcml0aG1ldGljLmdv) | `96.59% <100.00%> (+11.02%)` | :arrow_up: |
| [pkg/engine/jmespath/functions.go](https://codecov.io/gh/kyverno/kyverno/pull/6240?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=kyverno#diff-cGtnL2VuZ2luZS9qbWVzcGF0aC9mdW5jdGlvbnMuZ28=) | `80.26% <0.00%> (-1.80%)` | :arrow_down: |

: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/pkg/engine/jmespath/arithmetic.go b/pkg/engine/jmespath/arithmetic.go
index cc377f7d4..67d88c221 100644
--- a/pkg/engine/jmespath/arithmetic.go
+++ b/pkg/engine/jmespath/arithmetic.go
@@ -1,7 +1,6 @@
 package jmespath
 
 import (
-	"errors"
 	"fmt"
 	"math"
 	"reflect"
@@ -31,42 +30,24 @@ type Scalar struct {
 	float64
 }
 
-var errTypeMismatch = errors.New("types mismatch")
-
 func ParseArithemticOperands(arguments []interface{}, operator string) (Operand, Operand, error) {
 	op := [2]Operand{nil, nil}
-	t := [2]int{0, 0}
-
 	for i := 0; i < 2; i++ {
-		tmp, err := validateArg(divide, arguments, i, reflect.Float64)
-		if err == nil {
+		if tmp, err := validateArg(divide, arguments, i, reflect.Float64); err == nil {
 			var sc Scalar
 			sc.float64 = tmp.Float()
 			op[i] = sc
-		}
-
-		tmp, err = validateArg(divide, arguments, i, reflect.String)
-		if err == nil {
-			var q Quantity
-			q.Quantity, err = resource.ParseQuantity(tmp.String())
-			if err == nil {
-				op[i] = q
-				t[i] = 1
-			} else {
-				var d Duration
-				d.Duration, err = time.ParseDuration(tmp.String())
-				if err == nil {
-					op[i] = d
-					t[i] = 2
-				}
+		} else if tmp, err = validateArg(divide, arguments, i, reflect.String); err == nil {
+			if q, err := resource.ParseQuantity(tmp.String()); err == nil {
+				op[i] = Quantity{Quantity: q}
+			} else if d, err := time.ParseDuration(tmp.String()); err == nil {
+				op[i] = Duration{Duration: d}
 			}
 		}
 	}
-
-	if op[0] == nil || op[1] == nil || t[0]|t[1] == 3 {
+	if op[0] == nil || op[1] == nil {
 		return nil, nil, formatError(genericError, operator, "invalid operands")
 	}
-
 	return op[0], op[1], nil
 }
 
@@ -83,7 +64,7 @@ func (op1 Quantity) Add(op2 interface{}) (interface{}, error) {
 		op1.Quantity.Add(v.Quantity)
 		return op1.String(), nil
 	default:
-		return nil, errTypeMismatch
+		return nil, formatError(typeMismatchError, add)
 	}
 }
 
@@ -92,7 +73,7 @@ func (op1 Duration) Add(op2 interface{}) (interface{}, error) {
 	case Duration:
 		return (op1.Duration + v.Duration).String(), nil
 	default:
-		return nil, errTypeMismatch
+		return nil, formatError(typeMismatchError, add)
 	}
 }
 
@@ -101,7 +82,7 @@ func (op1 Scalar) Add(op2 interface{}) (interface{}, error) {
 	case Scalar:
 		return op1.float64 + v.float64, nil
 	default:
-		return nil, errTypeMismatch
+		return nil, formatError(typeMismatchError, add)
 	}
 }
 
@@ -111,7 +92,7 @@ func (op1 Quantity) Subtract(op2 interface{}) (interface{}, error) {
 		op1.Quantity.Sub(v.Quantity)
 		return op1.String(), nil
 	default:
-		return nil, errTypeMismatch
+		return nil, formatError(typeMismatchError, subtract)
 	}
 }
 
@@ -120,7 +101,7 @@ func (op1 Duration) Subtract(op2 interface{}) (interface{}, error) {
 	case Duration:
 		return (op1.Duration - v.Duration).String(), nil
 	default:
-		return nil, errTypeMismatch
+		return nil, formatError(typeMismatchError, subtract)
 	}
 }
 
@@ -129,7 +110,7 @@ func (op1 Scalar) Subtract(op2 interface{}) (interface{}, error) {
 	case Scalar:
 		return op1.float64 - v.float64, nil
 	default:
-		return nil, errTypeMismatch
+		return nil, formatError(typeMismatchError, subtract)
 	}
 }
 
@@ -154,7 +135,7 @@ func (op1 Quantity) Multiply(op2 interface{}) (interface{}, error) {
 		prod.Mul(op1.Quantity.AsDec(), q.AsDec())
 		return resource.NewDecimalQuantity(prod, op1.Quantity.Format).String(), nil
 	default:
-		return nil, errTypeMismatch
+		return nil, formatError(typeMismatchError, multiply)
 	}
 }
 
@@ -164,7 +145,7 @@ func (op1 Duration) Multiply(op2 interface{}) (interface{}, error) {
 		seconds := op1.Seconds() * v.float64
 		return time.Duration(seconds * float64(time.Second)).String(), nil
 	default:
-		return nil, errTypeMismatch
+		return nil, formatError(typeMismatchError, multiply)
 	}
 }
 
@@ -176,9 +157,9 @@ func (op1 Scalar) Multiply(op2 interface{}) (interface{}, error) {
 		return v.Multiply(op1)
 	case Duration:
 		return v.Multiply(op1)
+	default:
+		return nil, formatError(typeMismatchError, multiply)
 	}
-
-	return nil, nil
 }
 
 // Quantity / Duration			-> error
@@ -215,7 +196,7 @@ func (op1 Quantity) Divide(op2 interface{}) (interface{}, error) {
 		quo.QuoRound(op1.AsDec(), q.AsDec(), scale, inf.RoundDown)
 		return resource.NewDecimalQuantity(quo, op1.Quantity.Format).String(), nil
 	default:
-		return nil, errTypeMismatch
+		return nil, formatError(typeMismatchError, divide)
 	}
 }
 
@@ -233,7 +214,7 @@ func (op1 Duration) Divide(op2 interface{}) (interface{}, error) {
 		seconds := op1.Seconds() / v.float64
 		return time.Duration(seconds * float64(time.Second)).String(), nil
 	default:
-		return nil, errTypeMismatch
+		return nil, formatError(typeMismatchError, divide)
 	}
 }
 
@@ -245,7 +226,7 @@ func (op1 Scalar) Divide(op2 interface{}) (interface{}, error) {
 		}
 		return op1.float64 / v.float64, nil
 	default:
-		return nil, errTypeMismatch
+		return nil, formatError(typeMismatchError, divide)
 	}
 }
 
@@ -276,7 +257,7 @@ func (op1 Quantity) Modulo(op2 interface{}) (interface{}, error) {
 		}
 		return resource.NewQuantity(i1%i2, op1.Quantity.Format).String(), nil
 	default:
-		return nil, errTypeMismatch
+		return nil, formatError(typeMismatchError, modulo)
 	}
 }
 
@@ -288,7 +269,7 @@ func (op1 Duration) Modulo(op2 interface{}) (interface{}, error) {
 		}
 		return (op1.Duration % v.Duration).String(), nil
 	default:
-		return nil, errTypeMismatch
+		return nil, formatError(typeMismatchError, modulo)
 	}
 }
 
@@ -308,6 +289,6 @@ func (op1 Scalar) Modulo(op2 interface{}) (interface{}, error) {
 		}
 		return float64(val1 % val2), nil
 	default:
-		return nil, errTypeMismatch
+		return nil, formatError(typeMismatchError, modulo)
 	}
 }
diff --git a/pkg/engine/jmespath/error.go b/pkg/engine/jmespath/error.go
index 3153ad75c..be7596a89 100644
--- a/pkg/engine/jmespath/error.go
+++ b/pkg/engine/jmespath/error.go
@@ -11,6 +11,7 @@ const (
 	argOutOfBoundsError      = errorPrefix + "%d argument is out of bounds (%d)"
 	zeroDivisionError        = errorPrefix + "Zero divisor passed"
 	nonIntModuloError        = errorPrefix + "Non-integer argument(s) passed for modulo"
+	typeMismatchError        = errorPrefix + "Types mismatch"
 )
 
 func formatError(format string, function string, values ...interface{}) error {


Test patch:
diff --git a/pkg/engine/jmespath/arithmetic_test.go b/pkg/engine/jmespath/arithmetic_test.go
new file mode 100644
index 000000000..b9b4f93ff
--- /dev/null
+++ b/pkg/engine/jmespath/arithmetic_test.go
@@ -0,0 +1,707 @@
+package jmespath
+
+import (
+	"reflect"
+	"testing"
+
+	"gotest.tools/assert"
+)
+
+func Test_Add(t *testing.T) {
+	testCases := []struct {
+		name           string
+		test           string
+		expectedResult interface{}
+		err            bool
+		retFloat       bool
+	}{
+		// Scalar
+		{
+			name:           "Scalar + Scalar -> Scalar",
+			test:           "add(`12`, `13`)",
+			expectedResult: 25.0,
+			retFloat:       true,
+		},
+		{
+			name: "Scalar + Duration -> error",
+			test: "add('12', '13s')",
+			err:  true,
+		},
+		{
+			name: "Scalar + Quantity -> error",
+			test: "add(`12`, '13Ki')",
+			err:  true,
+		},
+		{
+			name: "Scalar + Quantity -> error",
+			test: "add(`12`, '13')",
+			err:  true,
+		},
+		// Quantity
+		{
+			name:           "Quantity + Quantity -> Quantity",
+			test:           "add('12Ki', '13Ki')",
+			expectedResult: `25Ki`,
+		},
+		{
+			name:           "Quantity + Quantity -> Quantity",
+			test:           "add('12Ki', '13')",
+			expectedResult: `12301`,
+		},
+		{
+			name: "Quantity + Duration -> error",
+			test: "add('12Ki', '13s')",
+			err:  true,
+		},
+		{
+			name: "Quantity + Scalar -> error",
+			test: "add('12Ki', `13`)",
+			err:  true,
+		},
+		// Duration
+		{
+			name:           "Duration + Duration -> Duration",
+			test:           "add('12s', '13s')",
+			expectedResult: `25s`,
+		},
+		{
+			name: "Duration + Scalar -> error",
+			test: "add('12s', `13`)",
+			err:  true,
+		},
+		{
+			name: "Duration + Quantity -> error",
+			test: "add('12s', '13Ki')",
+			err:  true,
+		},
+		{
+			name: "Duration + Quantity -> error",
+			test: "add('12s', '13')",
+			err:  true,
+		},
+	}
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			jp, err := New(tc.test)
+			assert.NilError(t, err)
+
+			result, err := jp.Search("")
+			if !tc.err {
+				assert.NilError(t, err)
+			} else {
+				assert.Assert(t, err != nil)
+				return
+			}
+
+			if tc.retFloat {
+				equal, ok := result.(float64)
+				assert.Assert(t, ok)
+				assert.Equal(t, equal, tc.expectedResult.(float64))
+			} else {
+				equal, ok := result.(string)
+				assert.Assert(t, ok)
+				assert.Equal(t, equal, tc.expectedResult.(string))
+			}
+		})
+	}
+}
+
+func Test_Subtract(t *testing.T) {
+	testCases := []struct {
+		name           string
+		test           string
+		expectedResult interface{}
+		err            bool
+		retFloat       bool
+	}{
+		// Scalar
+		{
+			name:           "Scalar - Scalar -> Scalar",
+			test:           "subtract(`12`, `13`)",
+			expectedResult: -1.0,
+			retFloat:       true,
+		},
+		{
+			name: "Scalar - Duration -> error",
+			test: "subtract('12', '13s')",
+			err:  true,
+		},
+		{
+			name: "Scalar - Quantity -> error",
+			test: "subtract(`12`, '13Ki')",
+			err:  true,
+		},
+		{
+			name: "Scalar - Quantity -> error",
+			test: "subtract(`12`, '13')",
+			err:  true,
+		},
+		// Quantity
+		{
+			name:           "Quantity - Quantity -> Quantity",
+			test:           "subtract('12Ki', '13Ki')",
+			expectedResult: `-1Ki`,
+		},
+		{
+			name:           "Quantity - Quantity -> Quantity",
+			test:           "subtract('12Ki', '13')",
+			expectedResult: `12275`,
+		},
+		{
+			name: "Quantity - Duration -> error",
+			test: "subtract('12Ki', '13s')",
+			err:  true,
+		},
+		{
+			name: "Quantity - Scalar -> error",
+			test: "subtract('12Ki', `13`)",
+			err:  true,
+		},
+		// Duration
+		{
+			name:           "Duration - Duration -> Duration",
+			test:           "subtract('12s', '13s')",
+			expectedResult: `-1s`,
+		},
+		{
+			name: "Duration - Scalar -> error",
+			test: "subtract('12s', `13`)",
+			err:  true,
+		},
+		{
+			name: "Duration - Quantity -> error",
+			test: "subtract('12s', '13Ki')",
+			err:  true,
+		},
+		{
+			name: "Duration - Quantity -> error",
+			test: "subtract('12s', '13')",
+			err:  true,
+		},
+	}
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			jp, err := New(tc.test)
+			assert.NilError(t, err)
+
+			result, err := jp.Search("")
+			if !tc.err {
+				assert.NilError(t, err)
+			} else {
+				assert.Assert(t, err != nil)
+				return
+			}
+
+			if tc.retFloat {
+				equal, ok := result.(float64)
+				assert.Assert(t, ok)
+				assert.Equal(t, equal, tc.expectedResult.(float64))
+			} else {
+				equal, ok := result.(string)
+				assert.Assert(t, ok)
+				assert.Equal(t, equal, tc.expectedResult.(string))
+			}
+		})
+	}
+}
+
+func Test_Multiply(t *testing.T) {
+	testCases := []struct {
+		name           string
+		test           string
+		expectedResult interface{}
+		err            bool
+		retFloat       bool
+	}{
+		// Quantity
+		{
+			name:           "Quantity * Scalar -> Quantity",
+			test:           "multiply('12Ki', `2`)",
+			expectedResult: `24Ki`,
+		},
+		{
+			name: "Quantity * Quantity -> error",
+			test: "multiply('12Ki', '12Ki')",
+			err:  true,
+		},
+		{
+			name: "Quantity * Quantity -> error",
+			test: "multiply('12Ki', '12')",
+			err:  true,
+		},
+		{
+			name: "Quantity * Duration -> error",
+			test: "multiply('12Ki', '12s')",
+			err:  true,
+		},
+		// Duration
+		{
+			name:           "Duration * Scalar -> Duration",
+			test:           "multiply('12s', `2`)",
+			expectedResult: `24s`,
+		},
+		{
+			name: "Duration * Quantity -> error",
+			test: "multiply('12s', '12Ki')",
+			err:  true,
+		},
+		{
+			name: "Duration * Quantity -> error",
+			test: "multiply('12s', '12')",
+			err:  true,
+		},
+		{
+			name: "Duration * Duration -> error",
+			test: "multiply('12s', '12s')",
+			err:  true,
+		},
+		// Scalar
+		{
+			name:           "Scalar * Scalar -> Scalar",
+			test:           "multiply(`2.5`, `2.5`)",
+			expectedResult: 2.5 * 2.5,
+			retFloat:       true,
+		},
+		{
+			name:           "Scalar * Quantity -> Quantity",
+			test:           "multiply(`2.5`, '12Ki')",
+			expectedResult: "30Ki",
+		},
+		{
+			name:           "Scalar * Quantity -> Quantity",
+			test:           "multiply(`2.5`, '12')",
+			expectedResult: "30",
+		},
+		{
+			name:           "Scalar * Duration -> Duration",
+			test:           "multiply(`2.5`, '40s')",
+			expectedResult: "1m40s",
+		},
+	}
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			jp, err := New(tc.test)
+			assert.NilError(t, err)
+
+			result, err := jp.Search("")
+			if !tc.err {
+				assert.NilError(t, err)
+			} else {
+				assert.Assert(t, err != nil)
+				return
+			}
+
+			if tc.retFloat {
+				equal, ok := result.(float64)
+				assert.Assert(t, ok)
+				assert.Equal(t, equal, tc.expectedResult.(float64))
+			} else {
+				equal, ok := result.(string)
+				assert.Assert(t, ok)
+				assert.Equal(t, equal, tc.expectedResult.(string))
+			}
+		})
+	}
+}
+
+func Test_Divide(t *testing.T) {
+	testCases := []struct {
+		name           string
+		test           string
+		expectedResult interface{}
+		err            bool
+		retFloat       bool
+	}{
+		// Quantity
+		{
+			name:           "Quantity / Quantity -> Scalar",
+			test:           "divide('256M', '256M')",
+			expectedResult: 1.0,
+			retFloat:       true,
+		},
+		{
+			name:           "Quantity / Quantity -> Scalar",
+			test:           "divide('512M', '256M')",
+			expectedResult: 2.0,
+			retFloat:       true,
+		},
+		{
+			name:           "Quantity / Quantity -> Scalar",
+			test:           "divide('8', '3')",
+			expectedResult: 8.0 / 3.0,
+			retFloat:       true,
+		},
+		{
+			name:           "Quantity / Quantity -> Scalar",
+			test:           "divide('128M', '256M')",
+			expectedResult: 0.5,
+			retFloat:       true,
+		},
+		{
+			name:           "Quantity / Scalar -> Quantity",
+			test:           "divide('12Ki', `3`)",
+			expectedResult: "4Ki",
+		},
+		{
+			name:           "Quantity / Quantity -> Scalar",
+			test:           "divide('12Ki', '2Ki')",
+			expectedResult: 6.0,
+			retFloat:       true,
+		},
+		{
+			name:           "Quantity / Quantity -> Scalar",
+			test:           "divide('12Ki', '200')",
+			expectedResult: 61.44,
+			retFloat:       true,
+		},
+		{
+			name: "Quantity / Duration -> error",
+			test: "divide('12Ki', '2s')",
+			err:  true,
+		},
+		// Duration
+		{
+			name:           "Duration / Scalar -> Duration",
+			test:           "divide('12s', `3`)",
+			expectedResult: "4s",
+		},
+		{
+			name:           "Duration / Duration -> Scalar",
+			test:           "divide('12s', '5s')",
+			expectedResult: 2.4,
+			retFloat:       true,
+		},
+		{
+			name: "Duration / Quantity -> error",
+			test: "divide('12s', '4Ki')",
+			err:  true,
+		},
+		{
+			name: "Duration / Quantity -> error",
+			test: "divide('12s', '4')",
+			err:  true,
+		},
+		// Scalar
+		{
+			name:           "Scalar / Scalar -> Scalar",
+			test:           "divide(`14`, `3`)",
+			expectedResult: 4.666666666666667,
+			retFloat:       true,
+		},
+		{
+			name: "Scalar / Duration -> error",
+			test: "divide(`14`, '5s')",
+			err:  true,
+		},
+		{
+			name: "Scalar / Quantity -> error",
+			test: "divide(`14`, '5Ki')",
+			err:  true,
+		},
+		{
+			name: "Scalar / Quantity -> error",
+			test: "divide(`14`, '5')",
+			err:  true,
+		},
+		// Divide by 0
+		{
+			name: "Scalar / Zero -> error",
+			test: "divide(`14`, `0`)",
+			err:  true,
+		},
+		{
+			name: "Quantity / Zero -> error",
+			test: "divide('4Ki', `0`)",
+			err:  true,
+		},
+		{
+			name: "Quantity / Zero -> error",
+			test: "divide('4Ki', '0Ki')",
+			err:  true,
+		},
+		{
+			name: "Quantity / Zero -> error",
+			test: "divide('4', `0`)",
+			err:  true,
+		},
+		{
+			name: "Quantity / Zero -> error",
+			test: "divide('4', '0')",
+			err:  true,
+		},
+		{
+			name: "Duration / Zero -> error",
+			test: "divide('4s', `0`)",
+			err:  true,
+		},
+		{
+			name: "Duration / Zero -> error",
+			test: "divide('4s', '0s')",
+			err:  true,
+		},
+	}
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			jp, err := New(tc.test)
+			assert.NilError(t, err)
+
+			result, err := jp.Search("")
+			if !tc.err {
+				assert.NilError(t, err)
+			} else {
+				assert.Assert(t, err != nil)
+				return
+			}
+
+			if tc.retFloat {
+				equal, ok := result.(float64)
+				assert.Assert(t, ok)
+				assert.Equal(t, equal, tc.expectedResult.(float64))
+			} else {
+				equal, ok := result.(string)
+				assert.Assert(t, ok)
+				assert.Equal(t, equal, tc.expectedResult.(string))
+			}
+		})
+	}
+}
+
+func Test_Modulo(t *testing.T) {
+	testCases := []struct {
+		name           string
+		test           string
+		expectedResult interface{}
+		err            bool
+		retFloat       bool
+	}{
+		// Quantity
+		{
+			name: "Quantity % Duration -> error",
+			test: "modulo('12', '13s')",
+			err:  true,
+		},
+		{
+			name: "Quantity % Duration -> error",
+			test: "modulo('12Ki', '13s')",
+			err:  true,
+		},
+		{
+			name: "Quantity % Scalar -> error",
+			test: "modulo('12Ki', `13`)",
+			err:  true,
+		},
+		{
+			name:           "Quantity % Quantity -> Quantity",
+			test:           "modulo('12Ki', '5Ki')",
+			expectedResult: `2Ki`,
+		},
+		// Duration
+		{
+			name: "Duration % Quantity -> error",
+			test: "modulo('13s', '12')",
+			err:  true,
+		},
+		{
+			name: "Duration % Quantity -> error",
+			test: "modulo('13s', '12Ki')",
+			err:  true,
+		},
+		{
+			name:           "Duration % Duration -> Duration",
+			test:           "modulo('13s', '2s')",
+			expectedResult: `1s`,
+		},
+		{
+			name: "Duration % Scalar -> error",
+			test: "modulo('13s', `2`)",
+			err:  true,
+		},
+		// Scalar
+		{
+			name: "Scalar % Quantity -> error",
+			test: "modulo(`13`, '12')",
+			err:  true,
+		},
+		{
+			name: "Scalar % Quantity -> error",
+			test: "modulo(`13`, '12Ki')",
+			err:  true,
+		},
+		{
+			name: "Scalar % Duration -> error",
+			test: "modulo(`13`, '5s')",
+			err:  true,
+		},
+		{
+			name:           "Scalar % Scalar -> Scalar",
+			test:           "modulo(`13`, `5`)",
+			expectedResult: 3.0,
+			retFloat:       true,
+		},
+		// Modulo by 0
+		{
+			name: "Scalar % Zero -> error",
+			test: "modulo(`14`, `0`)",
+			err:  true,
+		},
+		{
+			name: "Quantity % Zero -> error",
+			test: "modulo('4Ki', `0`)",
+			err:  true,
+		},
+		{
+			name: "Quantity % Zero -> error",
+			test: "modulo('4Ki', '0Ki')",
+			err:  true,
+		},
+		{
+			name: "Quantity % Zero -> error",
+			test: "modulo('4', `0`)",
+			err:  true,
+		},
+		{
+			name: "Quantity % Zero -> error",
+			test: "modulo('4', '0')",
+			err:  true,
+		},
+		{
+			name: "Duration % Zero -> error",
+			test: "modulo('4s', `0`)",
+			err:  true,
+		},
+		{
+			name: "Duration % Zero -> error",
+			test: "modulo('4s', '0s')",
+			err:  true,
+		},
+		// Modulo with non int values
+		{
+			name: "Quantity % Non int -> error",
+			test: "modulo('4', '1.5')",
+			err:  true,
+		},
+		{
+			name: "Non int % Quantity -> error",
+			test: "modulo('4.5', '1')",
+			err:  true,
+		},
+		{
+			name: "Scalar % Non int -> error",
+			test: "modulo(`14`, `1.5`)",
+			err:  true,
+		},
+		{
+			name: "Non int % Scalar -> error",
+			test: "modulo(`14.5`, `2`)",
+			err:  true,
+		},
+	}
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			jp, err := New(tc.test)
+			assert.NilError(t, err)
+
+			result, err := jp.Search("")
+			if !tc.err {
+				assert.NilError(t, err)
+			} else {
+				assert.Assert(t, err != nil)
+				return
+			}
+
+			if tc.retFloat {
+				equal, ok := result.(float64)
+				assert.Assert(t, ok)
+				assert.Equal(t, equal, tc.expectedResult.(float64))
+			} else {
+				equal, ok := result.(string)
+				assert.Assert(t, ok)
+				assert.Equal(t, equal, tc.expectedResult.(string))
+			}
+		})
+	}
+}
+
+func TestScalar_Multiply(t *testing.T) {
+	type fields struct {
+		float64 float64
+	}
+	type args struct {
+		op2 interface{}
+	}
+	tests := []struct {
+		name    string
+		fields  fields
+		args    args
+		want    interface{}
+		wantErr bool
+	}{{
+		fields: fields{
+			float64: 123,
+		},
+		args: args{
+			op2: true,
+		},
+		wantErr: true,
+	}}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			op1 := Scalar{
+				float64: tt.fields.float64,
+			}
+			got, err := op1.Multiply(tt.args.op2)
+			if (err != nil) != tt.wantErr {
+				t.Errorf("Scalar.Multiply() error = %v, wantErr %v", err, tt.wantErr)
+				return
+			}
+			if !reflect.DeepEqual(got, tt.want) {
+				t.Errorf("Scalar.Multiply() = %v, want %v", got, tt.want)
+			}
+		})
+	}
+}
+
+func TestParseArithemticOperands(t *testing.T) {
+	type args struct {
+		arguments []interface{}
+		operator  string
+	}
+	tests := []struct {
+		name    string
+		args    args
+		want    Operand
+		want1   Operand
+		wantErr bool
+	}{{
+		args: args{
+			arguments: []interface{}{
+				true,
+				1.0,
+			},
+		},
+		wantErr: true,
+	}, {
+		args: args{
+			arguments: []interface{}{
+				1.0,
+				true,
+			},
+		},
+		wantErr: true,
+	}}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			got, got1, err := ParseArithemticOperands(tt.args.arguments, tt.args.operator)
+			if (err != nil) != tt.wantErr {
+				t.Errorf("ParseArithemticOperands() error = %v, wantErr %v", err, tt.wantErr)
+				return
+			}
+			if !reflect.DeepEqual(got, tt.want) {
+				t.Errorf("ParseArithemticOperands() got = %v, want %v", got, tt.want)
+			}
+			if !reflect.DeepEqual(got1, tt.want1) {
+				t.Errorf("ParseArithemticOperands() got1 = %v, want %v", got1, tt.want1)
+			}
+		})
+	}
+}
diff --git a/pkg/engine/jmespath/functions_test.go b/pkg/engine/jmespath/functions_test.go
index fc6328139..6189f2e1a 100644
--- a/pkg/engine/jmespath/functions_test.go
+++ b/pkg/engine/jmespath/functions_test.go
@@ -538,600 +538,6 @@ func Test_LabelMatch(t *testing.T) {
 	}
 }
 
-func Test_Add(t *testing.T) {
-	testCases := []struct {
-		name           string
-		test           string
-		expectedResult interface{}
-		err            bool
-		retFloat       bool
-	}{
-		// Scalar
-		{
-			name:           "Scalar + Scalar -> Scalar",
-			test:           "add(`12`, `13`)",
-			expectedResult: 25.0,
-			retFloat:       true,
-		},
-		{
-			name: "Scalar + Duration -> error",
-			test: "add('12', '13s')",
-			err:  true,
-		},
-		{
-			name: "Scalar + Quantity -> error",
-			test: "add(`12`, '13Ki')",
-			err:  true,
-		},
-		{
-			name: "Scalar + Quantity -> error",
-			test: "add(`12`, '13')",
-			err:  true,
-		},
-		// Quantity
-		{
-			name:           "Quantity + Quantity -> Quantity",
-			test:           "add('12Ki', '13Ki')",
-			expectedResult: `25Ki`,
-		},
-		{
-			name:           "Quantity + Quantity -> Quantity",
-			test:           "add('12Ki', '13')",
-			expectedResult: `12301`,
-		},
-		{
-			name: "Quantity + Duration -> error",
-			test: "add('12Ki', '13s')",
-			err:  true,
-		},
-		{
-			name: "Quantity + Scalar -> error",
-			test: "add('12Ki', `13`)",
-			err:  true,
-		},
-		// Duration
-		{
-			name:           "Duration + Duration -> Duration",
-			test:           "add('12s', '13s')",
-			expectedResult: `25s`,
-		},
-		{
-			name: "Duration + Scalar -> error",
-			test: "add('12s', '13')",
-			err:  true,
-		},
-		{
-			name: "Duration + Quantity -> error",
-			test: "add('12s', '13Ki')",
-			err:  true,
-		},
-		{
-			name: "Duration + Quantity -> error",
-			test: "add('12s', '13')",
-			err:  true,
-		},
-	}
-	for _, tc := range testCases {
-		t.Run(tc.name, func(t *testing.T) {
-			jp, err := New(tc.test)
-			assert.NilError(t, err)
-
-			result, err := jp.Search("")
-			if !tc.err {
-				assert.NilError(t, err)
-			} else {
-				assert.Assert(t, err != nil)
-				return
-			}
-
-			if tc.retFloat {
-				equal, ok := result.(float64)
-				assert.Assert(t, ok)
-				assert.Equal(t, equal, tc.expectedResult.(float64))
-			} else {
-				equal, ok := result.(string)
-				assert.Assert(t, ok)
-				assert.Equal(t, equal, tc.expectedResult.(string))
-			}
-		})
-	}
-}
-
-func Test_Subtract(t *testing.T) {
-	testCases := []struct {
-		name           string
-		test           string
-		expectedResult interface{}
-		err            bool
-		retFloat       bool
-	}{
-		// Scalar
-		{
-			name:           "Scalar - Scalar -> Scalar",
-			test:           "subtract(`12`, `13`)",
-			expectedResult: -1.0,
-			retFloat:       true,
-		},
-		{
-			name: "Scalar - Duration -> error",
-			test: "subtract('12', '13s')",
-			err:  true,
-		},
-		{
-			name: "Scalar - Quantity -> error",
-			test: "subtract(`12`, '13Ki')",
-			err:  true,
-		},
-		{
-			name: "Scalar - Quantity -> error",
-			test: "subtract(`12`, '13')",
-			err:  true,
-		},
-		// Quantity
-		{
-			name:           "Quantity - Quantity -> Quantity",
-			test:           "subtract('12Ki', '13Ki')",
-			expectedResult: `-1Ki`,
-		},
-		{
-			name:           "Quantity - Quantity -> Quantity",
-			test:           "subtract('12Ki', '13')",
-			expectedResult: `12275`,
-		},
-		{
-			name: "Quantity - Duration -> error",
-			test: "subtract('12Ki', '13s')",
-			err:  true,
-		},
-		{
-			name: "Quantity - Scalar -> error",
-			test: "subtract('12Ki', `13`)",
-			err:  true,
-		},
-		// Duration
-		{
-			name:           "Duration - Duration -> Duration",
-			test:           "subtract('12s', '13s')",
-			expectedResult: `-1s`,
-		},
-		{
-			name: "Duration - Scalar -> error",
-			test: "subtract('12s', '13')",
-			err:  true,
-		},
-		{
-			name: "Duration - Quantity -> error",
-			test: "subtract('12s', '13Ki')",
-			err:  true,
-		},
-		{
-			name: "Duration - Quantity -> error",
-			test: "subtract('12s', '13')",
-			err:  true,
-		},
-	}
-	for _, tc := range testCases {
-		t.Run(tc.name, func(t *testing.T) {
-			jp, err := New(tc.test)
-			assert.NilError(t, err)
-
-			result, err := jp.Search("")
-			if !tc.err {
-				assert.NilError(t, err)
-			} else {
-				assert.Assert(t, err != nil)
-				return
-			}
-
-			if tc.retFloat {
-				equal, ok := result.(float64)
-				assert.Assert(t, ok)
-				assert.Equal(t, equal, tc.expectedResult.(float64))
-			} else {
-				equal, ok := result.(string)
-				assert.Assert(t, ok)
-				assert.Equal(t, equal, tc.expectedResult.(string))
-			}
-		})
-	}
-}
-
-func Test_Multiply(t *testing.T) {
-	testCases := []struct {
-		name           string
-		test           string
-		expectedResult interface{}
-		err            bool
-		retFloat       bool
-	}{
-		// Quantity
-		{
-			name:           "Quantity * Scalar -> Quantity",
-			test:           "multiply('12Ki', `2`)",
-			expectedResult: `24Ki`,
-		},
-		{
-			name: "Quantity * Quantity -> error",
-			test: "multiply('12Ki', '12Ki')",
-			err:  true,
-		},
-		{
-			name: "Quantity * Quantity -> error",
-			test: "multiply('12Ki', '12')",
-			err:  true,
-		},
-		{
-			name: "Quantity * Duration -> error",
-			test: "multiply('12Ki', '12s')",
-			err:  true,
-		},
-		// Duration
-		{
-			name:           "Duration * Scalar -> Duration",
-			test:           "multiply('12s', `2`)",
-			expectedResult: `24s`,
-		},
-		{
-			name: "Duration * Quantity -> error",
-			test: "multiply('12s', '12Ki')",
-			err:  true,
-		},
-		{
-			name: "Duration * Quantity -> error",
-			test: "multiply('12s', '12')",
-			err:  true,
-		},
-		{
-			name: "Duration * Duration -> error",
-			test: "multiply('12s', '12s')",
-			err:  true,
-		},
-		// Scalar
-		{
-			name:           "Scalar * Scalar -> Scalar",
-			test:           "multiply(`2.5`, `2.5`)",
-			expectedResult: 2.5 * 2.5,
-			retFloat:       true,
-		},
-		{
-			name:           "Scalar * Quantity -> Quantity",
-			test:           "multiply(`2.5`, '12Ki')",
-			expectedResult: "30Ki",
-		},
-		{
-			name:           "Scalar * Quantity -> Quantity",
-			test:           "multiply(`2.5`, '12')",
-			expectedResult: "30",
-		},
-		{
-			name:           "Scalar * Duration -> Duration",
-			test:           "multiply(`2.5`, '40s')",
-			expectedResult: "1m40s",
-		},
-	}
-	for _, tc := range testCases {
-		t.Run(tc.name, func(t *testing.T) {
-			jp, err := New(tc.test)
-			assert.NilError(t, err)
-
-			result, err := jp.Search("")
-			if !tc.err {
-				assert.NilError(t, err)
-			} else {
-				assert.Assert(t, err != nil)
-				return
-			}
-
-			if tc.retFloat {
-				equal, ok := result.(float64)
-				assert.Assert(t, ok)
-				assert.Equal(t, equal, tc.expectedResult.(float64))
-			} else {
-				equal, ok := result.(string)
-				assert.Assert(t, ok)
-				assert.Equal(t, equal, tc.expectedResult.(string))
-			}
-		})
-	}
-}
-
-func Test_Divide(t *testing.T) {
-	testCases := []struct {
-		name           string
-		test           string
-		expectedResult interface{}
-		err            bool
-		retFloat       bool
-	}{
-		// Quantity
-		{
-			name:           "Quantity / Quantity -> Scalar",
-			test:           "divide('256M', '256M')",
-			expectedResult: 1.0,
-			retFloat:       true,
-		},
-		{
-			name:           "Quantity / Quantity -> Scalar",
-			test:           "divide('512M', '256M')",
-			expectedResult: 2.0,
-			retFloat:       true,
-		},
-		{
-			name:           "Quantity / Quantity -> Scalar",
-			test:           "divide('8', '3')",
-			expectedResult: 8.0 / 3.0,
-			retFloat:       true,
-		},
-		{
-			name:           "Quantity / Quantity -> Scalar",
-			test:           "divide('128M', '256M')",
-			expectedResult: 0.5,
-			retFloat:       true,
-		},
-		{
-			name:           "Quantity / Scalar -> Quantity",
-			test:           "divide('12Ki', `3`)",
-			expectedResult: "4Ki",
-		},
-		{
-			name:           "Quantity / Quantity -> Scalar",
-			test:           "divide('12Ki', '2Ki')",
-			expectedResult: 6.0,
-			retFloat:       true,
-		},
-		{
-			name:           "Quantity / Quantity -> Scalar",
-			test:           "divide('12Ki', '200')",
-			expectedResult: 61.44,
-			retFloat:       true,
-		},
-		{
-			name: "Quantity / Duration -> error",
-			test: "divide('12Ki', '2s')",
-			err:  true,
-		},
-		// Duration
-		{
-			name:           "Duration / Scalar -> Duration",
-			test:           "divide('12s', `3`)",
-			expectedResult: "4s",
-		},
-		{
-			name:           "Duration / Duration -> Scalar",
-			test:           "divide('12s', '5s')",
-			expectedResult: 2.4,
-			retFloat:       true,
-		},
-		{
-			name: "Duration / Quantity -> error",
-			test: "divide('12s', '4Ki')",
-			err:  true,
-		},
-		{
-			name: "Duration / Quantity -> error",
-			test: "divide('12s', '4')",
-			err:  true,
-		},
-		// Scalar
-		{
-			name:           "Scalar / Scalar -> Scalar",
-			test:           "divide(`14`, `3`)",
-			expectedResult: 4.666666666666667,
-			retFloat:       true,
-		},
-		{
-			name: "Scalar / Duration -> error",
-			test: "divide(`14`, '5s')",
-			err:  true,
-		},
-		{
-			name: "Scalar / Quantity -> error",
-			test: "divide(`14`, '5Ki')",
-			err:  true,
-		},
-		{
-			name: "Scalar / Quantity -> error",
-			test: "divide(`14`, '5')",
-			err:  true,
-		},
-		// Divide by 0
-		{
-			name: "Scalar / Zero -> error",
-			test: "divide(`14`, `0`)",
-			err:  true,
-		},
-		{
-			name: "Quantity / Zero -> error",
-			test: "divide('4Ki', `0`)",
-			err:  true,
-		},
-		{
-			name: "Quantity / Zero -> error",
-			test: "divide('4Ki', '0Ki')",
-			err:  true,
-		},
-		{
-			name: "Quantity / Zero -> error",
-			test: "divide('4', `0`)",
-			err:  true,
-		},
-		{
-			name: "Quantity / Zero -> error",
-			test: "divide('4', '0')",
-			err:  true,
-		},
-		{
-			name: "Duration / Zero -> error",
-			test: "divide('4s', `0`)",
-			err:  true,
-		},
-		{
-			name: "Duration / Zero -> error",
-			test: "divide('4s', '0s')",
-			err:  true,
-		},
-	}
-	for _, tc := range testCases {
-		t.Run(tc.name, func(t *testing.T) {
-			jp, err := New(tc.test)
-			assert.NilError(t, err)
-
-			result, err := jp.Search("")
-			if !tc.err {
-				assert.NilError(t, err)
-			} else {
-				assert.Assert(t, err != nil)
-				return
-			}
-
-			if tc.retFloat {
-				equal, ok := result.(float64)
-				assert.Assert(t, ok)
-				assert.Equal(t, equal, tc.expectedResult.(float64))
-			} else {
-				equal, ok := result.(string)
-				assert.Assert(t, ok)
-				assert.Equal(t, equal, tc.expectedResult.(string))
-			}
-		})
-	}
-}
-
-func Test_Modulo(t *testing.T) {
-	testCases := []struct {
-		name           string
-		test           string
-		expectedResult interface{}
-		err            bool
-		retFloat       bool
-	}{
-		// Quantity
-		{
-			name: "Quantity % Duration -> error",
-			test: "modulo('12', '13s')",
-			err:  true,
-		},
-		{
-			name: "Quantity % Duration -> error",
-			test: "modulo('12Ki', '13s')",
-			err:  true,
-		},
-		{
-			name: "Quantity % Scalar -> error",
-			test: "modulo('12Ki', `13`)",
-			err:  true,
-		},
-		{
-			name:           "Quantity % Quantity -> Quantity",
-			test:           "modulo('12Ki', '5Ki')",
-			expectedResult: `2Ki`,
-		},
-		// Duration
-		{
-			name: "Duration % Quantity -> error",
-			test: "modulo('13s', '12')",
-			err:  true,
-		},
-		{
-			name: "Duration % Quantity -> error",
-			test: "modulo('13s', '12Ki')",
-			err:  true,
-		},
-		{
-			name:           "Duration % Duration -> Duration",
-			test:           "modulo('13s', '2s')",
-			expectedResult: `1s`,
-		},
-		{
-			name: "Duration % Scalar -> error",
-			test: "modulo('13s', `2`)",
-			err:  true,
-		},
-		// Scalar
-		{
-			name: "Scalar % Quantity -> error",
-			test: "modulo(`13`, '12')",
-			err:  true,
-		},
-		{
-			name: "Scalar % Quantity -> error",
-			test: "modulo(`13`, '12Ki')",
-			err:  true,
-		},
-		{
-			name: "Scalar % Duration -> error",
-			test: "modulo(`13`, '5s')",
-			err:  true,
-		},
-		{
-			name:           "Scalar % Scalar -> Scalar",
-			test:           "modulo(`13`, `5`)",
-			expectedResult: 3.0,
-			retFloat:       true,
-		},
-		// Modulo by 0
-		{
-			name: "Scalar % Zero -> error",
-			test: "modulo(`14`, `0`)",
-			err:  true,
-		},
-		{
-			name: "Quantity % Zero -> error",
-			test: "modulo('4Ki', `0`)",
-			err:  true,
-		},
-		{
-			name: "Quantity % Zero -> error",
-			test: "modulo('4Ki', '0Ki')",
-			err:  true,
-		},
-		{
-			name: "Quantity % Zero -> error",
-			test: "modulo('4', `0`)",
-			err:  true,
-		},
-		{
-			name: "Quantity % Zero -> error",
-			test: "modulo('4', '0')",
-			err:  true,
-		},
-		{
-			name: "Duration % Zero -> error",
-			test: "modulo('4s', `0`)",
-			err:  true,
-		},
-		{
-			name: "Duration % Zero -> error",
-			test: "modulo('4s', '0s')",
-			err:  true,
-		},
-	}
-	for _, tc := range testCases {
-		t.Run(tc.name, func(t *testing.T) {
-			jp, err := New(tc.test)
-			assert.NilError(t, err)
-
-			result, err := jp.Search("")
-			if !tc.err {
-				assert.NilError(t, err)
-			} else {
-				assert.Assert(t, err != nil)
-				return
-			}
-
-			if tc.retFloat {
-				equal, ok := result.(float64)
-				assert.Assert(t, ok)
-				assert.Equal(t, equal, tc.expectedResult.(float64))
-			} else {
-				equal, ok := result.(string)
-				assert.Assert(t, ok)
-				assert.Equal(t, equal, tc.expectedResult.(string))
-			}
-		})
-	}
-}
-
 func Test_Base64Decode(t *testing.T) {
 	jp, err := New("base64_decode('SGVsbG8sIHdvcmxkIQ==')")
 	assert.NilError(t, err)