* feat(suite): Implement Suites Fixes #1230 * Update docs * Fix variable alignment * Prevent always-run endpoint from running if a context placeholder fails to resolve in the URL * Return errors when a context placeholder path fails to resolve * Add a couple of unit tests * Add a couple of unit tests * fix(ui): Update group count properly Fixes #1233 * refactor: Pass down entire config instead of several sub-configs * fix: Change default suite interval and timeout * fix: Deprecate disable-monitoring-lock in favor of concurrency * fix: Make sure there are no duplicate keys * Refactor some code * Update watchdog/watchdog.go * Update web/app/src/components/StepDetailsModal.vue Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * chore: Remove useless log * fix: Set default concurrency to 3 instead of 5 --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
126 lines
3.8 KiB
Go
126 lines
3.8 KiB
Go
package endpoint
|
|
|
|
import (
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/TwiN/gatus/v5/config/gontext"
|
|
)
|
|
|
|
func TestResolvePlaceholder(t *testing.T) {
|
|
result := &Result{
|
|
HTTPStatus: 200,
|
|
IP: "127.0.0.1",
|
|
Duration: 250 * time.Millisecond,
|
|
DNSRCode: "NOERROR",
|
|
Connected: true,
|
|
CertificateExpiration: 30 * 24 * time.Hour,
|
|
DomainExpiration: 365 * 24 * time.Hour,
|
|
Body: []byte(`{"status":"success","items":[1,2,3],"user":{"name":"john","id":123}}`),
|
|
}
|
|
|
|
ctx := gontext.New(map[string]interface{}{
|
|
"user_id": "abc123",
|
|
"session_token": "xyz789",
|
|
"array_data": []interface{}{"a", "b", "c"},
|
|
"nested": map[string]interface{}{
|
|
"value": "test",
|
|
},
|
|
})
|
|
|
|
tests := []struct {
|
|
name string
|
|
placeholder string
|
|
expected string
|
|
}{
|
|
// Basic placeholders
|
|
{"status", "[STATUS]", "200"},
|
|
{"ip", "[IP]", "127.0.0.1"},
|
|
{"response-time", "[RESPONSE_TIME]", "250"},
|
|
{"dns-rcode", "[DNS_RCODE]", "NOERROR"},
|
|
{"connected", "[CONNECTED]", "true"},
|
|
{"certificate-expiration", "[CERTIFICATE_EXPIRATION]", "2592000000"},
|
|
{"domain-expiration", "[DOMAIN_EXPIRATION]", "31536000000"},
|
|
{"body", "[BODY]", `{"status":"success","items":[1,2,3],"user":{"name":"john","id":123}}`},
|
|
|
|
// Case insensitive placeholders
|
|
{"status-lowercase", "[status]", "200"},
|
|
{"ip-mixed-case", "[Ip]", "127.0.0.1"},
|
|
|
|
// Function wrappers on basic placeholders
|
|
{"len-status", "len([STATUS])", "3"},
|
|
{"len-ip", "len([IP])", "9"},
|
|
{"has-status", "has([STATUS])", "true"},
|
|
{"has-empty", "has()", "false"},
|
|
|
|
// JSONPath expressions
|
|
{"body-status", "[BODY].status", "success"},
|
|
{"body-user-name", "[BODY].user.name", "john"},
|
|
{"body-user-id", "[BODY].user.id", "123"},
|
|
{"len-body-items", "len([BODY].items)", "3"},
|
|
{"body-array-index", "[BODY].items[0]", "1"},
|
|
{"has-body-status", "has([BODY].status)", "true"},
|
|
{"has-body-missing", "has([BODY].missing)", "false"},
|
|
|
|
// Context placeholders
|
|
{"context-user-id", "[CONTEXT].user_id", "abc123"},
|
|
{"context-session-token", "[CONTEXT].session_token", "xyz789"},
|
|
{"context-nested", "[CONTEXT].nested.value", "test"},
|
|
{"len-context-array", "len([CONTEXT].array_data)", "3"},
|
|
{"has-context-user-id", "has([CONTEXT].user_id)", "true"},
|
|
{"has-context-missing", "has([CONTEXT].missing)", "false"},
|
|
|
|
// Invalid placeholders
|
|
{"unknown-placeholder", "[UNKNOWN]", "[UNKNOWN]"},
|
|
{"len-unknown", "len([UNKNOWN])", "len([UNKNOWN]) (INVALID)"},
|
|
{"has-unknown", "has([UNKNOWN])", "false"},
|
|
{"invalid-jsonpath", "[BODY].invalid.path", "[BODY].invalid.path (INVALID)"},
|
|
|
|
// Literal strings
|
|
{"literal-string", "literal", "literal"},
|
|
{"number-string", "123", "123"},
|
|
}
|
|
|
|
for _, test := range tests {
|
|
t.Run(test.name, func(t *testing.T) {
|
|
actual, err := ResolvePlaceholder(test.placeholder, result, ctx)
|
|
if err != nil {
|
|
t.Errorf("unexpected error: %v", err)
|
|
}
|
|
if actual != test.expected {
|
|
t.Errorf("expected '%s', got '%s'", test.expected, actual)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestResolvePlaceholderWithoutContext(t *testing.T) {
|
|
result := &Result{
|
|
HTTPStatus: 404,
|
|
Body: []byte(`{"error":"not found"}`),
|
|
}
|
|
|
|
tests := []struct {
|
|
name string
|
|
placeholder string
|
|
expected string
|
|
}{
|
|
{"status-without-context", "[STATUS]", "404"},
|
|
{"body-without-context", "[BODY].error", "not found"},
|
|
{"context-without-context", "[CONTEXT].user_id", "[CONTEXT].user_id"},
|
|
{"has-context-without-context", "has([CONTEXT].user_id)", "false"},
|
|
}
|
|
|
|
for _, test := range tests {
|
|
t.Run(test.name, func(t *testing.T) {
|
|
actual, err := ResolvePlaceholder(test.placeholder, result, nil)
|
|
if err != nil {
|
|
t.Errorf("unexpected error: %v", err)
|
|
}
|
|
if actual != test.expected {
|
|
t.Errorf("expected '%s', got '%s'", test.expected, actual)
|
|
}
|
|
})
|
|
}
|
|
}
|