From 8ec256edbf639f89facdb482d57ffe692aa6ffd3 Mon Sep 17 00:00:00 2001 From: TwinProduction Date: Wed, 10 Mar 2021 21:49:13 -0500 Subject: [PATCH] Implement has() function to determine if an element at a JSONPath exists --- README.md | 3 + core/condition.go | 46 +- core/condition_test.go | 1090 ++++++++++++++++------------------------ 3 files changed, 461 insertions(+), 678 deletions(-) diff --git a/README.md b/README.md index 7d9afaa4..aeeb4e83 100644 --- a/README.md +++ b/README.md @@ -159,6 +159,8 @@ Here are some examples of conditions you can use: | `[BODY].age == [BODY].id` | JSONPath value of `$.age` is equal JSONPath `$.id` | `{"age":1,"id":1}` | | | `len([BODY].data) < 5` | Array at JSONPath `$.data` has less than 5 elements | `{"data":[{"id":1}]}` | | | `len([BODY].name) == 8` | String at JSONPath `$.name` has a length of 8 | `{"name":"john.doe"}` | `{"name":"bob"}` | +| `has([BODY].errors) == false` | JSONPath `$.errors` does not exist | `{"name":"john.doe"}` | `{"errors":[]}` | +| `has([BODY].users) == true` | JSONPath `$.users` exists | `{"users":[]}` | `{}` | | `[BODY].name == pat(john*)` | String at JSONPath `$.name` matches pattern `john*` | `{"name":"john.doe"}` | `{"name":"bob"}` | | `[BODY].id == any(1, 2)` | Value at JSONPath `$.id` is equal to `1` or `2` | 1, 2 | 3, 4, 5 | | `[CERTIFICATE_EXPIRATION] > 48h` | Certificate expiration is more than 48h away | 49h, 50h, 123h | 1h, 24h, ... | @@ -182,6 +184,7 @@ Here are some examples of conditions you can use: | Function | Description | Example | |:-----------|:---------------------------------------------------------------------------------------------------------------- |:-------------------------- | | `len` | Returns the length of the object/slice. Works only with the `[BODY]` placeholder. | `len([BODY].username) > 8` +| `has` | Returns `true` or `false` based on whether a given path is valid. Works only with the `[BODY]` placeholder. | `has([BODY].errors) == false` | `pat` | Specifies that the string passed as parameter should be evaluated as a pattern. Works only with `==` and `!=`. | `[IP] == pat(192.168.*)` | `any` | Specifies that any one of the values passed as parameters is a valid value. Works only with `==` and `!=`. | `[BODY].ip == any(127.0.0.1, ::1)` diff --git a/core/condition.go b/core/condition.go index 7c957d32..93736087 100644 --- a/core/condition.go +++ b/core/condition.go @@ -51,14 +51,19 @@ const ( // Usage: len([BODY].articles) == 10, len([BODY].name) > 5 LengthFunctionPrefix = "len(" + // HasFunctionPrefix is the prefix for the has function + // + // Usage: has([BODY].errors) == true + HasFunctionPrefix = "has(" + // PatternFunctionPrefix is the prefix for the pattern function // - // Usage: pat(192.168.*.*) + // Usage: [IP] == pat(192.168.*.*) PatternFunctionPrefix = "pat(" // AnyFunctionPrefix is the prefix for the any function // - // Usage: any(1.1.1.1, 1.0.0.1) + // Usage: [IP] == any(1.1.1.1, 1.0.0.1) AnyFunctionPrefix = "any(" // FunctionSuffix is the suffix for all functions @@ -209,26 +214,39 @@ func sanitizeAndResolve(elements []string, result *Result) ([]string, []string) default: // if contains the BodyPlaceholder, then evaluate json path if strings.Contains(element, BodyPlaceholder) { - wantLength := false + checkingForLength := false + checkingForExistence := false if strings.HasPrefix(element, LengthFunctionPrefix) && strings.HasSuffix(element, FunctionSuffix) { - wantLength = true + checkingForLength = true element = strings.TrimSuffix(strings.TrimPrefix(element, LengthFunctionPrefix), FunctionSuffix) } + if strings.HasPrefix(element, HasFunctionPrefix) && strings.HasSuffix(element, FunctionSuffix) { + checkingForExistence = true + element = strings.TrimSuffix(strings.TrimPrefix(element, HasFunctionPrefix), FunctionSuffix) + } resolvedElement, resolvedElementLength, err := jsonpath.Eval(strings.TrimPrefix(element, BodyPlaceholder+"."), result.body) - if err != nil { - if err.Error() != "unexpected end of JSON input" { - result.Errors = append(result.Errors, err.Error()) - } - if wantLength { - element = LengthFunctionPrefix + element + FunctionSuffix + " " + InvalidConditionElementSuffix + if checkingForExistence { + if err != nil { + element = "false" } else { - element = element + " " + InvalidConditionElementSuffix + element = "true" } } else { - if wantLength { - element = strconv.Itoa(resolvedElementLength) + if err != nil { + if err.Error() != "unexpected end of JSON input" { + result.Errors = append(result.Errors, err.Error()) + } + if checkingForLength { + element = LengthFunctionPrefix + element + FunctionSuffix + " " + InvalidConditionElementSuffix + } else { + element = element + " " + InvalidConditionElementSuffix + } } else { - element = resolvedElement + if checkingForLength { + element = strconv.Itoa(resolvedElementLength) + } else { + element = resolvedElement + } } } } diff --git a/core/condition_test.go b/core/condition_test.go index 2b4d5d99..8645ca68 100644 --- a/core/condition_test.go +++ b/core/condition_test.go @@ -1,663 +1,437 @@ package core import ( - "fmt" "strconv" "testing" "time" ) -func TestCondition_evaluateWithIP(t *testing.T) { - condition := Condition("[IP] == 127.0.0.1") - result := &Result{IP: "127.0.0.1"} - condition.evaluate(result) - if !result.ConditionResults[0].Success { - t.Errorf("Condition '%s' should have been a success", condition) - } -} - -func TestCondition_evaluateWithStatus(t *testing.T) { - condition := Condition("[STATUS] == 201") - result := &Result{HTTPStatus: 201} - condition.evaluate(result) - if !result.ConditionResults[0].Success { - t.Errorf("Condition '%s' should have been a success", condition) - } - if result.ConditionResults[0].Condition != string(condition) { - t.Errorf("Condition '%s' should have resolved to '%s', got '%s'", condition, condition, result.ConditionResults[0].Condition) - } -} - -func TestCondition_evaluateWithStatusFailure(t *testing.T) { - condition := Condition("[STATUS] == 200") - result := &Result{HTTPStatus: 500} - condition.evaluate(result) - if result.ConditionResults[0].Success { - t.Errorf("Condition '%s' should have been a failure", condition) - } - expectedConditionDisplayed := "[STATUS] (500) == 200" - if result.ConditionResults[0].Condition != expectedConditionDisplayed { - t.Errorf("Condition '%s' should have resolved to '%s', got '%s'", condition, expectedConditionDisplayed, result.ConditionResults[0].Condition) - } -} - -func TestCondition_evaluateWithStatusUsingLessThan(t *testing.T) { - condition := Condition("[STATUS] < 300") - result := &Result{HTTPStatus: 201} - condition.evaluate(result) - if !result.ConditionResults[0].Success { - t.Errorf("Condition '%s' should have been a success", condition) - } - expectedConditionDisplayed := "[STATUS] < 300" - if result.ConditionResults[0].Condition != expectedConditionDisplayed { - t.Errorf("Condition '%s' should have resolved to '%s', got '%s'", condition, expectedConditionDisplayed, result.ConditionResults[0].Condition) - } -} - -func TestCondition_evaluateWithStatusFailureUsingLessThan(t *testing.T) { - condition := Condition("[STATUS] < 300") - result := &Result{HTTPStatus: 404} - condition.evaluate(result) - if result.ConditionResults[0].Success { - t.Errorf("Condition '%s' should have been a failure", condition) - } - expectedConditionDisplayed := "[STATUS] (404) < 300" - if result.ConditionResults[0].Condition != expectedConditionDisplayed { - t.Errorf("Condition '%s' should have resolved to '%s', got '%s'", condition, expectedConditionDisplayed, result.ConditionResults[0].Condition) - } -} - -func TestCondition_evaluateWithResponseTimeUsingLessThan(t *testing.T) { - condition := Condition("[RESPONSE_TIME] < 500") - result := &Result{Duration: time.Millisecond * 50} - condition.evaluate(result) - if !result.ConditionResults[0].Success { - t.Errorf("Condition '%s' should have been a success", condition) - } - expectedConditionDisplayed := "[RESPONSE_TIME] < 500" - if result.ConditionResults[0].Condition != expectedConditionDisplayed { - t.Errorf("Condition '%s' should have resolved to '%s', got '%s'", condition, expectedConditionDisplayed, result.ConditionResults[0].Condition) - } -} - -func TestCondition_evaluateWithResponseTimeUsingLessThanDuration(t *testing.T) { - condition := Condition("[RESPONSE_TIME] < 1s") - result := &Result{Duration: time.Millisecond * 50} - condition.evaluate(result) - if !result.ConditionResults[0].Success { - t.Errorf("Condition '%s' should have been a success", condition) - } - expectedConditionDisplayed := "[RESPONSE_TIME] < 1s" - if result.ConditionResults[0].Condition != expectedConditionDisplayed { - t.Errorf("Condition '%s' should have resolved to '%s', got '%s'", condition, expectedConditionDisplayed, result.ConditionResults[0].Condition) - } -} - -func TestCondition_evaluateWithResponseTimeUsingLessThanInvalid(t *testing.T) { - condition := Condition("[RESPONSE_TIME] < potato") - result := &Result{Duration: time.Millisecond * 50} - condition.evaluate(result) - if result.ConditionResults[0].Success { - t.Errorf("Condition '%s' should have failed because the condition has an invalid numerical value that should've automatically resolved to 0", condition) - } - expectedConditionDisplayed := "[RESPONSE_TIME] (50) < potato (0)" - if result.ConditionResults[0].Condition != expectedConditionDisplayed { - t.Errorf("Condition '%s' should have resolved to '%s', got '%s'", condition, expectedConditionDisplayed, result.ConditionResults[0].Condition) - } -} - -func TestCondition_evaluateWithResponseTimeUsingGreaterThan(t *testing.T) { - // Not exactly sure why you'd want to have a condition that checks if the response time is too fast, - // but hey, who am I to judge? - condition := Condition("[RESPONSE_TIME] > 500") - result := &Result{Duration: time.Millisecond * 750} - condition.evaluate(result) - if !result.ConditionResults[0].Success { - t.Errorf("Condition '%s' should have been a success", condition) - } - expectedConditionDisplayed := "[RESPONSE_TIME] > 500" - if result.ConditionResults[0].Condition != expectedConditionDisplayed { - t.Errorf("Condition '%s' should have resolved to '%s', got '%s'", condition, expectedConditionDisplayed, result.ConditionResults[0].Condition) - } -} - -func TestCondition_evaluateWithResponseTimeUsingGreaterThanDuration(t *testing.T) { - condition := Condition("[RESPONSE_TIME] > 1s") - result := &Result{Duration: time.Second * 2} - condition.evaluate(result) - if !result.ConditionResults[0].Success { - t.Errorf("Condition '%s' should have been a success", condition) - } - expectedConditionDisplayed := "[RESPONSE_TIME] > 1s" - if result.ConditionResults[0].Condition != expectedConditionDisplayed { - t.Errorf("Condition '%s' should have resolved to '%s', got '%s'", condition, expectedConditionDisplayed, result.ConditionResults[0].Condition) - } -} - -func TestCondition_evaluateWithResponseTimeUsingGreaterThanOrEqualTo(t *testing.T) { - condition := Condition("[RESPONSE_TIME] >= 500") - result := &Result{Duration: time.Millisecond * 500} - condition.evaluate(result) - if !result.ConditionResults[0].Success { - t.Errorf("Condition '%s' should have been a success", condition) - } - expectedConditionDisplayed := "[RESPONSE_TIME] >= 500" - if result.ConditionResults[0].Condition != expectedConditionDisplayed { - t.Errorf("Condition '%s' should have resolved to '%s', got '%s'", condition, expectedConditionDisplayed, result.ConditionResults[0].Condition) - } -} - -func TestCondition_evaluateWithResponseTimeUsingLessThanOrEqualTo(t *testing.T) { - condition := Condition("[RESPONSE_TIME] <= 500") - result := &Result{Duration: time.Millisecond * 500} - condition.evaluate(result) - if !result.ConditionResults[0].Success { - t.Errorf("Condition '%s' should have been a success", condition) - } - expectedConditionDisplayed := "[RESPONSE_TIME] <= 500" - if result.ConditionResults[0].Condition != expectedConditionDisplayed { - t.Errorf("Condition '%s' should have resolved to '%s', got '%s'", condition, expectedConditionDisplayed, result.ConditionResults[0].Condition) - } -} - -func TestCondition_evaluateWithBody(t *testing.T) { - condition := Condition("[BODY] == test") - result := &Result{body: []byte("test")} - condition.evaluate(result) - if !result.ConditionResults[0].Success { - t.Errorf("Condition '%s' should have been a success", condition) - } - expectedConditionDisplayed := "[BODY] == test" - if result.ConditionResults[0].Condition != expectedConditionDisplayed { - t.Errorf("Condition '%s' should have resolved to '%s', got '%s'", condition, expectedConditionDisplayed, result.ConditionResults[0].Condition) - } -} - -func TestCondition_evaluateWithBodyJSONPath(t *testing.T) { - condition := Condition("[BODY].status == UP") - result := &Result{body: []byte("{\"status\":\"UP\"}")} - condition.evaluate(result) - if !result.ConditionResults[0].Success { - t.Errorf("Condition '%s' should have been a success", condition) - } - expectedConditionDisplayed := "[BODY].status == UP" - if result.ConditionResults[0].Condition != expectedConditionDisplayed { - t.Errorf("Condition '%s' should have resolved to '%s', got '%s'", condition, expectedConditionDisplayed, result.ConditionResults[0].Condition) - } -} - -func TestCondition_evaluateWithBodyJSONPathComplex(t *testing.T) { - condition := Condition("[BODY].data.name == john") - result := &Result{body: []byte("{\"data\": {\"id\": 1, \"name\": \"john\"}}")} - condition.evaluate(result) - if !result.ConditionResults[0].Success { - t.Errorf("Condition '%s' should have been a success", condition) - } - expectedConditionDisplayed := "[BODY].data.name == john" - if result.ConditionResults[0].Condition != expectedConditionDisplayed { - t.Errorf("Condition '%s' should have resolved to '%s', got '%s'", condition, expectedConditionDisplayed, result.ConditionResults[0].Condition) - } -} - -func TestCondition_evaluateWithInvalidBodyJSONPathComplex(t *testing.T) { - condition := Condition("[BODY].data.name == john") - result := &Result{body: []byte("{\"data\": {\"id\": 1}}")} - condition.evaluate(result) - if result.ConditionResults[0].Success { - t.Errorf("Condition '%s' should have been a failure, because the path was invalid", condition) - } - expectedConditionDisplayed := "[BODY].data.name (INVALID) == john" - if result.ConditionResults[0].Condition != expectedConditionDisplayed { - t.Errorf("Condition '%s' should have resolved to '%s', got '%s'", condition, expectedConditionDisplayed, result.ConditionResults[0].Condition) - } -} - -func TestCondition_evaluateWithInvalidBodyJSONPathComplexWithLengthFunction(t *testing.T) { - condition := Condition("len([BODY].data.name) == john") - result := &Result{body: []byte("{\"data\": {\"id\": 1}}")} - condition.evaluate(result) - if result.ConditionResults[0].Success { - t.Errorf("Condition '%s' should have been a failure, because the path was invalid", condition) - } - expectedConditionDisplayed := "len([BODY].data.name) (INVALID) == john" - if result.ConditionResults[0].Condition != expectedConditionDisplayed { - t.Errorf("Condition '%s' should have resolved to '%s', got '%s'", condition, expectedConditionDisplayed, result.ConditionResults[0].Condition) - } -} - -func TestCondition_evaluateWithBodyJSONPathDoublePlaceholders(t *testing.T) { - condition := Condition("[BODY].user.firstName != [BODY].user.lastName") - result := &Result{body: []byte("{\"user\": {\"firstName\": \"john\", \"lastName\": \"doe\"}}")} - condition.evaluate(result) - if !result.ConditionResults[0].Success { - t.Errorf("Condition '%s' should have been a success", condition) - } - expectedConditionDisplayed := "[BODY].user.firstName != [BODY].user.lastName" - if result.ConditionResults[0].Condition != expectedConditionDisplayed { - t.Errorf("Condition '%s' should have resolved to '%s', got '%s'", condition, expectedConditionDisplayed, result.ConditionResults[0].Condition) - } -} - -func TestCondition_evaluateWithBodyJSONPathDoublePlaceholdersFailure(t *testing.T) { - condition := Condition("[BODY].user.firstName == [BODY].user.lastName") - result := &Result{body: []byte("{\"user\": {\"firstName\": \"john\", \"lastName\": \"doe\"}}")} - condition.evaluate(result) - if result.ConditionResults[0].Success { - t.Errorf("Condition '%s' should have been a failure", condition) - } - expectedConditionDisplayed := "[BODY].user.firstName (john) == [BODY].user.lastName (doe)" - if result.ConditionResults[0].Condition != expectedConditionDisplayed { - t.Errorf("Condition '%s' should have resolved to '%s', got '%s'", condition, expectedConditionDisplayed, result.ConditionResults[0].Condition) - } -} - -func TestCondition_evaluateWithBodyJSONPathLongInt(t *testing.T) { - condition := Condition("[BODY].data.id == 1") - result := &Result{body: []byte("{\"data\": {\"id\": 1}}")} - condition.evaluate(result) - if !result.ConditionResults[0].Success { - t.Errorf("Condition '%s' should have been a success", condition) - } - expectedConditionDisplayed := "[BODY].data.id == 1" - if result.ConditionResults[0].Condition != expectedConditionDisplayed { - t.Errorf("Condition '%s' should have resolved to '%s', got '%s'", condition, expectedConditionDisplayed, result.ConditionResults[0].Condition) - } -} - -func TestCondition_evaluateWithBodyJSONPathComplexInt(t *testing.T) { - condition := Condition("[BODY].data[1].id == 2") - result := &Result{body: []byte("{\"data\": [{\"id\": 1}, {\"id\": 2}, {\"id\": 3}]}")} - condition.evaluate(result) - if !result.ConditionResults[0].Success { - t.Errorf("Condition '%s' should have been a success", condition) - } - expectedConditionDisplayed := "[BODY].data[1].id == 2" - if result.ConditionResults[0].Condition != expectedConditionDisplayed { - t.Errorf("Condition '%s' should have resolved to '%s', got '%s'", condition, expectedConditionDisplayed, result.ConditionResults[0].Condition) - } -} - -func TestCondition_evaluateWithBodyJSONPathComplexIntUsingGreaterThan(t *testing.T) { - condition := Condition("[BODY].data.id > 0") - result := &Result{body: []byte("{\"data\": {\"id\": 1}}")} - condition.evaluate(result) - if !result.ConditionResults[0].Success { - t.Errorf("Condition '%s' should have been a success", condition) - } - expectedConditionDisplayed := "[BODY].data.id > 0" - if result.ConditionResults[0].Condition != expectedConditionDisplayed { - t.Errorf("Condition '%s' should have resolved to '%s', got '%s'", condition, expectedConditionDisplayed, result.ConditionResults[0].Condition) - } -} - -func TestCondition_evaluateWithBodyJSONPathComplexIntFailureUsingGreaterThan(t *testing.T) { - condition := Condition("[BODY].data.id > 5") - result := &Result{body: []byte("{\"data\": {\"id\": 1}}")} - condition.evaluate(result) - if result.ConditionResults[0].Success { - t.Errorf("Condition '%s' should have been a failure", condition) - } - expectedConditionDisplayed := "[BODY].data.id (1) > 5" - if result.ConditionResults[0].Condition != expectedConditionDisplayed { - t.Errorf("Condition '%s' should have resolved to '%s', got '%s'", condition, expectedConditionDisplayed, result.ConditionResults[0].Condition) - } -} - -func TestCondition_evaluateWithBodyJSONPathComplexIntUsingLessThan(t *testing.T) { - condition := Condition("[BODY].data.id < 5") - result := &Result{body: []byte("{\"data\": {\"id\": 2}}")} - condition.evaluate(result) - if !result.ConditionResults[0].Success { - t.Errorf("Condition '%s' should have been a success", condition) - } - expectedConditionDisplayed := "[BODY].data.id < 5" - if result.ConditionResults[0].Condition != expectedConditionDisplayed { - t.Errorf("Condition '%s' should have resolved to '%s', got '%s'", condition, expectedConditionDisplayed, result.ConditionResults[0].Condition) - } -} - -func TestCondition_evaluateWithBodyJSONPathComplexIntFailureUsingLessThan(t *testing.T) { - condition := Condition("[BODY].data.id < 5") - result := &Result{body: []byte("{\"data\": {\"id\": 10}}")} - condition.evaluate(result) - if result.ConditionResults[0].Success { - t.Errorf("Condition '%s' should have been a failure", condition) - } - expectedConditionDisplayed := "[BODY].data.id (10) < 5" - if result.ConditionResults[0].Condition != expectedConditionDisplayed { - t.Errorf("Condition '%s' should have resolved to '%s', got '%s'", condition, expectedConditionDisplayed, result.ConditionResults[0].Condition) - } -} - -func TestCondition_evaluateWithBodySliceLength(t *testing.T) { - condition := Condition("len([BODY].data) == 3") - result := &Result{body: []byte("{\"data\": [{\"id\": 1}, {\"id\": 2}, {\"id\": 3}]}")} - condition.evaluate(result) - if !result.ConditionResults[0].Success { - t.Errorf("Condition '%s' should have been a success", condition) - } - expectedConditionDisplayed := "len([BODY].data) == 3" - if result.ConditionResults[0].Condition != expectedConditionDisplayed { - t.Errorf("Condition '%s' should have resolved to '%s', got '%s'", condition, expectedConditionDisplayed, result.ConditionResults[0].Condition) - } -} - -func TestCondition_evaluateWithBodyStringLength(t *testing.T) { - condition := Condition("len([BODY].name) == 8") - result := &Result{body: []byte("{\"name\": \"john.doe\"}")} - condition.evaluate(result) - if !result.ConditionResults[0].Success { - t.Errorf("Condition '%s' should have been a success", condition) - } - expectedConditionDisplayed := "len([BODY].name) == 8" - if result.ConditionResults[0].Condition != expectedConditionDisplayed { - t.Errorf("Condition '%s' should have resolved to '%s', got '%s'", condition, expectedConditionDisplayed, result.ConditionResults[0].Condition) - } -} - -func TestCondition_evaluateWithBodyPattern(t *testing.T) { - condition := Condition("[BODY] == pat(*john*)") - result := &Result{body: []byte("{\"name\": \"john.doe\"}")} - condition.evaluate(result) - if !result.ConditionResults[0].Success { - t.Errorf("Condition '%s' should have been a success", condition) - } - expectedConditionDisplayed := "[BODY] == pat(*john*)" - if result.ConditionResults[0].Condition != expectedConditionDisplayed { - t.Errorf("Condition '%s' should have resolved to '%s', got '%s'", condition, expectedConditionDisplayed, result.ConditionResults[0].Condition) - } -} - -func TestCondition_evaluateWithReverseBodyPattern(t *testing.T) { - condition := Condition("pat(*john*) == [BODY]") - result := &Result{body: []byte("{\"name\": \"john.doe\"}")} - condition.evaluate(result) - if !result.ConditionResults[0].Success { - t.Errorf("Condition '%s' should have been a success", condition) - } - expectedConditionDisplayed := "pat(*john*) == [BODY]" - if result.ConditionResults[0].Condition != expectedConditionDisplayed { - t.Errorf("Condition '%s' should have resolved to '%s', got '%s'", condition, expectedConditionDisplayed, result.ConditionResults[0].Condition) - } -} - -func TestCondition_evaluateWithBodyStringPattern(t *testing.T) { - condition := Condition("[BODY].name == pat(*ohn*)") - result := &Result{body: []byte("{\"name\": \"john.doe\"}")} - condition.evaluate(result) - if !result.ConditionResults[0].Success { - t.Errorf("Condition '%s' should have been a success", condition) - } - expectedConditionDisplayed := "[BODY].name == pat(*ohn*)" - if result.ConditionResults[0].Condition != expectedConditionDisplayed { - t.Errorf("Condition '%s' should have resolved to '%s', got '%s'", condition, expectedConditionDisplayed, result.ConditionResults[0].Condition) - } -} - -func TestCondition_evaluateWithBodyHTMLPattern(t *testing.T) { - var html = `
john.doe
` - condition := Condition("[BODY] == pat(*
john.doe
*)") - result := &Result{body: []byte(html)} - condition.evaluate(result) - if !result.ConditionResults[0].Success { - t.Errorf("Condition '%s' should have been a success", condition) - } - expectedConditionDisplayed := "[BODY] == pat(*
john.doe
*)" - if result.ConditionResults[0].Condition != expectedConditionDisplayed { - t.Errorf("Condition '%s' should have resolved to '%s', got '%s'", condition, expectedConditionDisplayed, result.ConditionResults[0].Condition) - } -} - -func TestCondition_evaluateWithBodyStringPatternFailure(t *testing.T) { - condition := Condition("[BODY].name == pat(bob*)") - result := &Result{body: []byte("{\"name\": \"john.doe\"}")} - condition.evaluate(result) - if result.ConditionResults[0].Success { - t.Errorf("Condition '%s' should have been a failure", condition) - } - expectedConditionDisplayed := "[BODY].name (john.doe) == pat(bob*)" - if result.ConditionResults[0].Condition != expectedConditionDisplayed { - t.Errorf("Condition '%s' should have resolved to '%s', got '%s'", condition, expectedConditionDisplayed, result.ConditionResults[0].Condition) - } -} - -func TestCondition_evaluateWithIPPattern(t *testing.T) { - condition := Condition("[IP] == pat(10.*)") - result := &Result{IP: "10.0.0.0"} - condition.evaluate(result) - if !result.ConditionResults[0].Success { - t.Errorf("Condition '%s' should have been a success", condition) - } - expectedConditionDisplayed := "[IP] == pat(10.*)" - if result.ConditionResults[0].Condition != expectedConditionDisplayed { - t.Errorf("Condition '%s' should have resolved to '%s', got '%s'", condition, expectedConditionDisplayed, result.ConditionResults[0].Condition) - } -} - -func TestCondition_evaluateWithIPPatternFailure(t *testing.T) { - condition := Condition("[IP] == pat(10.*)") - result := &Result{IP: "255.255.255.255"} - condition.evaluate(result) - if result.ConditionResults[0].Success { - t.Errorf("Condition '%s' should have been a failure", condition) - } - expectedConditionDisplayed := "[IP] (255.255.255.255) == pat(10.*)" - if result.ConditionResults[0].Condition != expectedConditionDisplayed { - t.Errorf("Condition '%s' should have resolved to '%s', got '%s'", condition, expectedConditionDisplayed, result.ConditionResults[0].Condition) - } -} - -func TestCondition_evaluateWithStatusPattern(t *testing.T) { - condition := Condition("[STATUS] == pat(4*)") - result := &Result{HTTPStatus: 404} - condition.evaluate(result) - if !result.ConditionResults[0].Success { - t.Errorf("Condition '%s' should have been a success", condition) - } - expectedConditionDisplayed := "[STATUS] == pat(4*)" - if result.ConditionResults[0].Condition != expectedConditionDisplayed { - t.Errorf("Condition '%s' should have resolved to '%s', got '%s'", condition, expectedConditionDisplayed, result.ConditionResults[0].Condition) - } -} - -func TestCondition_evaluateWithStatusPatternFailure(t *testing.T) { - condition := Condition("[STATUS] != pat(4*)") - result := &Result{HTTPStatus: 404} - condition.evaluate(result) - if result.ConditionResults[0].Success { - t.Errorf("Condition '%s' should have been a failure", condition) - } - expectedConditionDisplayed := "[STATUS] (404) != pat(4*)" - if result.ConditionResults[0].Condition != expectedConditionDisplayed { - t.Errorf("Condition '%s' should have resolved to '%s', got '%s'", condition, expectedConditionDisplayed, result.ConditionResults[0].Condition) - } -} - -func TestCondition_evaluateWithBodyStringAny(t *testing.T) { - condition := Condition("[BODY].name == any(john.doe, jane.doe)") - expectedConditionDisplayed := "[BODY].name == any(john.doe, jane.doe)" - results := []*Result{ - {body: []byte("{\"name\": \"john.doe\"}")}, - {body: []byte("{\"name\": \"jane.doe\"}")}, - } - for _, result := range results { - success := condition.evaluate(result) - if !success || !result.ConditionResults[0].Success { - t.Errorf("Condition '%s' should have been a success", condition) - } - if result.ConditionResults[0].Condition != expectedConditionDisplayed { - t.Errorf("Condition '%s' should have resolved to '%s', got '%s'", condition, expectedConditionDisplayed, result.ConditionResults[0].Condition) - } - } -} - -func TestCondition_evaluateWithBodyStringAnyFailure(t *testing.T) { - condition := Condition("[BODY].name == any(john.doe, jane.doe)") - result := &Result{body: []byte("{\"name\": \"bob.doe\"}")} - condition.evaluate(result) - if result.ConditionResults[0].Success { - t.Errorf("Condition '%s' should have been a failure", condition) - } - expectedConditionDisplayed := "[BODY].name (bob.doe) == any(john.doe, jane.doe)" - if result.ConditionResults[0].Condition != expectedConditionDisplayed { - t.Errorf("Condition '%s' should have resolved to '%s', got '%s'", condition, expectedConditionDisplayed, result.ConditionResults[0].Condition) - } -} - -func TestCondition_evaluateWithStatusAny(t *testing.T) { - condition := Condition("[STATUS] == any(200, 429)") - statuses := []int{200, 429} - for _, status := range statuses { - result := &Result{HTTPStatus: status} - condition.evaluate(result) - if !result.ConditionResults[0].Success { - t.Errorf("Condition '%s' should have been a success", condition) - } - expectedConditionDisplayed := "[STATUS] == any(200, 429)" - if result.ConditionResults[0].Condition != expectedConditionDisplayed { - t.Errorf("Condition '%s' should have resolved to '%s', got '%s'", condition, expectedConditionDisplayed, result.ConditionResults[0].Condition) - } - } -} - -func TestCondition_evaluateWithReverseStatusAny(t *testing.T) { - condition := Condition("any(200, 429) == [STATUS]") - statuses := []int{200, 429} - for _, status := range statuses { - result := &Result{HTTPStatus: status} - condition.evaluate(result) - if !result.ConditionResults[0].Success { - t.Errorf("Condition '%s' should have been a success", condition) - } - expectedConditionDisplayed := "any(200, 429) == [STATUS]" - if result.ConditionResults[0].Condition != expectedConditionDisplayed { - t.Errorf("Condition '%s' should have resolved to '%s', got '%s'", condition, expectedConditionDisplayed, result.ConditionResults[0].Condition) - } - } -} - -func TestCondition_evaluateWithStatusAnyFailure(t *testing.T) { - condition := Condition("[STATUS] == any(200, 429)") - statuses := []int{201, 400, 404, 500} - for _, status := range statuses { - result := &Result{HTTPStatus: status} - condition.evaluate(result) - if result.ConditionResults[0].Success { - t.Errorf("Condition '%s' should have been a failure", condition) - } - expectedConditionDisplayed := fmt.Sprintf("[STATUS] (%d) == any(200, 429)", status) - if result.ConditionResults[0].Condition != expectedConditionDisplayed { - t.Errorf("Condition '%s' should have resolved to '%s', got '%s'", condition, expectedConditionDisplayed, result.ConditionResults[0].Condition) - } - } -} - -func TestCondition_evaluateWithReverseStatusAnyFailure(t *testing.T) { - condition := Condition("any(200, 429) == [STATUS]") - statuses := []int{201, 400, 404, 500} - for _, status := range statuses { - result := &Result{HTTPStatus: status} - condition.evaluate(result) - if result.ConditionResults[0].Success { - t.Errorf("Condition '%s' should have been a failure", condition) - } - expectedConditionDisplayed := fmt.Sprintf("any(200, 429) == [STATUS] (%d)", status) - if result.ConditionResults[0].Condition != expectedConditionDisplayed { - t.Errorf("Condition '%s' should have resolved to '%s', got '%s'", condition, expectedConditionDisplayed, result.ConditionResults[0].Condition) - } - } -} - -func TestCondition_evaluateWithConnected(t *testing.T) { - condition := Condition("[CONNECTED] == true") - result := &Result{Connected: true} - condition.evaluate(result) - if !result.ConditionResults[0].Success { - t.Errorf("Condition '%s' should have been a success", condition) - } - expectedConditionDisplayed := "[CONNECTED] == true" - if result.ConditionResults[0].Condition != expectedConditionDisplayed { - t.Errorf("Condition '%s' should have resolved to '%s', got '%s'", condition, expectedConditionDisplayed, result.ConditionResults[0].Condition) - } -} - -func TestCondition_evaluateWithConnectedFailure(t *testing.T) { - condition := Condition("[CONNECTED] == true") - result := &Result{Connected: false} - condition.evaluate(result) - if result.ConditionResults[0].Success { - t.Errorf("Condition '%s' should have been a failure", condition) - } - expectedConditionDisplayed := "[CONNECTED] (false) == true" - if result.ConditionResults[0].Condition != expectedConditionDisplayed { - t.Errorf("Condition '%s' should have resolved to '%s', got '%s'", condition, expectedConditionDisplayed, result.ConditionResults[0].Condition) - } -} - -func TestCondition_evaluateWithUnsetCertificateExpiration(t *testing.T) { - condition := Condition("[CERTIFICATE_EXPIRATION] == 0") - result := &Result{} - condition.evaluate(result) - if !result.ConditionResults[0].Success { - t.Errorf("Condition '%s' should have been a success", condition) - } - expectedConditionDisplayed := "[CERTIFICATE_EXPIRATION] == 0" - if result.ConditionResults[0].Condition != expectedConditionDisplayed { - t.Errorf("Condition '%s' should have resolved to '%s', got '%s'", condition, expectedConditionDisplayed, result.ConditionResults[0].Condition) - } -} - -func TestCondition_evaluateWithCertificateExpirationGreaterThanNumerical(t *testing.T) { - acceptable := (time.Hour * 24 * 28).Milliseconds() - condition := Condition("[CERTIFICATE_EXPIRATION] > " + strconv.FormatInt(acceptable, 10)) - result := &Result{CertificateExpiration: time.Hour * 24 * 60} - condition.evaluate(result) - if !result.ConditionResults[0].Success { - t.Errorf("Condition '%s' should have been a success", condition) - } - expectedConditionDisplayed := "[CERTIFICATE_EXPIRATION] > 2419200000" - if result.ConditionResults[0].Condition != expectedConditionDisplayed { - t.Errorf("Condition '%s' should have resolved to '%s', got '%s'", condition, expectedConditionDisplayed, result.ConditionResults[0].Condition) - } -} - -func TestCondition_evaluateWithCertificateExpirationGreaterThanNumericalFailure(t *testing.T) { - acceptable := (time.Hour * 24 * 28).Milliseconds() - condition := Condition("[CERTIFICATE_EXPIRATION] > " + strconv.FormatInt(acceptable, 10)) - result := &Result{CertificateExpiration: time.Hour * 24 * 14} - condition.evaluate(result) - if result.ConditionResults[0].Success { - t.Errorf("Condition '%s' should have been a failure", condition) - } - expectedConditionDisplayed := "[CERTIFICATE_EXPIRATION] (1209600000) > 2419200000" - if result.ConditionResults[0].Condition != expectedConditionDisplayed { - t.Errorf("Condition '%s' should have resolved to '%s', got '%s'", condition, expectedConditionDisplayed, result.ConditionResults[0].Condition) - } -} - -func TestCondition_evaluateWithCertificateExpirationGreaterThanDuration(t *testing.T) { - condition := Condition("[CERTIFICATE_EXPIRATION] > 12h") - result := &Result{CertificateExpiration: 24 * time.Hour} - condition.evaluate(result) - if !result.ConditionResults[0].Success { - t.Errorf("Condition '%s' should have been a success", condition) - } - expectedConditionDisplayed := "[CERTIFICATE_EXPIRATION] > 12h" - if result.ConditionResults[0].Condition != expectedConditionDisplayed { - t.Errorf("Condition '%s' should have resolved to '%s', got '%s'", condition, expectedConditionDisplayed, result.ConditionResults[0].Condition) - } -} - -func TestCondition_evaluateWithCertificateExpirationGreaterThanDurationFailure(t *testing.T) { - condition := Condition("[CERTIFICATE_EXPIRATION] > 48h") - result := &Result{CertificateExpiration: 24 * time.Hour} - condition.evaluate(result) - if result.ConditionResults[0].Success { - t.Errorf("Condition '%s' should have been a failure", condition) - } - expectedConditionDisplayed := "[CERTIFICATE_EXPIRATION] (86400000) > 48h (172800000)" - if result.ConditionResults[0].Condition != expectedConditionDisplayed { - t.Errorf("Condition '%s' should have resolved to '%s', got '%s'", condition, expectedConditionDisplayed, result.ConditionResults[0].Condition) +func TestCondition_evaluate(t *testing.T) { + type scenario struct { + Name string + Condition Condition + Result *Result + ExpectedSuccess bool + ExpectedOutput string + } + scenarios := []scenario{ + { + Name: "ip", + Condition: Condition("[IP] == 127.0.0.1"), + Result: &Result{IP: "127.0.0.1"}, + ExpectedSuccess: true, + ExpectedOutput: "[IP] == 127.0.0.1", + }, + { + Name: "status", + Condition: Condition("[STATUS] == 200"), + Result: &Result{HTTPStatus: 200}, + ExpectedSuccess: true, + ExpectedOutput: "[STATUS] == 200", + }, + { + Name: "status-failure", + Condition: Condition("[STATUS] == 200"), + Result: &Result{HTTPStatus: 500}, + ExpectedSuccess: false, + ExpectedOutput: "[STATUS] (500) == 200", + }, + { + Name: "status-using-less-than", + Condition: Condition("[STATUS] < 300"), + Result: &Result{HTTPStatus: 201}, + ExpectedSuccess: true, + ExpectedOutput: "[STATUS] < 300", + }, + { + Name: "status-using-less-than-failure", + Condition: Condition("[STATUS] < 300"), + Result: &Result{HTTPStatus: 404}, + ExpectedSuccess: false, + ExpectedOutput: "[STATUS] (404) < 300", + }, + { + Name: "response-time-using-less-than", + Condition: Condition("[RESPONSE_TIME] < 500"), + Result: &Result{Duration: 50 * time.Millisecond}, + ExpectedSuccess: true, + ExpectedOutput: "[RESPONSE_TIME] < 500", + }, + { + Name: "response-time-using-less-than-with-duration", + Condition: Condition("[RESPONSE_TIME] < 1s"), + Result: &Result{Duration: 50 * time.Millisecond}, + ExpectedSuccess: true, + ExpectedOutput: "[RESPONSE_TIME] < 1s", + }, + { + Name: "response-time-using-less-than-invalid", + Condition: Condition("[RESPONSE_TIME] < potato"), + Result: &Result{Duration: 50 * time.Millisecond}, + ExpectedSuccess: false, + ExpectedOutput: "[RESPONSE_TIME] (50) < potato (0)", // Non-numerical values automatically resolve to 0 + }, + { + Name: "response-time-using-greater-than", + Condition: Condition("[RESPONSE_TIME] > 500"), + Result: &Result{Duration: 750 * time.Millisecond}, + ExpectedSuccess: true, + ExpectedOutput: "[RESPONSE_TIME] > 500", + }, + { + Name: "response-time-using-greater-than-with-duration", + Condition: Condition("[RESPONSE_TIME] > 1s"), + Result: &Result{Duration: 2 * time.Second}, + ExpectedSuccess: true, + ExpectedOutput: "[RESPONSE_TIME] > 1s", + }, + { + Name: "response-time-using-greater-than-or-equal-to-equal", + Condition: Condition("[RESPONSE_TIME] >= 500"), + Result: &Result{Duration: 500 * time.Millisecond}, + ExpectedSuccess: true, + ExpectedOutput: "[RESPONSE_TIME] >= 500", + }, + { + Name: "response-time-using-greater-than-or-equal-to-greater", + Condition: Condition("[RESPONSE_TIME] >= 500"), + Result: &Result{Duration: 499 * time.Millisecond}, + ExpectedSuccess: false, + ExpectedOutput: "[RESPONSE_TIME] (499) >= 500", + }, + { + Name: "response-time-using-greater-than-or-equal-to-failure", + Condition: Condition("[RESPONSE_TIME] >= 500"), + Result: &Result{Duration: 499 * time.Millisecond}, + ExpectedSuccess: false, + ExpectedOutput: "[RESPONSE_TIME] (499) >= 500", + }, + { + Name: "response-time-using-less-than-or-equal-to-equal", + Condition: Condition("[RESPONSE_TIME] <= 500"), + Result: &Result{Duration: 500 * time.Millisecond}, + ExpectedSuccess: true, + ExpectedOutput: "[RESPONSE_TIME] <= 500", + }, + { + Name: "response-time-using-less-than-or-equal-to-less", + Condition: Condition("[RESPONSE_TIME] <= 500"), + Result: &Result{Duration: 25 * time.Millisecond}, + ExpectedSuccess: true, + ExpectedOutput: "[RESPONSE_TIME] <= 500", + }, + { + Name: "response-time-using-less-than-or-equal-to-failure", + Condition: Condition("[RESPONSE_TIME] <= 500"), + Result: &Result{Duration: 750 * time.Millisecond}, + ExpectedSuccess: false, + ExpectedOutput: "[RESPONSE_TIME] (750) <= 500", + }, + { + Name: "body", + Condition: Condition("[BODY] == test"), + Result: &Result{body: []byte("test")}, + ExpectedSuccess: true, + ExpectedOutput: "[BODY] == test", + }, + { + Name: "body-jsonpath", + Condition: Condition("[BODY].status == UP"), + Result: &Result{body: []byte("{\"status\":\"UP\"}")}, + ExpectedSuccess: true, + ExpectedOutput: "[BODY].status == UP", + }, + { + Name: "body-jsonpath-complex", + Condition: Condition("[BODY].data.name == john"), + Result: &Result{body: []byte("{\"data\": {\"id\": 1, \"name\": \"john\"}}")}, + ExpectedSuccess: true, + ExpectedOutput: "[BODY].data.name == john", + }, + { + Name: "body-jsonpath-complex-invalid", + Condition: Condition("[BODY].data.name == john"), + Result: &Result{body: []byte("{\"data\": {\"id\": 1}}")}, + ExpectedSuccess: false, + ExpectedOutput: "[BODY].data.name (INVALID) == john", + }, + { + Name: "body-jsonpath-complex-len", + Condition: Condition("len([BODY].data.name) == 4"), + Result: &Result{body: []byte("{\"data\": {\"name\": \"john\"}}")}, + ExpectedSuccess: true, + ExpectedOutput: "len([BODY].data.name) == 4", + }, + { + Name: "body-jsonpath-complex-len-invalid", + Condition: Condition("len([BODY].data.name) == john"), + Result: &Result{body: []byte("{\"data\": {\"id\": 1}}")}, + ExpectedSuccess: false, + ExpectedOutput: "len([BODY].data.name) (INVALID) == john", + }, + { + Name: "body-jsonpath-double-placeholder", + Condition: Condition("[BODY].user.firstName != [BODY].user.lastName"), + Result: &Result{body: []byte("{\"user\": {\"firstName\": \"john\", \"lastName\": \"doe\"}}")}, + ExpectedSuccess: true, + ExpectedOutput: "[BODY].user.firstName != [BODY].user.lastName", + }, + { + Name: "body-jsonpath-double-placeholder-failure", + Condition: Condition("[BODY].user.firstName == [BODY].user.lastName"), + Result: &Result{body: []byte("{\"user\": {\"firstName\": \"john\", \"lastName\": \"doe\"}}")}, + ExpectedSuccess: false, + ExpectedOutput: "[BODY].user.firstName (john) == [BODY].user.lastName (doe)", + }, + { + Name: "body-jsonpath-complex-int", + Condition: Condition("[BODY].data.id == 1"), + Result: &Result{body: []byte("{\"data\": {\"id\": 1}}")}, + ExpectedSuccess: true, + ExpectedOutput: "[BODY].data.id == 1", + }, + { + Name: "body-jsonpath-complex-array-int", + Condition: Condition("[BODY].data[1].id == 2"), + Result: &Result{body: []byte("{\"data\": [{\"id\": 1}, {\"id\": 2}, {\"id\": 3}]}")}, + ExpectedSuccess: true, + ExpectedOutput: "[BODY].data[1].id == 2", + }, + { + Name: "body-jsonpath-complex-int-using-greater-than", + Condition: Condition("[BODY].data.id > 0"), + Result: &Result{body: []byte("{\"data\": {\"id\": 1}}")}, + ExpectedSuccess: true, + ExpectedOutput: "[BODY].data.id > 0", + }, + { + Name: "body-jsonpath-complex-int-using-greater-than-failure", + Condition: Condition("[BODY].data.id > 5"), + Result: &Result{body: []byte("{\"data\": {\"id\": 1}}")}, + ExpectedSuccess: false, + ExpectedOutput: "[BODY].data.id (1) > 5", + }, + { + Name: "body-jsonpath-complex-int-using-less-than", + Condition: Condition("[BODY].data.id < 5"), + Result: &Result{body: []byte("{\"data\": {\"id\": 2}}")}, + ExpectedSuccess: true, + ExpectedOutput: "[BODY].data.id < 5", + }, + { + Name: "body-jsonpath-complex-int-using-less-than-failure", + Condition: Condition("[BODY].data.id < 5"), + Result: &Result{body: []byte("{\"data\": {\"id\": 10}}")}, + ExpectedSuccess: false, + ExpectedOutput: "[BODY].data.id (10) < 5", + }, + { + Name: "body-len-array", + Condition: Condition("len([BODY].data) == 3"), + Result: &Result{body: []byte("{\"data\": [{\"id\": 1}, {\"id\": 2}, {\"id\": 3}]}")}, + ExpectedSuccess: true, + ExpectedOutput: "len([BODY].data) == 3", + }, + { + Name: "body-len-array-invalid", + Condition: Condition("len([BODY].data) == 8"), + Result: &Result{body: []byte("{\"name\": \"john.doe\"}")}, + ExpectedSuccess: false, + ExpectedOutput: "len([BODY].data) (INVALID) == 8", + }, + { + Name: "body-len-string", + Condition: Condition("len([BODY].name) == 8"), + Result: &Result{body: []byte("{\"name\": \"john.doe\"}")}, + ExpectedSuccess: true, + ExpectedOutput: "len([BODY].name) == 8", + }, + { + Name: "body-pattern", + Condition: Condition("[BODY] == pat(*john*)"), + Result: &Result{body: []byte("{\"name\": \"john.doe\"}")}, + ExpectedSuccess: true, + ExpectedOutput: "[BODY] == pat(*john*)", + }, + { + Name: "body-pattern-2", + Condition: Condition("[BODY].name == pat(john*)"), + Result: &Result{body: []byte("{\"name\": \"john.doe\"}")}, + ExpectedSuccess: true, + ExpectedOutput: "[BODY].name == pat(john*)", + }, + { + Name: "body-pattern-failure", + Condition: Condition("[BODY].name == pat(bob*)"), + Result: &Result{body: []byte("{\"name\": \"john.doe\"}")}, + ExpectedSuccess: false, + ExpectedOutput: "[BODY].name (john.doe) == pat(bob*)", + }, + { + Name: "body-pattern-html", + Condition: Condition("[BODY] == pat(*
john.doe
*)"), + Result: &Result{body: []byte(`
john.doe
`)}, + ExpectedSuccess: true, + ExpectedOutput: "[BODY] == pat(*
john.doe
*)", + }, + { + Name: "ip-pattern", + Condition: Condition("[IP] == pat(10.*)"), + Result: &Result{IP: "10.0.0.0"}, + ExpectedSuccess: true, + ExpectedOutput: "[IP] == pat(10.*)", + }, + { + Name: "ip-pattern-failure", + Condition: Condition("[IP] == pat(10.*)"), + Result: &Result{IP: "255.255.255.255"}, + ExpectedSuccess: false, + ExpectedOutput: "[IP] (255.255.255.255) == pat(10.*)", + }, + { + Name: "status-pattern", + Condition: Condition("[STATUS] == pat(4*)"), + Result: &Result{HTTPStatus: 404}, + ExpectedSuccess: true, + ExpectedOutput: "[STATUS] == pat(4*)", + }, + { + Name: "status-pattern-failure", + Condition: Condition("[STATUS] == pat(4*)"), + Result: &Result{HTTPStatus: 200}, + ExpectedSuccess: false, + ExpectedOutput: "[STATUS] (200) == pat(4*)", + }, + { + Name: "body-any", + Condition: Condition("[BODY].name == any(john.doe, jane.doe)"), + Result: &Result{body: []byte("{\"name\": \"john.doe\"}")}, + ExpectedSuccess: true, + ExpectedOutput: "[BODY].name == any(john.doe, jane.doe)", + }, + { + Name: "body-any-2", + Condition: Condition("[BODY].name == any(john.doe, jane.doe)"), + Result: &Result{body: []byte("{\"name\": \"jane.doe\"}")}, + ExpectedSuccess: true, + ExpectedOutput: "[BODY].name == any(john.doe, jane.doe)", + }, + { + Name: "body-any-failure", + Condition: Condition("[BODY].name == any(john.doe, jane.doe)"), + Result: &Result{body: []byte("{\"name\": \"bob\"}")}, + ExpectedSuccess: false, + ExpectedOutput: "[BODY].name (bob) == any(john.doe, jane.doe)", + }, + { + Name: "status-any", + Condition: Condition("[STATUS] == any(200, 429)"), + Result: &Result{HTTPStatus: 200}, + ExpectedSuccess: true, + ExpectedOutput: "[STATUS] == any(200, 429)", + }, + { + Name: "status-any-2", + Condition: Condition("[STATUS] == any(200, 429)"), + Result: &Result{HTTPStatus: 429}, + ExpectedSuccess: true, + ExpectedOutput: "[STATUS] == any(200, 429)", + }, + { + Name: "status-any-reverse", + Condition: Condition("any(200, 429) == [STATUS]"), + Result: &Result{HTTPStatus: 429}, + ExpectedSuccess: true, + ExpectedOutput: "any(200, 429) == [STATUS]", + }, + { + Name: "status-any-failure", + Condition: Condition("[STATUS] == any(200, 429)"), + Result: &Result{HTTPStatus: 404}, + ExpectedSuccess: false, + ExpectedOutput: "[STATUS] (404) == any(200, 429)", + }, + { + Name: "connected", + Condition: Condition("[CONNECTED] == true"), + Result: &Result{Connected: true}, + ExpectedSuccess: true, + ExpectedOutput: "[CONNECTED] == true", + }, + { + Name: "connected-failure", + Condition: Condition("[CONNECTED] == true"), + Result: &Result{Connected: false}, + ExpectedSuccess: false, + ExpectedOutput: "[CONNECTED] (false) == true", + }, + { + Name: "certificate-expiration-not-set", + Condition: Condition("[CERTIFICATE_EXPIRATION] == 0"), + Result: &Result{}, + ExpectedSuccess: true, + ExpectedOutput: "[CERTIFICATE_EXPIRATION] == 0", + }, + { + Name: "certificate-expiration-greater-than-numerical", + Condition: Condition("[CERTIFICATE_EXPIRATION] > " + strconv.FormatInt((time.Hour*24*28).Milliseconds(), 10)), + Result: &Result{CertificateExpiration: time.Hour * 24 * 60}, + ExpectedSuccess: true, + ExpectedOutput: "[CERTIFICATE_EXPIRATION] > 2419200000", + }, + { + Name: "certificate-expiration-greater-than-numerical-failure", + Condition: Condition("[CERTIFICATE_EXPIRATION] > " + strconv.FormatInt((time.Hour*24*28).Milliseconds(), 10)), + Result: &Result{CertificateExpiration: time.Hour * 24 * 14}, + ExpectedSuccess: false, + ExpectedOutput: "[CERTIFICATE_EXPIRATION] (1209600000) > 2419200000", + }, + { + Name: "certificate-expiration-greater-than-duration", + Condition: Condition("[CERTIFICATE_EXPIRATION] > 12h"), + Result: &Result{CertificateExpiration: 24 * time.Hour}, + ExpectedSuccess: true, + ExpectedOutput: "[CERTIFICATE_EXPIRATION] > 12h", + }, + { + Name: "certificate-expiration-greater-than-duration", + Condition: Condition("[CERTIFICATE_EXPIRATION] > 48h"), + Result: &Result{CertificateExpiration: 24 * time.Hour}, + ExpectedSuccess: false, + ExpectedOutput: "[CERTIFICATE_EXPIRATION] (86400000) > 48h (172800000)", + }, + { + Name: "has", + Condition: Condition("has([BODY].errors) == false"), + Result: &Result{body: []byte("{}")}, + ExpectedSuccess: true, + ExpectedOutput: "has([BODY].errors) == false", + }, + { + Name: "has-failure", + Condition: Condition("has([BODY].errors) == false"), + Result: &Result{body: []byte("{\"errors\": [\"1\"]}")}, + ExpectedSuccess: false, + ExpectedOutput: "has([BODY].errors) (true) == false", + }, + { + Name: "no-placeholders", + Condition: Condition("1 == 2"), + Result: &Result{}, + ExpectedSuccess: false, + ExpectedOutput: "1 == 2", + }, + } + for _, scenario := range scenarios { + t.Run(scenario.Name, func(t *testing.T) { + scenario.Condition.evaluate(scenario.Result) + if scenario.Result.ConditionResults[0].Success != scenario.ExpectedSuccess { + t.Errorf("Condition '%s' should have been success=%v", scenario.Condition, scenario.ExpectedSuccess) + } + if scenario.Result.ConditionResults[0].Condition != scenario.ExpectedOutput { + t.Errorf("Condition '%s' should have resolved to '%s', got '%s'", scenario.Condition, scenario.ExpectedOutput, scenario.Result.ConditionResults[0].Condition) + } + }) } } @@ -672,15 +446,3 @@ func TestCondition_evaluateWithInvalidOperator(t *testing.T) { t.Error("condition was invalid, result should've had an error") } } - -func TestCondition_evaluateWithNoPlaceholder(t *testing.T) { - condition := Condition("1 == 2") - result := &Result{} - condition.evaluate(result) - if result.ConditionResults[0].Success { - t.Errorf("Condition '%s' should have been a failure", condition) - } - if result.ConditionResults[0].Condition != string(condition) { - t.Errorf("Condition '%s' should have resolved to '%s', got '%s'", condition, condition, result.ConditionResults[0].Condition) - } -}