mirror of
https://github.com/TwiN/gatus.git
synced 2024-12-22 14:41:01 +01:00
59842d5e88
* feat(alerting): add support for including endpoint errors in custom alerts - Updated `buildHTTPRequest` method in `AlertProvider` to accept a `result` parameter. - Added support for including `[ENDPOINT_ERRORS]` in both the request body and URL, which will be replaced by the errors from `Result.Errors[]`. - Adjusted `CreateExternalEndpointResult` to capture and store errors from query parameters. - This allows custom alerts to include detailed error information, enhancing the flexibility of alert notifications. * feat: add ENDPOINT_ERRORS example * feat: add tests * Refactor: code review feedback * delete unsed errors * Update README.md * Apply suggestions from code review --------- Co-authored-by: raojinlin <raojinlin302@gmail.com> Co-authored-by: TwiN <twin@linux.com>
162 lines
5.6 KiB
Go
162 lines
5.6 KiB
Go
package api
|
|
|
|
import (
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"testing"
|
|
|
|
"github.com/TwiN/gatus/v5/alerting"
|
|
"github.com/TwiN/gatus/v5/alerting/alert"
|
|
"github.com/TwiN/gatus/v5/alerting/provider/discord"
|
|
"github.com/TwiN/gatus/v5/config"
|
|
"github.com/TwiN/gatus/v5/config/endpoint"
|
|
"github.com/TwiN/gatus/v5/config/maintenance"
|
|
"github.com/TwiN/gatus/v5/storage/store"
|
|
"github.com/TwiN/gatus/v5/storage/store/common/paging"
|
|
)
|
|
|
|
func TestCreateExternalEndpointResult(t *testing.T) {
|
|
defer store.Get().Clear()
|
|
defer cache.Clear()
|
|
cfg := &config.Config{
|
|
Alerting: &alerting.Config{
|
|
Discord: &discord.AlertProvider{},
|
|
},
|
|
ExternalEndpoints: []*endpoint.ExternalEndpoint{
|
|
{
|
|
Name: "n",
|
|
Group: "g",
|
|
Token: "token",
|
|
Alerts: []*alert.Alert{
|
|
{
|
|
Type: alert.TypeDiscord,
|
|
FailureThreshold: 2,
|
|
SuccessThreshold: 2,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
Maintenance: &maintenance.Config{},
|
|
}
|
|
api := New(cfg)
|
|
router := api.Router()
|
|
scenarios := []struct {
|
|
Name string
|
|
Path string
|
|
AuthorizationHeaderBearerToken string
|
|
ExpectedCode int
|
|
}{
|
|
{
|
|
Name: "no-token",
|
|
Path: "/api/v1/endpoints/g_n/external?success=true",
|
|
AuthorizationHeaderBearerToken: "",
|
|
ExpectedCode: 401,
|
|
},
|
|
{
|
|
Name: "bad-token",
|
|
Path: "/api/v1/endpoints/g_n/external?success=true",
|
|
AuthorizationHeaderBearerToken: "Bearer bad-token",
|
|
ExpectedCode: 401,
|
|
},
|
|
{
|
|
Name: "bad-key",
|
|
Path: "/api/v1/endpoints/bad_key/external?success=true",
|
|
AuthorizationHeaderBearerToken: "Bearer token",
|
|
ExpectedCode: 404,
|
|
},
|
|
{
|
|
Name: "bad-success-value",
|
|
Path: "/api/v1/endpoints/g_n/external?success=invalid",
|
|
AuthorizationHeaderBearerToken: "Bearer token",
|
|
ExpectedCode: 400,
|
|
},
|
|
{
|
|
Name: "good-token-success-true",
|
|
Path: "/api/v1/endpoints/g_n/external?success=true",
|
|
AuthorizationHeaderBearerToken: "Bearer token",
|
|
ExpectedCode: 200,
|
|
},
|
|
{
|
|
Name: "good-token-success-true-with-ignored-error-because-success-true",
|
|
Path: "/api/v1/endpoints/g_n/external?success=true&error=failed",
|
|
AuthorizationHeaderBearerToken: "Bearer token",
|
|
ExpectedCode: 200,
|
|
},
|
|
{
|
|
Name: "good-token-success-false",
|
|
Path: "/api/v1/endpoints/g_n/external?success=false",
|
|
AuthorizationHeaderBearerToken: "Bearer token",
|
|
ExpectedCode: 200,
|
|
},
|
|
{
|
|
Name: "good-token-success-false-again",
|
|
Path: "/api/v1/endpoints/g_n/external?success=false",
|
|
AuthorizationHeaderBearerToken: "Bearer token",
|
|
ExpectedCode: 200,
|
|
},
|
|
{
|
|
Name: "good-token-success-false-with-error",
|
|
Path: "/api/v1/endpoints/g_n/external?success=false&error=failed",
|
|
AuthorizationHeaderBearerToken: "Bearer token",
|
|
ExpectedCode: 200,
|
|
},
|
|
}
|
|
for _, scenario := range scenarios {
|
|
t.Run(scenario.Name, func(t *testing.T) {
|
|
request := httptest.NewRequest("POST", scenario.Path, http.NoBody)
|
|
if len(scenario.AuthorizationHeaderBearerToken) > 0 {
|
|
request.Header.Set("Authorization", scenario.AuthorizationHeaderBearerToken)
|
|
}
|
|
response, err := router.Test(request)
|
|
if err != nil {
|
|
return
|
|
}
|
|
defer response.Body.Close()
|
|
if response.StatusCode != scenario.ExpectedCode {
|
|
t.Errorf("%s %s should have returned %d, but returned %d instead", request.Method, request.URL, scenario.ExpectedCode, response.StatusCode)
|
|
}
|
|
})
|
|
}
|
|
t.Run("verify-end-results", func(t *testing.T) {
|
|
endpointStatus, err := store.Get().GetEndpointStatus("g", "n", paging.NewEndpointStatusParams().WithResults(1, 10))
|
|
if err != nil {
|
|
t.Errorf("failed to get endpoint status: %s", err.Error())
|
|
return
|
|
}
|
|
if endpointStatus.Key != "g_n" {
|
|
t.Errorf("expected key to be g_n but got %s", endpointStatus.Key)
|
|
}
|
|
if len(endpointStatus.Results) != 5 {
|
|
t.Errorf("expected 3 results but got %d", len(endpointStatus.Results))
|
|
}
|
|
if !endpointStatus.Results[0].Success {
|
|
t.Errorf("expected first result to be successful")
|
|
}
|
|
if !endpointStatus.Results[1].Success {
|
|
t.Errorf("expected second result to be successful")
|
|
}
|
|
if len(endpointStatus.Results[1].Errors) > 0 {
|
|
t.Errorf("expected second result to have no errors")
|
|
}
|
|
if endpointStatus.Results[2].Success {
|
|
t.Errorf("expected third result to be unsuccessful")
|
|
}
|
|
if endpointStatus.Results[3].Success {
|
|
t.Errorf("expected fourth result to be unsuccessful")
|
|
}
|
|
if endpointStatus.Results[4].Success {
|
|
t.Errorf("expected fifth result to be unsuccessful")
|
|
}
|
|
if len(endpointStatus.Results[4].Errors) == 0 || endpointStatus.Results[4].Errors[0] != "failed" {
|
|
t.Errorf("expected fifth result to have errors: failed")
|
|
}
|
|
externalEndpointFromConfig := cfg.GetExternalEndpointByKey("g_n")
|
|
if externalEndpointFromConfig.NumberOfFailuresInARow != 3 {
|
|
t.Errorf("expected 3 failures in a row but got %d", externalEndpointFromConfig.NumberOfFailuresInARow)
|
|
}
|
|
if externalEndpointFromConfig.NumberOfSuccessesInARow != 0 {
|
|
t.Errorf("expected 0 successes in a row but got %d", externalEndpointFromConfig.NumberOfSuccessesInARow)
|
|
}
|
|
})
|
|
}
|