.examples
.github
alerting
client
config
controller
core
ui
condition.go
condition_bench_test.go
condition_result.go
condition_test.go
dns.go
dns_test.go
endpoint.go
endpoint_status.go
endpoint_status_test.go
endpoint_test.go
event.go
event_test.go
health_status.go
result.go
result_test.go
uptime.go
docs
jsonpath
metric
pattern
security
storage
util
vendor
watchdog
web
.dockerignore
.gitattributes
.gitignore
Dockerfile
LICENSE.md
Makefile
README.md
config.yaml
go.mod
go.sum
main.go
377 lines
12 KiB
Go
377 lines
12 KiB
Go
package core
|
|
|
|
import (
|
|
"io/ioutil"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/TwiN/gatus/v3/alerting/alert"
|
|
"github.com/TwiN/gatus/v3/client"
|
|
)
|
|
|
|
func TestEndpoint_IsEnabled(t *testing.T) {
|
|
if !(Endpoint{Enabled: nil}).IsEnabled() {
|
|
t.Error("endpoint.IsEnabled() should've returned true, because Enabled was set to nil")
|
|
}
|
|
if value := false; (Endpoint{Enabled: &value}).IsEnabled() {
|
|
t.Error("endpoint.IsEnabled() should've returned false, because Enabled was set to false")
|
|
}
|
|
if value := true; !(Endpoint{Enabled: &value}).IsEnabled() {
|
|
t.Error("Endpoint.IsEnabled() should've returned true, because Enabled was set to true")
|
|
}
|
|
}
|
|
|
|
func TestEndpoint_ValidateAndSetDefaults(t *testing.T) {
|
|
condition := Condition("[STATUS] == 200")
|
|
endpoint := Endpoint{
|
|
Name: "website-health",
|
|
URL: "https://twin.sh/health",
|
|
Conditions: []*Condition{&condition},
|
|
Alerts: []*alert.Alert{{Type: alert.TypePagerDuty}},
|
|
}
|
|
endpoint.ValidateAndSetDefaults()
|
|
if endpoint.ClientConfig == nil {
|
|
t.Error("client configuration should've been set to the default configuration")
|
|
} else {
|
|
if endpoint.ClientConfig.Insecure != client.GetDefaultConfig().Insecure {
|
|
t.Errorf("Default client configuration should've set Insecure to %v, got %v", client.GetDefaultConfig().Insecure, endpoint.ClientConfig.Insecure)
|
|
}
|
|
if endpoint.ClientConfig.IgnoreRedirect != client.GetDefaultConfig().IgnoreRedirect {
|
|
t.Errorf("Default client configuration should've set IgnoreRedirect to %v, got %v", client.GetDefaultConfig().IgnoreRedirect, endpoint.ClientConfig.IgnoreRedirect)
|
|
}
|
|
if endpoint.ClientConfig.Timeout != client.GetDefaultConfig().Timeout {
|
|
t.Errorf("Default client configuration should've set Timeout to %v, got %v", client.GetDefaultConfig().Timeout, endpoint.ClientConfig.Timeout)
|
|
}
|
|
}
|
|
if endpoint.Method != "GET" {
|
|
t.Error("Endpoint method should've defaulted to GET")
|
|
}
|
|
if endpoint.Interval != time.Minute {
|
|
t.Error("Endpoint interval should've defaulted to 1 minute")
|
|
}
|
|
if endpoint.Headers == nil {
|
|
t.Error("Endpoint headers should've defaulted to an empty map")
|
|
}
|
|
if len(endpoint.Alerts) != 1 {
|
|
t.Error("Endpoint should've had 1 alert")
|
|
}
|
|
if endpoint.Alerts[0].IsEnabled() {
|
|
t.Error("Endpoint alert should've defaulted to disabled")
|
|
}
|
|
if endpoint.Alerts[0].SuccessThreshold != 2 {
|
|
t.Error("Endpoint alert should've defaulted to a success threshold of 2")
|
|
}
|
|
if endpoint.Alerts[0].FailureThreshold != 3 {
|
|
t.Error("Endpoint alert should've defaulted to a failure threshold of 3")
|
|
}
|
|
}
|
|
|
|
func TestEndpoint_ValidateAndSetDefaultsWithClientConfig(t *testing.T) {
|
|
condition := Condition("[STATUS] == 200")
|
|
endpoint := Endpoint{
|
|
Name: "website-health",
|
|
URL: "https://twin.sh/health",
|
|
Conditions: []*Condition{&condition},
|
|
ClientConfig: &client.Config{
|
|
Insecure: true,
|
|
IgnoreRedirect: true,
|
|
Timeout: 0,
|
|
},
|
|
}
|
|
endpoint.ValidateAndSetDefaults()
|
|
if endpoint.ClientConfig == nil {
|
|
t.Error("client configuration should've been set to the default configuration")
|
|
} else {
|
|
if !endpoint.ClientConfig.Insecure {
|
|
t.Error("endpoint.ClientConfig.Insecure should've been set to true")
|
|
}
|
|
if !endpoint.ClientConfig.IgnoreRedirect {
|
|
t.Error("endpoint.ClientConfig.IgnoreRedirect should've been set to true")
|
|
}
|
|
if endpoint.ClientConfig.Timeout != client.GetDefaultConfig().Timeout {
|
|
t.Error("endpoint.ClientConfig.Timeout should've been set to 10s, because the timeout value entered is not set or invalid")
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestEndpoint_ValidateAndSetDefaultsWithNoName(t *testing.T) {
|
|
defer func() { recover() }()
|
|
condition := Condition("[STATUS] == 200")
|
|
endpoint := &Endpoint{
|
|
Name: "",
|
|
URL: "http://example.com",
|
|
Conditions: []*Condition{&condition},
|
|
}
|
|
err := endpoint.ValidateAndSetDefaults()
|
|
if err == nil {
|
|
t.Fatal("Should've returned an error because endpoint didn't have a name, which is a mandatory field")
|
|
}
|
|
}
|
|
|
|
func TestEndpoint_ValidateAndSetDefaultsWithNoUrl(t *testing.T) {
|
|
defer func() { recover() }()
|
|
condition := Condition("[STATUS] == 200")
|
|
endpoint := &Endpoint{
|
|
Name: "example",
|
|
URL: "",
|
|
Conditions: []*Condition{&condition},
|
|
}
|
|
err := endpoint.ValidateAndSetDefaults()
|
|
if err == nil {
|
|
t.Fatal("Should've returned an error because endpoint didn't have an url, which is a mandatory field")
|
|
}
|
|
}
|
|
|
|
func TestEndpoint_ValidateAndSetDefaultsWithNoConditions(t *testing.T) {
|
|
defer func() { recover() }()
|
|
endpoint := &Endpoint{
|
|
Name: "example",
|
|
URL: "http://example.com",
|
|
Conditions: nil,
|
|
}
|
|
err := endpoint.ValidateAndSetDefaults()
|
|
if err == nil {
|
|
t.Fatal("Should've returned an error because endpoint didn't have at least 1 condition")
|
|
}
|
|
}
|
|
|
|
func TestEndpoint_ValidateAndSetDefaultsWithDNS(t *testing.T) {
|
|
conditionSuccess := Condition("[DNS_RCODE] == NOERROR")
|
|
endpoint := &Endpoint{
|
|
Name: "dns-test",
|
|
URL: "http://example.com",
|
|
DNS: &DNS{
|
|
QueryType: "A",
|
|
QueryName: "example.com",
|
|
},
|
|
Conditions: []*Condition{&conditionSuccess},
|
|
}
|
|
err := endpoint.ValidateAndSetDefaults()
|
|
if err != nil {
|
|
|
|
}
|
|
if endpoint.DNS.QueryName != "example.com." {
|
|
t.Error("Endpoint.dns.query-name should be formatted with . suffix")
|
|
}
|
|
}
|
|
|
|
func TestEndpoint_buildHTTPRequest(t *testing.T) {
|
|
condition := Condition("[STATUS] == 200")
|
|
endpoint := Endpoint{
|
|
Name: "website-health",
|
|
URL: "https://twin.sh/health",
|
|
Conditions: []*Condition{&condition},
|
|
}
|
|
endpoint.ValidateAndSetDefaults()
|
|
request := endpoint.buildHTTPRequest()
|
|
if request.Method != "GET" {
|
|
t.Error("request.Method should've been GET, but was", request.Method)
|
|
}
|
|
if request.Host != "twin.sh" {
|
|
t.Error("request.Host should've been twin.sh, but was", request.Host)
|
|
}
|
|
if userAgent := request.Header.Get("User-Agent"); userAgent != GatusUserAgent {
|
|
t.Errorf("request.Header.Get(User-Agent) should've been %s, but was %s", GatusUserAgent, userAgent)
|
|
}
|
|
}
|
|
|
|
func TestEndpoint_buildHTTPRequestWithCustomUserAgent(t *testing.T) {
|
|
condition := Condition("[STATUS] == 200")
|
|
endpoint := Endpoint{
|
|
Name: "website-health",
|
|
URL: "https://twin.sh/health",
|
|
Conditions: []*Condition{&condition},
|
|
Headers: map[string]string{
|
|
"User-Agent": "Test/2.0",
|
|
},
|
|
}
|
|
endpoint.ValidateAndSetDefaults()
|
|
request := endpoint.buildHTTPRequest()
|
|
if request.Method != "GET" {
|
|
t.Error("request.Method should've been GET, but was", request.Method)
|
|
}
|
|
if request.Host != "twin.sh" {
|
|
t.Error("request.Host should've been twin.sh, but was", request.Host)
|
|
}
|
|
if userAgent := request.Header.Get("User-Agent"); userAgent != "Test/2.0" {
|
|
t.Errorf("request.Header.Get(User-Agent) should've been %s, but was %s", "Test/2.0", userAgent)
|
|
}
|
|
}
|
|
|
|
func TestEndpoint_buildHTTPRequestWithHostHeader(t *testing.T) {
|
|
condition := Condition("[STATUS] == 200")
|
|
endpoint := Endpoint{
|
|
Name: "website-health",
|
|
URL: "https://twin.sh/health",
|
|
Method: "POST",
|
|
Conditions: []*Condition{&condition},
|
|
Headers: map[string]string{
|
|
"Host": "example.com",
|
|
},
|
|
}
|
|
endpoint.ValidateAndSetDefaults()
|
|
request := endpoint.buildHTTPRequest()
|
|
if request.Method != "POST" {
|
|
t.Error("request.Method should've been POST, but was", request.Method)
|
|
}
|
|
if request.Host != "example.com" {
|
|
t.Error("request.Host should've been example.com, but was", request.Host)
|
|
}
|
|
}
|
|
|
|
func TestEndpoint_buildHTTPRequestWithGraphQLEnabled(t *testing.T) {
|
|
condition := Condition("[STATUS] == 200")
|
|
endpoint := Endpoint{
|
|
Name: "website-graphql",
|
|
URL: "https://twin.sh/graphql",
|
|
Method: "POST",
|
|
Conditions: []*Condition{&condition},
|
|
GraphQL: true,
|
|
Body: `{
|
|
users(gender: "female") {
|
|
id
|
|
name
|
|
gender
|
|
avatar
|
|
}
|
|
}`,
|
|
}
|
|
endpoint.ValidateAndSetDefaults()
|
|
request := endpoint.buildHTTPRequest()
|
|
if request.Method != "POST" {
|
|
t.Error("request.Method should've been POST, but was", request.Method)
|
|
}
|
|
if contentType := request.Header.Get(ContentTypeHeader); contentType != "application/json" {
|
|
t.Error("request.Header.Content-Type should've been application/json, but was", contentType)
|
|
}
|
|
body, _ := ioutil.ReadAll(request.Body)
|
|
if !strings.HasPrefix(string(body), "{\"query\":") {
|
|
t.Error("request.body should've started with '{\"query\":', but it didn't:", string(body))
|
|
}
|
|
}
|
|
|
|
func TestIntegrationEvaluateHealth(t *testing.T) {
|
|
condition := Condition("[STATUS] == 200")
|
|
bodyCondition := Condition("[BODY].status == UP")
|
|
endpoint := Endpoint{
|
|
Name: "website-health",
|
|
URL: "https://twin.sh/health",
|
|
Conditions: []*Condition{&condition, &bodyCondition},
|
|
}
|
|
endpoint.ValidateAndSetDefaults()
|
|
result := endpoint.EvaluateHealth()
|
|
if !result.ConditionResults[0].Success {
|
|
t.Errorf("Condition '%s' should have been a success", condition)
|
|
}
|
|
if !result.Connected {
|
|
t.Error("Because the connection has been established, result.Connected should've been true")
|
|
}
|
|
if !result.Success {
|
|
t.Error("Because all conditions passed, this should have been a success")
|
|
}
|
|
}
|
|
|
|
func TestIntegrationEvaluateHealthWithFailure(t *testing.T) {
|
|
condition := Condition("[STATUS] == 500")
|
|
endpoint := Endpoint{
|
|
Name: "website-health",
|
|
URL: "https://twin.sh/health",
|
|
Conditions: []*Condition{&condition},
|
|
}
|
|
endpoint.ValidateAndSetDefaults()
|
|
result := endpoint.EvaluateHealth()
|
|
if result.ConditionResults[0].Success {
|
|
t.Errorf("Condition '%s' should have been a failure", condition)
|
|
}
|
|
if !result.Connected {
|
|
t.Error("Because the connection has been established, result.Connected should've been true")
|
|
}
|
|
if result.Success {
|
|
t.Error("Because one of the conditions failed, success should have been false")
|
|
}
|
|
}
|
|
|
|
func TestIntegrationEvaluateHealthForDNS(t *testing.T) {
|
|
conditionSuccess := Condition("[DNS_RCODE] == NOERROR")
|
|
conditionBody := Condition("[BODY] == 93.184.216.34")
|
|
endpoint := Endpoint{
|
|
Name: "example",
|
|
URL: "8.8.8.8",
|
|
DNS: &DNS{
|
|
QueryType: "A",
|
|
QueryName: "example.com.",
|
|
},
|
|
Conditions: []*Condition{&conditionSuccess, &conditionBody},
|
|
}
|
|
endpoint.ValidateAndSetDefaults()
|
|
result := endpoint.EvaluateHealth()
|
|
if !result.ConditionResults[0].Success {
|
|
t.Errorf("Conditions '%s' and %s should have been a success", conditionSuccess, conditionBody)
|
|
}
|
|
if !result.Connected {
|
|
t.Error("Because the connection has been established, result.Connected should've been true")
|
|
}
|
|
if !result.Success {
|
|
t.Error("Because all conditions passed, this should have been a success")
|
|
}
|
|
}
|
|
|
|
func TestIntegrationEvaluateHealthForICMP(t *testing.T) {
|
|
conditionSuccess := Condition("[CONNECTED] == true")
|
|
endpoint := Endpoint{
|
|
Name: "icmp-test",
|
|
URL: "icmp://127.0.0.1",
|
|
Conditions: []*Condition{&conditionSuccess},
|
|
}
|
|
endpoint.ValidateAndSetDefaults()
|
|
result := endpoint.EvaluateHealth()
|
|
if !result.ConditionResults[0].Success {
|
|
t.Errorf("Conditions '%s' should have been a success", conditionSuccess)
|
|
}
|
|
if !result.Connected {
|
|
t.Error("Because the connection has been established, result.Connected should've been true")
|
|
}
|
|
if !result.Success {
|
|
t.Error("Because all conditions passed, this should have been a success")
|
|
}
|
|
}
|
|
|
|
func TestEndpoint_getIP(t *testing.T) {
|
|
conditionSuccess := Condition("[CONNECTED] == true")
|
|
endpoint := Endpoint{
|
|
Name: "invalid-url-test",
|
|
URL: "",
|
|
Conditions: []*Condition{&conditionSuccess},
|
|
}
|
|
result := &Result{}
|
|
endpoint.getIP(result)
|
|
if len(result.Errors) == 0 {
|
|
t.Error("endpoint.getIP(result) should've thrown an error because the URL is invalid, thus cannot be parsed")
|
|
}
|
|
}
|
|
|
|
func TestEndpoint_NeedsToReadBody(t *testing.T) {
|
|
statusCondition := Condition("[STATUS] == 200")
|
|
bodyCondition := Condition("[BODY].status == UP")
|
|
bodyConditionWithLength := Condition("len([BODY].tags) > 0")
|
|
if (&Endpoint{Conditions: []*Condition{&statusCondition}}).needsToReadBody() {
|
|
t.Error("expected false, got true")
|
|
}
|
|
if !(&Endpoint{Conditions: []*Condition{&bodyCondition}}).needsToReadBody() {
|
|
t.Error("expected true, got false")
|
|
}
|
|
if !(&Endpoint{Conditions: []*Condition{&bodyConditionWithLength}}).needsToReadBody() {
|
|
t.Error("expected true, got false")
|
|
}
|
|
if !(&Endpoint{Conditions: []*Condition{&statusCondition, &bodyCondition}}).needsToReadBody() {
|
|
t.Error("expected true, got false")
|
|
}
|
|
if !(&Endpoint{Conditions: []*Condition{&bodyCondition, &statusCondition}}).needsToReadBody() {
|
|
t.Error("expected true, got false")
|
|
}
|
|
if !(&Endpoint{Conditions: []*Condition{&bodyConditionWithLength, &statusCondition}}).needsToReadBody() {
|
|
t.Error("expected true, got false")
|
|
}
|
|
}
|