2024-04-08 21:00:40 -04:00
package api
import (
"net/http"
"net/http/httptest"
"testing"
2026-09-30 19:02:30 +05:30
"time"
2024-04-08 21:00:40 -04:00
"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"
2024-05-09 22:56:16 -04:00
"github.com/TwiN/gatus/v5/config/endpoint"
2024-04-08 21:00:40 -04:00
"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 {},
},
2024-05-09 22:56:16 -04:00
ExternalEndpoints : [] * endpoint . ExternalEndpoint {
2024-04-08 21:00:40 -04:00
{
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 ,
},
2024-05-15 21:29:45 -04:00
{
Name : "bad-success-value" ,
Path : "/api/v1/endpoints/g_n/external?success=invalid" ,
AuthorizationHeaderBearerToken : "Bearer token" ,
ExpectedCode : 400 ,
},
2025-07-08 10:53:57 -05:00
{
Name : "bad-duration-value" ,
Path : "/api/v1/endpoints/g_n/external?success=true&duration=invalid" ,
AuthorizationHeaderBearerToken : "Bearer token" ,
ExpectedCode : 400 ,
},
2024-04-08 21:00:40 -04:00
{
Name : "good-token-success-true" ,
Path : "/api/v1/endpoints/g_n/external?success=true" ,
AuthorizationHeaderBearerToken : "Bearer token" ,
ExpectedCode : 200 ,
},
2024-09-18 10:26:21 +08:00
{
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 ,
},
2025-07-08 10:53:57 -05:00
{
Name : "good-duration-success-true" ,
Path : "/api/v1/endpoints/g_n/external?success=true&duration=10s" ,
AuthorizationHeaderBearerToken : "Bearer token" ,
ExpectedCode : 200 ,
},
2024-04-08 21:00:40 -04:00
{
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 ,
},
2024-09-18 10:26:21 +08:00
{
Name : "good-token-success-false-with-error" ,
Path : "/api/v1/endpoints/g_n/external?success=false&error=failed" ,
AuthorizationHeaderBearerToken : "Bearer token" ,
ExpectedCode : 200 ,
},
2024-04-08 21:00:40 -04:00
}
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 ) {
2025-07-08 10:53:57 -05:00
endpointStatus , err := store . Get (). GetEndpointStatus ( "g" , "n" , paging . NewEndpointStatusParams (). WithResults ( 1 , 11 ))
2024-04-08 21:00:40 -04:00
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 )
}
2025-07-08 10:53:57 -05:00
if len ( endpointStatus . Results ) != 6 {
t . Errorf ( "expected 6 results but got %d" , len ( endpointStatus . Results ))
2024-04-08 21:00:40 -04:00
}
if ! endpointStatus . Results [ 0 ]. Success {
t . Errorf ( "expected first result to be successful" )
}
2024-09-18 10:26:21 +08:00
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" )
2024-04-08 21:00:40 -04:00
}
2025-07-08 10:53:57 -05:00
if endpointStatus . Results [ 2 ]. Duration == 0 || endpointStatus . Results [ 2 ]. Duration . Seconds () != 10 {
t . Errorf ( "expected third result to have a duration of 10 seconds" )
2024-04-08 21:00:40 -04:00
}
2024-09-18 10:26:21 +08:00
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" )
}
2025-07-08 10:53:57 -05:00
if endpointStatus . Results [ 5 ]. Success {
t . Errorf ( "expected sixth result to be unsuccessful" )
}
if len ( endpointStatus . Results [ 5 ]. Errors ) == 0 || endpointStatus . Results [ 5 ]. Errors [ 0 ] != "failed" {
t . Errorf ( "expected sixth result to have errors: failed" )
2024-09-18 10:26:21 +08:00
}
2024-04-08 21:00:40 -04:00
externalEndpointFromConfig := cfg . GetExternalEndpointByKey ( "g_n" )
2024-09-18 10:26:21 +08:00
if externalEndpointFromConfig . NumberOfFailuresInARow != 3 {
t . Errorf ( "expected 3 failures in a row but got %d" , externalEndpointFromConfig . NumberOfFailuresInARow )
2024-04-08 21:00:40 -04:00
}
if externalEndpointFromConfig . NumberOfSuccessesInARow != 0 {
t . Errorf ( "expected 0 successes in a row but got %d" , externalEndpointFromConfig . NumberOfSuccessesInARow )
}
})
}
2026-09-30 19:02:30 +05:30
func TestCreateExternalEndpointResultUnderEndpointMaintenanceWindow ( t * testing . T ) {
defer store . Get (). Clear ()
defer cache . Clear ()
location , err := time . LoadLocation ( "Europe/Berlin" )
if err != nil {
t . Fatal ( err )
}
externalEndpoint := & endpoint . ExternalEndpoint {
Name : "n" ,
Group : "g" ,
Token : "token" ,
Alerts : [] * alert . Alert {
{
Type : alert . TypeDiscord ,
FailureThreshold : 2 ,
SuccessThreshold : 2 ,
},
},
MaintenanceWindows : [] * maintenance . Config {{
Start : time . Now (). In ( location ). Add ( - time . Minute ). Format ( "15:04" ),
Duration : time . Hour ,
Timezone : "Europe/Berlin" ,
}},
}
if err := externalEndpoint . ValidateAndSetDefaults (); err != nil {
t . Fatal ( "did not expect an error, got" , err )
}
cfg := & config . Config {
Alerting : & alerting . Config {
Discord : & discord . AlertProvider {},
},
ExternalEndpoints : [] * endpoint . ExternalEndpoint { externalEndpoint },
Maintenance : & maintenance . Config {},
}
router := New ( cfg ). Router ()
request := httptest . NewRequest ( "POST" , "/api/v1/endpoints/g_n/external?success=false" , http . NoBody )
request . Header . Set ( "Authorization" , "Bearer token" )
response , err := router . Test ( request )
if err != nil {
t . Fatal ( err )
}
defer response . Body . Close ()
if response . StatusCode != 200 {
t . Fatalf ( "expected 200, got %d" , response . StatusCode )
}
if externalEndpoint . NumberOfFailuresInARow != 0 {
t . Errorf ( "expected alerting to be skipped because the endpoint is under maintenance, but NumberOfFailuresInARow is %d" , externalEndpoint . NumberOfFailuresInARow )
}
}