mirror of
https://github.com/TwiN/gatus.git
synced 2026-02-14 18:42:27 +00:00
fix(condition): Properly format conditions with invalid context placeholders (#1281)
This commit is contained in:
@@ -214,30 +214,35 @@ func prettifyNumericalParameters(parameters []string, resolvedParameters []int64
|
|||||||
|
|
||||||
// prettify returns a string representation of a condition with its parameters resolved between parentheses
|
// prettify returns a string representation of a condition with its parameters resolved between parentheses
|
||||||
func prettify(parameters []string, resolvedParameters []string, operator string) string {
|
func prettify(parameters []string, resolvedParameters []string, operator string) string {
|
||||||
// Since, in the event of an invalid path, the resolvedParameters also contain the condition itself,
|
// Handle pattern function truncation first
|
||||||
// we'll return the resolvedParameters as-is.
|
|
||||||
if strings.HasSuffix(resolvedParameters[0], InvalidConditionElementSuffix) || strings.HasSuffix(resolvedParameters[1], InvalidConditionElementSuffix) {
|
|
||||||
return resolvedParameters[0] + " " + operator + " " + resolvedParameters[1]
|
|
||||||
}
|
|
||||||
// If using the pattern function, truncate the parameter it's being compared to if said parameter is long enough
|
|
||||||
if strings.HasPrefix(parameters[0], PatternFunctionPrefix) && strings.HasSuffix(parameters[0], FunctionSuffix) && len(resolvedParameters[1]) > maximumLengthBeforeTruncatingWhenComparedWithPattern {
|
if strings.HasPrefix(parameters[0], PatternFunctionPrefix) && strings.HasSuffix(parameters[0], FunctionSuffix) && len(resolvedParameters[1]) > maximumLengthBeforeTruncatingWhenComparedWithPattern {
|
||||||
resolvedParameters[1] = fmt.Sprintf("%.25s...(truncated)", resolvedParameters[1])
|
resolvedParameters[1] = fmt.Sprintf("%.25s...(truncated)", resolvedParameters[1])
|
||||||
}
|
}
|
||||||
if strings.HasPrefix(parameters[1], PatternFunctionPrefix) && strings.HasSuffix(parameters[1], FunctionSuffix) && len(resolvedParameters[0]) > maximumLengthBeforeTruncatingWhenComparedWithPattern {
|
if strings.HasPrefix(parameters[1], PatternFunctionPrefix) && strings.HasSuffix(parameters[1], FunctionSuffix) && len(resolvedParameters[0]) > maximumLengthBeforeTruncatingWhenComparedWithPattern {
|
||||||
resolvedParameters[0] = fmt.Sprintf("%.25s...(truncated)", resolvedParameters[0])
|
resolvedParameters[0] = fmt.Sprintf("%.25s...(truncated)", resolvedParameters[0])
|
||||||
}
|
}
|
||||||
// First element is a placeholder
|
// Determine the state of each parameter
|
||||||
if parameters[0] != resolvedParameters[0] && parameters[1] == resolvedParameters[1] {
|
leftChanged := parameters[0] != resolvedParameters[0]
|
||||||
return parameters[0] + " (" + resolvedParameters[0] + ") " + operator + " " + parameters[1]
|
rightChanged := parameters[1] != resolvedParameters[1]
|
||||||
|
leftInvalid := resolvedParameters[0] == parameters[0]+" "+InvalidConditionElementSuffix
|
||||||
|
rightInvalid := resolvedParameters[1] == parameters[1]+" "+InvalidConditionElementSuffix
|
||||||
|
// Build the output based on what was resolved
|
||||||
|
var left, right string
|
||||||
|
// Format left side
|
||||||
|
if leftChanged && !leftInvalid {
|
||||||
|
left = parameters[0] + " (" + resolvedParameters[0] + ")"
|
||||||
|
} else if leftInvalid {
|
||||||
|
left = resolvedParameters[0] // Already has (INVALID)
|
||||||
|
} else {
|
||||||
|
left = parameters[0] // Unchanged
|
||||||
}
|
}
|
||||||
// Second element is a placeholder
|
// Format right side
|
||||||
if parameters[0] == resolvedParameters[0] && parameters[1] != resolvedParameters[1] {
|
if rightChanged && !rightInvalid {
|
||||||
return parameters[0] + " " + operator + " " + parameters[1] + " (" + resolvedParameters[1] + ")"
|
right = parameters[1] + " (" + resolvedParameters[1] + ")"
|
||||||
|
} else if rightInvalid {
|
||||||
|
right = resolvedParameters[1] // Already has (INVALID)
|
||||||
|
} else {
|
||||||
|
right = parameters[1] // Unchanged
|
||||||
}
|
}
|
||||||
// Both elements are placeholders...?
|
return left + " " + operator + " " + right
|
||||||
if parameters[0] != resolvedParameters[0] && parameters[1] != resolvedParameters[1] {
|
|
||||||
return parameters[0] + " (" + resolvedParameters[0] + ") " + operator + " " + parameters[1] + " (" + resolvedParameters[1] + ")"
|
|
||||||
}
|
|
||||||
// Neither elements are placeholders
|
|
||||||
return parameters[0] + " " + operator + " " + parameters[1]
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,8 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/TwiN/gatus/v5/config/gontext"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestCondition_Validate(t *testing.T) {
|
func TestCondition_Validate(t *testing.T) {
|
||||||
@@ -777,3 +779,77 @@ func TestCondition_evaluateWithInvalidOperator(t *testing.T) {
|
|||||||
t.Error("condition was invalid, result should've had an error")
|
t.Error("condition was invalid, result should've had an error")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestConditionEvaluateWithInvalidContextPlaceholder(t *testing.T) {
|
||||||
|
// Test case: Suite endpoint with invalid context placeholder
|
||||||
|
// This should display the original placeholder names with resolved values
|
||||||
|
condition := Condition("[STATUS] == [CONTEXT].expected_statusz")
|
||||||
|
result := &Result{HTTPStatus: 200}
|
||||||
|
ctx := gontext.New(map[string]interface{}{
|
||||||
|
// Note: expected_statusz is not in the context (typo - should be expected_status)
|
||||||
|
"expected_status": 200,
|
||||||
|
"max_response_time": 5000,
|
||||||
|
})
|
||||||
|
// Simulate suite endpoint evaluation with context
|
||||||
|
success := condition.evaluate(result, false, ctx) // false = don't skip resolution (default)
|
||||||
|
if success {
|
||||||
|
t.Error("Condition should have failed because [CONTEXT].expected_statusz doesn't exist")
|
||||||
|
}
|
||||||
|
if len(result.ConditionResults) == 0 {
|
||||||
|
t.Fatal("No condition results found")
|
||||||
|
}
|
||||||
|
actualDisplay := result.ConditionResults[0].Condition
|
||||||
|
// The expected format should preserve the placeholder names
|
||||||
|
expectedDisplay := "[STATUS] (200) == [CONTEXT].expected_statusz (INVALID)"
|
||||||
|
if actualDisplay != expectedDisplay {
|
||||||
|
t.Errorf("Incorrect condition display for failed context placeholder\nExpected: %s\nActual: %s", expectedDisplay, actualDisplay)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestConditionEvaluateWithValidContextPlaceholder(t *testing.T) {
|
||||||
|
// Test case: Suite endpoint with valid context placeholder
|
||||||
|
condition := Condition("[STATUS] == [CONTEXT].expected_status")
|
||||||
|
result := &Result{HTTPStatus: 200}
|
||||||
|
ctx := gontext.New(map[string]interface{}{
|
||||||
|
"expected_status": 200,
|
||||||
|
})
|
||||||
|
// Simulate suite endpoint evaluation with context
|
||||||
|
success := condition.evaluate(result, false, ctx)
|
||||||
|
if !success {
|
||||||
|
t.Error("Condition should have succeeded")
|
||||||
|
}
|
||||||
|
if len(result.ConditionResults) == 0 {
|
||||||
|
t.Fatal("No condition results found")
|
||||||
|
}
|
||||||
|
actualDisplay := result.ConditionResults[0].Condition
|
||||||
|
// For successful conditions, just the original condition is shown
|
||||||
|
expectedDisplay := "[STATUS] == [CONTEXT].expected_status"
|
||||||
|
if actualDisplay != expectedDisplay {
|
||||||
|
t.Errorf("Incorrect condition display for successful context placeholder\nExpected: %s\nActual: %s", expectedDisplay, actualDisplay)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestConditionEvaluateWithMixedValidAndInvalidContext(t *testing.T) {
|
||||||
|
// Test case: One valid placeholder, one invalid
|
||||||
|
// Note: For numerical comparisons, invalid placeholders that can't be parsed as numbers
|
||||||
|
// default to 0 due to sanitizeAndResolveNumericalWithContext's behavior
|
||||||
|
condition := Condition("[RESPONSE_TIME] < [CONTEXT].invalid_key")
|
||||||
|
result := &Result{Duration: 100 * 1000000} // 100ms in nanoseconds
|
||||||
|
ctx := gontext.New(map[string]interface{}{
|
||||||
|
"valid_key": 5000,
|
||||||
|
})
|
||||||
|
// Simulate suite endpoint evaluation with context
|
||||||
|
success := condition.evaluate(result, false, ctx)
|
||||||
|
if success {
|
||||||
|
t.Error("Condition should have failed because [CONTEXT].invalid_key doesn't exist")
|
||||||
|
}
|
||||||
|
if len(result.ConditionResults) == 0 {
|
||||||
|
t.Fatal("No condition results found")
|
||||||
|
}
|
||||||
|
actualDisplay := result.ConditionResults[0].Condition
|
||||||
|
// For numerical comparisons, invalid context placeholders become 0
|
||||||
|
expectedDisplay := "[RESPONSE_TIME] (100) < [CONTEXT].invalid_key (0)"
|
||||||
|
if actualDisplay != expectedDisplay {
|
||||||
|
t.Errorf("Incorrect condition display\nExpected: %s\nActual: %s", expectedDisplay, actualDisplay)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user