2019-09-06 00:02:00 -04:00
package config
2019-09-08 21:07:08 -04:00
import (
2023-01-08 17:53:37 -05:00
"errors"
2020-10-14 19:22:58 -04:00
"fmt"
2023-01-08 17:53:37 -05:00
"os"
"path/filepath"
2026-02-09 01:08:59 +01:00
"slices"
2019-09-08 21:07:08 -04:00
"testing"
"time"
2020-11-11 18:05:18 -05:00
2022-12-06 01:41:09 -05:00
"github.com/TwiN/gatus/v5/alerting"
"github.com/TwiN/gatus/v5/alerting/alert"
"github.com/TwiN/gatus/v5/alerting/provider"
2024-12-16 20:32:13 -05:00
"github.com/TwiN/gatus/v5/alerting/provider/awsses"
2025-12-27 06:09:28 +05:30
"github.com/TwiN/gatus/v5/alerting/provider/clickup"
2022-12-06 01:41:09 -05:00
"github.com/TwiN/gatus/v5/alerting/provider/custom"
feat(alerting): Add new providers for Datadog, IFTTT, Line, NewRelic, Plivo, RocketChat, SendGrid, Signal, SIGNL4, Splunk, Squadcast, Vonage, Webex and Zapier ( #1224 )
2025-08-25 13:22:17 -04:00
"github.com/TwiN/gatus/v5/alerting/provider/datadog"
2022-12-06 01:41:09 -05:00
"github.com/TwiN/gatus/v5/alerting/provider/discord"
"github.com/TwiN/gatus/v5/alerting/provider/email"
2024-12-16 20:32:13 -05:00
"github.com/TwiN/gatus/v5/alerting/provider/gitea"
2023-01-08 17:53:37 -05:00
"github.com/TwiN/gatus/v5/alerting/provider/github"
2024-12-16 20:32:13 -05:00
"github.com/TwiN/gatus/v5/alerting/provider/gitlab"
2022-12-06 01:41:09 -05:00
"github.com/TwiN/gatus/v5/alerting/provider/googlechat"
2024-05-15 21:29:45 -04:00
"github.com/TwiN/gatus/v5/alerting/provider/gotify"
feat(alerting): Add new providers for Datadog, IFTTT, Line, NewRelic, Plivo, RocketChat, SendGrid, Signal, SIGNL4, Splunk, Squadcast, Vonage, Webex and Zapier ( #1224 )
2025-08-25 13:22:17 -04:00
"github.com/TwiN/gatus/v5/alerting/provider/homeassistant"
"github.com/TwiN/gatus/v5/alerting/provider/ifttt"
"github.com/TwiN/gatus/v5/alerting/provider/ilert"
"github.com/TwiN/gatus/v5/alerting/provider/incidentio"
"github.com/TwiN/gatus/v5/alerting/provider/line"
2022-12-06 01:41:09 -05:00
"github.com/TwiN/gatus/v5/alerting/provider/matrix"
"github.com/TwiN/gatus/v5/alerting/provider/mattermost"
"github.com/TwiN/gatus/v5/alerting/provider/messagebird"
feat(alerting): Add new providers for Datadog, IFTTT, Line, NewRelic, Plivo, RocketChat, SendGrid, Signal, SIGNL4, Splunk, Squadcast, Vonage, Webex and Zapier ( #1224 )
2025-08-25 13:22:17 -04:00
"github.com/TwiN/gatus/v5/alerting/provider/newrelic"
2022-12-06 01:41:09 -05:00
"github.com/TwiN/gatus/v5/alerting/provider/ntfy"
"github.com/TwiN/gatus/v5/alerting/provider/opsgenie"
"github.com/TwiN/gatus/v5/alerting/provider/pagerduty"
feat(alerting): Add new providers for Datadog, IFTTT, Line, NewRelic, Plivo, RocketChat, SendGrid, Signal, SIGNL4, Splunk, Squadcast, Vonage, Webex and Zapier ( #1224 )
2025-08-25 13:22:17 -04:00
"github.com/TwiN/gatus/v5/alerting/provider/plivo"
2023-01-29 23:32:16 +01:00
"github.com/TwiN/gatus/v5/alerting/provider/pushover"
feat(alerting): Add new providers for Datadog, IFTTT, Line, NewRelic, Plivo, RocketChat, SendGrid, Signal, SIGNL4, Splunk, Squadcast, Vonage, Webex and Zapier ( #1224 )
2025-08-25 13:22:17 -04:00
"github.com/TwiN/gatus/v5/alerting/provider/rocketchat"
"github.com/TwiN/gatus/v5/alerting/provider/sendgrid"
"github.com/TwiN/gatus/v5/alerting/provider/signal"
"github.com/TwiN/gatus/v5/alerting/provider/signl4"
2022-12-06 01:41:09 -05:00
"github.com/TwiN/gatus/v5/alerting/provider/slack"
feat(alerting): Add new providers for Datadog, IFTTT, Line, NewRelic, Plivo, RocketChat, SendGrid, Signal, SIGNL4, Splunk, Squadcast, Vonage, Webex and Zapier ( #1224 )
2025-08-25 13:22:17 -04:00
"github.com/TwiN/gatus/v5/alerting/provider/splunk"
"github.com/TwiN/gatus/v5/alerting/provider/squadcast"
2022-12-06 01:41:09 -05:00
"github.com/TwiN/gatus/v5/alerting/provider/teams"
2024-11-13 23:54:00 -05:00
"github.com/TwiN/gatus/v5/alerting/provider/teamsworkflows"
2022-12-06 01:41:09 -05:00
"github.com/TwiN/gatus/v5/alerting/provider/telegram"
"github.com/TwiN/gatus/v5/alerting/provider/twilio"
feat(alerting): Add new providers for Datadog, IFTTT, Line, NewRelic, Plivo, RocketChat, SendGrid, Signal, SIGNL4, Splunk, Squadcast, Vonage, Webex and Zapier ( #1224 )
2025-08-25 13:22:17 -04:00
"github.com/TwiN/gatus/v5/alerting/provider/vonage"
"github.com/TwiN/gatus/v5/alerting/provider/webex"
"github.com/TwiN/gatus/v5/alerting/provider/zapier"
2024-12-16 20:32:13 -05:00
"github.com/TwiN/gatus/v5/alerting/provider/zulip"
2022-12-06 01:41:09 -05:00
"github.com/TwiN/gatus/v5/client"
2024-05-09 22:56:16 -04:00
"github.com/TwiN/gatus/v5/config/endpoint"
2025-09-28 14:26:12 -04:00
"github.com/TwiN/gatus/v5/config/suite"
"github.com/TwiN/gatus/v5/config/tunneling"
"github.com/TwiN/gatus/v5/config/tunneling/sshtunnel"
2022-12-06 01:41:09 -05:00
"github.com/TwiN/gatus/v5/config/web"
"github.com/TwiN/gatus/v5/storage"
2023-01-08 17:53:37 -05:00
"gopkg.in/yaml.v3"
2019-09-08 21:07:08 -04:00
)
2019-09-06 00:02:00 -04:00
2023-01-08 17:53:37 -05:00
func TestLoadConfiguration ( t * testing . T ) {
2024-05-15 21:29:45 -04:00
yes := true
2023-01-08 17:53:37 -05:00
dir := t . TempDir ()
scenarios := [] struct {
name string
configPath string // value to pass as the configPath parameter in LoadConfiguration
pathAndFiles map [ string ] string // files to create in dir
expectedConfig * Config
expectedError error
}{
{
name : "empty-config-file" ,
configPath : filepath . Join ( dir , "config.yaml" ),
pathAndFiles : map [ string ] string {
"config.yaml" : "" ,
},
expectedError : ErrConfigFileNotFound ,
},
{
name : "config-file-that-does-not-exist" ,
configPath : filepath . Join ( dir , "config.yaml" ),
expectedError : ErrConfigFileNotFound ,
},
{
name : "config-file-with-endpoint-that-has-no-url" ,
configPath : filepath . Join ( dir , "config.yaml" ),
pathAndFiles : map [ string ] string {
"config.yaml" : `
endpoints:
- name: website` ,
},
2024-05-09 22:56:16 -04:00
expectedError : endpoint . ErrEndpointWithNoURL ,
2023-01-08 17:53:37 -05:00
},
{
name : "config-file-with-endpoint-that-has-no-conditions" ,
configPath : filepath . Join ( dir , "config.yaml" ),
pathAndFiles : map [ string ] string {
"config.yaml" : `
endpoints:
- name: website
url: https://twin.sh/health` ,
},
2024-05-09 22:56:16 -04:00
expectedError : endpoint . ErrEndpointWithNoCondition ,
2023-01-08 17:53:37 -05:00
},
{
name : "config-file" ,
configPath : filepath . Join ( dir , "config.yaml" ),
pathAndFiles : map [ string ] string {
"config.yaml" : `
endpoints:
- name: website
url: https://twin.sh/health
conditions:
- "[STATUS] == 200"` ,
},
expectedConfig : & Config {
2024-05-09 22:56:16 -04:00
Endpoints : [] * endpoint . Endpoint {
2023-01-08 17:53:37 -05:00
{
Name : "website" ,
URL : "https://twin.sh/health" ,
2024-05-09 22:56:16 -04:00
Conditions : [] endpoint . Condition { "[STATUS] == 200" },
2023-01-08 17:53:37 -05:00
},
},
},
},
{
name : "empty-dir" ,
configPath : dir ,
pathAndFiles : map [ string ] string {},
expectedError : ErrConfigFileNotFound ,
},
{
name : "dir-with-empty-config-file" ,
configPath : dir ,
pathAndFiles : map [ string ] string {
"config.yaml" : "" ,
},
2025-09-16 16:56:36 -04:00
expectedError : ErrNoEndpointOrSuiteInConfig ,
2023-01-08 17:53:37 -05:00
},
{
name : "dir-with-two-config-files" ,
configPath : dir ,
pathAndFiles : map [ string ] string {
2025-08-06 00:26:50 +08:00
"config.yaml" : `endpoints:
2023-01-08 17:53:37 -05:00
- name: one
url: https://example.com
conditions:
- "[CONNECTED] == true"
- "[STATUS] == 200"
- name: two
url: https://example.org
conditions:
- "len([BODY]) > 0"` ,
2025-08-06 00:26:50 +08:00
"config.yml" : `endpoints:
2023-01-08 17:53:37 -05:00
- name: three
url: https://twin.sh/health
conditions:
- "[STATUS] == 200"
- "[BODY].status == UP"` ,
},
expectedConfig : & Config {
2024-05-09 22:56:16 -04:00
Endpoints : [] * endpoint . Endpoint {
2023-01-08 17:53:37 -05:00
{
Name : "one" ,
URL : "https://example.com" ,
2024-05-09 22:56:16 -04:00
Conditions : [] endpoint . Condition { "[CONNECTED] == true" , "[STATUS] == 200" },
2023-01-08 17:53:37 -05:00
},
{
Name : "two" ,
URL : "https://example.org" ,
2024-05-09 22:56:16 -04:00
Conditions : [] endpoint . Condition { "len([BODY]) > 0" },
2023-01-08 17:53:37 -05:00
},
{
Name : "three" ,
URL : "https://twin.sh/health" ,
2024-05-09 22:56:16 -04:00
Conditions : [] endpoint . Condition { "[STATUS] == 200" , "[BODY].status == UP" },
2023-01-08 17:53:37 -05:00
},
},
},
},
{
name : "dir-with-2-config-files-deep-merge-with-map-slice-and-primitive" ,
configPath : dir ,
pathAndFiles : map [ string ] string {
"a.yaml" : `
metrics: true
alerting:
slack:
webhook-url: https://hooks.slack.com/services/xxx/yyy/zzz
2024-05-15 21:29:45 -04:00
default-alert:
enabled: true
2023-01-08 17:53:37 -05:00
endpoints:
- name: example
url: https://example.org
interval: 5s
conditions:
- "[STATUS] == 200"` ,
"b.yaml" : `
alerting:
discord:
webhook-url: https://discord.com/api/webhooks/xxx/yyy
2024-05-15 21:29:45 -04:00
external-endpoints:
- name: ext-ep-test
token: "potato"
alerts:
- type: slack
2023-01-08 17:53:37 -05:00
endpoints:
- name: frontend
url: https://example.com
conditions:
- "[STATUS] == 200"` ,
},
expectedConfig : & Config {
Metrics : true ,
Alerting : & alerting . Config {
2024-12-16 20:32:13 -05:00
Discord : & discord . AlertProvider { DefaultConfig : discord . Config { WebhookURL : "https://discord.com/api/webhooks/xxx/yyy" }},
Slack : & slack . AlertProvider { DefaultConfig : slack . Config { WebhookURL : "https://hooks.slack.com/services/xxx/yyy/zzz" }, DefaultAlert : & alert . Alert { Enabled : & yes }},
2024-05-15 21:29:45 -04:00
},
ExternalEndpoints : [] * endpoint . ExternalEndpoint {
{
Name : "ext-ep-test" ,
Token : "potato" ,
Alerts : [] * alert . Alert {
{
Type : alert . TypeSlack ,
FailureThreshold : 3 ,
SuccessThreshold : 2 ,
},
},
},
2023-01-08 17:53:37 -05:00
},
2024-05-09 22:56:16 -04:00
Endpoints : [] * endpoint . Endpoint {
2023-01-08 17:53:37 -05:00
{
Name : "example" ,
URL : "https://example.org" ,
Interval : 5 * time . Second ,
2024-05-09 22:56:16 -04:00
Conditions : [] endpoint . Condition { "[STATUS] == 200" },
2023-01-08 17:53:37 -05:00
},
{
Name : "frontend" ,
URL : "https://example.com" ,
2024-05-09 22:56:16 -04:00
Conditions : [] endpoint . Condition { "[STATUS] == 200" },
2023-01-08 17:53:37 -05:00
},
},
},
},
}
for _ , scenario := range scenarios {
t . Run ( scenario . name , func ( t * testing . T ) {
for path , content := range scenario . pathAndFiles {
2025-08-06 00:26:50 +08:00
if err := os . WriteFile ( filepath . Join ( dir , path ), [] byte ( content ), 0 o644 ); err != nil {
2023-01-08 17:53:37 -05:00
t . Fatalf ( "[%s] failed to write file: %v" , scenario . name , err )
}
}
defer func ( pathAndFiles map [ string ] string ) {
for path := range pathAndFiles {
_ = os . Remove ( filepath . Join ( dir , path ))
}
}( scenario . pathAndFiles )
config , err := LoadConfiguration ( scenario . configPath )
if ! errors . Is ( err , scenario . expectedError ) {
t . Errorf ( "[%s] expected error %v, got %v" , scenario . name , scenario . expectedError , err )
return
} else if err != nil && errors . Is ( err , scenario . expectedError ) {
return
}
// parse the expected output so that expectations are closer to reality (under the right circumstances, even I can be poetic)
expectedConfigAsYAML , _ := yaml . Marshal ( scenario . expectedConfig )
expectedConfigAfterBeingParsedAndValidated , err := parseAndValidateConfigBytes ( expectedConfigAsYAML )
if err != nil {
t . Fatalf ( "[%s] failed to parse expected config: %v" , scenario . name , err )
}
// Marshal em' before comparing em' so that we don't have to deal with formatting and ordering
actualConfigAsYAML , err := yaml . Marshal ( config )
if err != nil {
t . Fatalf ( "[%s] failed to marshal actual config: %v" , scenario . name , err )
}
expectedConfigAfterBeingParsedAndValidatedAsYAML , _ := yaml . Marshal ( expectedConfigAfterBeingParsedAndValidated )
if string ( actualConfigAsYAML ) != string ( expectedConfigAfterBeingParsedAndValidatedAsYAML ) {
t . Errorf ( "[%s] expected config %s, got %s" , scenario . name , string ( expectedConfigAfterBeingParsedAndValidatedAsYAML ), string ( actualConfigAsYAML ))
}
})
2020-10-21 22:56:35 -04:00
}
}
2023-01-08 17:53:37 -05:00
func TestConfig_HasLoadedConfigurationBeenModified ( t * testing . T ) {
t . Parallel ()
dir := t . TempDir ()
configFilePath := filepath . Join ( dir , "config.yaml" )
_ = os . WriteFile ( configFilePath , [] byte ( `endpoints:
- name: website
url: https://twin.sh/health
conditions:
- "[STATUS] == 200"
2025-08-06 00:26:50 +08:00
` ), 0 o644 )
2023-01-08 17:53:37 -05:00
t . Run ( "config-file-as-config-path" , func ( t * testing . T ) {
config , err := LoadConfiguration ( configFilePath )
if err != nil {
t . Fatalf ( "failed to load configuration: %v" , err )
}
if config . HasLoadedConfigurationBeenModified () {
t . Errorf ( "expected config.HasLoadedConfigurationBeenModified() to return false because nothing has happened since it was created" )
}
2026-09-21 21:08:52 -04:00
if err = os . WriteFile ( configFilePath , [] byte ( `endpoints:
2023-01-08 17:53:37 -05:00
- name: website
url: https://twin.sh/health
conditions:
2025-08-06 00:26:50 +08:00
- "[STATUS] == 200"` ), 0 o644 ); err != nil {
2023-01-08 17:53:37 -05:00
t . Fatalf ( "failed to overwrite config file: %v" , err )
}
2026-09-21 21:08:52 -04:00
// File mod times have second precision, so advance it explicitly.
future := time . Now (). Add ( 2 * time . Second )
if err = os . Chtimes ( configFilePath , future , future ); err != nil {
t . Fatalf ( "failed to advance config file mod time: %v" , err )
}
2023-01-08 17:53:37 -05:00
if ! config . HasLoadedConfigurationBeenModified () {
2026-09-21 21:08:52 -04:00
t . Errorf ( "expected config.HasLoadedConfigurationBeenModified() to return true because the config file has been modified" )
2023-01-08 17:53:37 -05:00
}
})
t . Run ( "config-directory-as-config-path" , func ( t * testing . T ) {
2026-09-21 21:08:52 -04:00
dir := t . TempDir ()
if err := os . WriteFile ( filepath . Join ( dir , "config.yaml" ), [] byte ( `endpoints:
- name: website
url: https://twin.sh/health
conditions:
- "[STATUS] == 200"
` ), 0 o644 ); err != nil {
t . Fatalf ( "failed to write config file: %v" , err )
}
2023-01-08 17:53:37 -05:00
config , err := LoadConfiguration ( dir )
if err != nil {
t . Fatalf ( "failed to load configuration: %v" , err )
}
if config . HasLoadedConfigurationBeenModified () {
t . Errorf ( "expected config.HasLoadedConfigurationBeenModified() to return false because nothing has happened since it was created" )
}
2025-08-06 00:26:50 +08:00
if err = os . WriteFile ( filepath . Join ( dir , "metrics.yaml" ), [] byte ( `metrics: true` ), 0 o644 ); err != nil {
2026-09-21 21:08:52 -04:00
t . Fatalf ( "failed to add config file: %v" , err )
}
// File mod times have second precision, so advance it explicitly.
future := time . Now (). Add ( 2 * time . Second )
if err = os . Chtimes ( filepath . Join ( dir , "metrics.yaml" ), future , future ); err != nil {
t . Fatalf ( "failed to advance config file mod time: %v" , err )
2023-01-08 17:53:37 -05:00
}
if ! config . HasLoadedConfigurationBeenModified () {
t . Errorf ( "expected config.HasLoadedConfigurationBeenModified() to return true because a new file has been added in the directory" )
}
})
2020-10-21 22:56:35 -04:00
}
2019-10-19 22:03:55 -04:00
func TestParseAndValidateConfigBytes ( t * testing . T ) {
2021-02-02 23:06:34 -05:00
file := t . TempDir () + "/test.db"
config , err := parseAndValidateConfigBytes ([] byte ( fmt . Sprintf ( `
storage:
2021-11-04 21:33:13 -04:00
type: sqlite
path: %s
2025-05-17 16:10:28 -04:00
maximum-number-of-results: 10
maximum-number-of-events: 5
2024-04-08 21:00:40 -04:00
2021-09-22 00:04:51 -04:00
maintenance:
enabled: true
start: 00:00
duration: 4h
every: [Monday, Thursday]
2024-04-08 21:00:40 -04:00
2021-09-11 01:51:14 -04:00
ui:
2022-04-25 20:20:32 -04:00
title: T
header: H
link: https://example.org
buttons:
- name: "Home"
link: "https://example.org"
- name: "Status page"
link: "https://status.example.org"
2024-04-08 21:00:40 -04:00
external-endpoints:
- name: ext-ep-test
group: core
token: "potato"
2021-10-23 16:47:12 -04:00
endpoints:
2021-09-18 12:42:11 -04:00
- name: website
url: https://twin.sh/health
2019-09-08 21:07:08 -04:00
interval: 15s
2019-09-06 00:02:00 -04:00
conditions:
2019-12-04 17:27:27 -05:00
- "[STATUS] == 200"
2021-07-28 21:41:26 -04:00
2019-09-06 00:02:00 -04:00
- name: github
2019-09-08 21:07:08 -04:00
url: https://api.github.com/healthz
2021-07-28 21:41:26 -04:00
client:
insecure: true
ignore-redirect: true
timeout: 5s
2019-09-06 00:02:00 -04:00
conditions:
2019-12-04 17:27:27 -05:00
- "[STATUS] != 400"
- "[STATUS] != 500"
2021-07-28 21:41:26 -04:00
- name: example
url: https://example.com/
interval: 30m
client:
insecure: true
conditions:
- "[STATUS] == 200"
2021-02-02 23:06:34 -05:00
` , file )))
2019-10-19 22:03:55 -04:00
if err != nil {
2021-02-02 23:06:34 -05:00
t . Error ( "expected no error, got" , err . Error ())
2019-10-19 22:03:55 -04:00
}
2020-08-22 14:15:21 -04:00
if config == nil {
t . Fatal ( "Config shouldn't have been nil" )
}
2021-11-04 21:33:13 -04:00
if config . Storage == nil || config . Storage . Path != file || config . Storage . Type != storage . TypeSQLite {
t . Error ( "expected storage to be set to sqlite, got" , config . Storage )
}
2025-05-17 16:10:28 -04:00
if config . Storage == nil || config . Storage . MaximumNumberOfResults != 10 || config . Storage . MaximumNumberOfEvents != 5 {
t . Error ( "expected MaximumNumberOfResults and MaximumNumberOfEvents to be set to 10 and 5, got" , config . Storage . MaximumNumberOfResults , config . Storage . MaximumNumberOfEvents )
}
2022-04-25 20:20:32 -04:00
if config . UI == nil || config . UI . Title != "T" || config . UI . Header != "H" || config . UI . Link != "https://example.org" || len ( config . UI . Buttons ) != 2 || config . UI . Buttons [ 0 ]. Name != "Home" || config . UI . Buttons [ 0 ]. Link != "https://example.org" || config . UI . Buttons [ 1 ]. Name != "Status page" || config . UI . Buttons [ 1 ]. Link != "https://status.example.org" {
t . Error ( "expected ui to be set to T, H, https://example.org, 2 buttons, Home and Status page, got" , config . UI )
2021-09-11 01:51:14 -04:00
}
2021-09-22 00:04:51 -04:00
if mc := config . Maintenance ; mc == nil || mc . Start != "00:00" || ! mc . IsEnabled () || mc . Duration != 4 * time . Hour || len ( mc . Every ) != 2 {
t . Error ( "Expected Config.Maintenance to be configured properly" )
}
2024-04-08 21:00:40 -04:00
if len ( config . ExternalEndpoints ) != 1 {
t . Error ( "Should have returned one external endpoint" )
}
if config . ExternalEndpoints [ 0 ]. Name != "ext-ep-test" {
t . Errorf ( "Name should have been %s" , "ext-ep-test" )
}
if config . ExternalEndpoints [ 0 ]. Group != "core" {
t . Errorf ( "Group should have been %s" , "core" )
}
if config . ExternalEndpoints [ 0 ]. Token != "potato" {
t . Errorf ( "Token should have been %s" , "potato" )
}
2024-05-15 21:29:45 -04:00
2021-10-23 16:47:12 -04:00
if len ( config . Endpoints ) != 3 {
t . Error ( "Should have returned two endpoints" )
2019-09-06 00:02:00 -04:00
}
2021-10-23 16:47:12 -04:00
if config . Endpoints [ 0 ]. URL != "https://twin.sh/health" {
2021-09-18 12:42:11 -04:00
t . Errorf ( "URL should have been %s" , "https://twin.sh/health" )
2019-09-06 00:02:00 -04:00
}
2021-10-23 16:47:12 -04:00
if config . Endpoints [ 0 ]. Method != "GET" {
2020-10-21 21:56:07 -04:00
t . Errorf ( "Method should have been %s (default)" , "GET" )
}
2021-10-23 16:47:12 -04:00
if config . Endpoints [ 0 ]. Interval != 15 * time . Second {
2019-09-08 21:07:08 -04:00
t . Errorf ( "Interval should have been %s" , 15 * time . Second )
2019-09-06 00:02:00 -04:00
}
2021-10-23 16:47:12 -04:00
if config . Endpoints [ 0 ]. ClientConfig . Insecure != client . GetDefaultConfig (). Insecure {
t . Errorf ( "ClientConfig.Insecure should have been %v, got %v" , true , config . Endpoints [ 0 ]. ClientConfig . Insecure )
2021-07-28 21:41:26 -04:00
}
2021-10-23 16:47:12 -04:00
if config . Endpoints [ 0 ]. ClientConfig . IgnoreRedirect != client . GetDefaultConfig (). IgnoreRedirect {
t . Errorf ( "ClientConfig.IgnoreRedirect should have been %v, got %v" , true , config . Endpoints [ 0 ]. ClientConfig . IgnoreRedirect )
2021-07-28 21:41:26 -04:00
}
2021-10-23 16:47:12 -04:00
if config . Endpoints [ 0 ]. ClientConfig . Timeout != client . GetDefaultConfig (). Timeout {
t . Errorf ( "ClientConfig.Timeout should have been %v, got %v" , client . GetDefaultConfig (). Timeout , config . Endpoints [ 0 ]. ClientConfig . Timeout )
2019-09-06 00:02:00 -04:00
}
2021-10-23 16:47:12 -04:00
if len ( config . Endpoints [ 0 ]. Conditions ) != 1 {
2019-09-06 00:02:00 -04:00
t . Errorf ( "There should have been %d conditions" , 1 )
}
2021-10-23 16:47:12 -04:00
if config . Endpoints [ 1 ]. URL != "https://api.github.com/healthz" {
2021-07-28 21:41:26 -04:00
t . Errorf ( "URL should have been %s" , "https://api.github.com/healthz" )
}
2021-10-23 16:47:12 -04:00
if config . Endpoints [ 1 ]. Method != "GET" {
2021-07-28 21:41:26 -04:00
t . Errorf ( "Method should have been %s (default)" , "GET" )
}
2021-10-23 16:47:12 -04:00
if config . Endpoints [ 1 ]. Interval != 60 * time . Second {
2021-07-28 21:41:26 -04:00
t . Errorf ( "Interval should have been %s, because it is the default value" , 60 * time . Second )
}
2021-10-23 16:47:12 -04:00
if ! config . Endpoints [ 1 ]. ClientConfig . Insecure {
t . Errorf ( "ClientConfig.Insecure should have been %v, got %v" , true , config . Endpoints [ 1 ]. ClientConfig . Insecure )
2021-07-28 21:41:26 -04:00
}
2021-10-23 16:47:12 -04:00
if ! config . Endpoints [ 1 ]. ClientConfig . IgnoreRedirect {
t . Errorf ( "ClientConfig.IgnoreRedirect should have been %v, got %v" , true , config . Endpoints [ 1 ]. ClientConfig . IgnoreRedirect )
2021-07-28 21:41:26 -04:00
}
2021-10-23 16:47:12 -04:00
if config . Endpoints [ 1 ]. ClientConfig . Timeout != 5 * time . Second {
t . Errorf ( "ClientConfig.Timeout should have been %v, got %v" , 5 * time . Second , config . Endpoints [ 1 ]. ClientConfig . Timeout )
2021-07-28 21:41:26 -04:00
}
2021-10-23 16:47:12 -04:00
if len ( config . Endpoints [ 1 ]. Conditions ) != 2 {
2019-09-06 00:02:00 -04:00
t . Errorf ( "There should have been %d conditions" , 2 )
}
2021-10-23 16:47:12 -04:00
if config . Endpoints [ 2 ]. URL != "https://example.com/" {
2021-07-28 21:41:26 -04:00
t . Errorf ( "URL should have been %s" , "https://example.com/" )
}
2021-10-23 16:47:12 -04:00
if config . Endpoints [ 2 ]. Method != "GET" {
2021-07-28 21:41:26 -04:00
t . Errorf ( "Method should have been %s (default)" , "GET" )
}
2021-10-23 16:47:12 -04:00
if config . Endpoints [ 2 ]. Interval != 30 * time . Minute {
2021-07-28 21:41:26 -04:00
t . Errorf ( "Interval should have been %s, because it is the default value" , 30 * time . Minute )
}
2021-10-23 16:47:12 -04:00
if ! config . Endpoints [ 2 ]. ClientConfig . Insecure {
t . Errorf ( "ClientConfig.Insecure should have been %v, got %v" , true , config . Endpoints [ 2 ]. ClientConfig . Insecure )
2021-07-28 21:41:26 -04:00
}
2021-10-23 16:47:12 -04:00
if config . Endpoints [ 2 ]. ClientConfig . IgnoreRedirect {
t . Errorf ( "ClientConfig.IgnoreRedirect should have been %v by default, got %v" , false , config . Endpoints [ 2 ]. ClientConfig . IgnoreRedirect )
2021-07-28 21:41:26 -04:00
}
2021-10-23 16:47:12 -04:00
if config . Endpoints [ 2 ]. ClientConfig . Timeout != 10 * time . Second {
t . Errorf ( "ClientConfig.Timeout should have been %v by default, got %v" , 10 * time . Second , config . Endpoints [ 2 ]. ClientConfig . Timeout )
2021-07-28 21:41:26 -04:00
}
2021-10-23 16:47:12 -04:00
if len ( config . Endpoints [ 2 ]. Conditions ) != 1 {
2021-07-28 21:41:26 -04:00
t . Errorf ( "There should have been %d conditions" , 1 )
}
2019-09-06 00:02:00 -04:00
}
2019-09-09 22:21:18 -04:00
2019-10-19 22:03:55 -04:00
func TestParseAndValidateConfigBytesDefault ( t * testing . T ) {
config , err := parseAndValidateConfigBytes ([] byte ( `
2021-10-23 16:47:12 -04:00
endpoints:
2021-09-18 12:42:11 -04:00
- name: website
url: https://twin.sh/health
2019-09-09 22:21:18 -04:00
conditions:
2019-12-04 17:27:27 -05:00
- "[STATUS] == 200"
2019-09-09 22:21:18 -04:00
` ))
2019-10-19 22:03:55 -04:00
if err != nil {
2021-02-02 23:06:34 -05:00
t . Error ( "expected no error, got" , err . Error ())
2019-10-19 22:03:55 -04:00
}
2020-08-22 14:15:21 -04:00
if config == nil {
2024-12-16 20:32:13 -05:00
t . Fatal ( "DefaultConfig shouldn't have been nil" )
2020-08-22 14:15:21 -04:00
}
2019-11-16 15:47:56 -05:00
if config . Metrics {
t . Error ( "Metrics should've been false by default" )
}
2021-09-22 00:47:51 -04:00
if config . Web . Address != web . DefaultAddress {
t . Errorf ( "Bind address should have been %s, because it is the default value" , web . DefaultAddress )
2021-07-28 21:41:26 -04:00
}
2021-09-22 00:47:51 -04:00
if config . Web . Port != web . DefaultPort {
t . Errorf ( "Port should have been %d, because it is the default value" , web . DefaultPort )
2021-07-28 21:41:26 -04:00
}
2021-10-23 16:47:12 -04:00
if config . Endpoints [ 0 ]. URL != "https://twin.sh/health" {
2021-09-18 12:42:11 -04:00
t . Errorf ( "URL should have been %s" , "https://twin.sh/health" )
2019-11-16 15:47:56 -05:00
}
2021-10-23 16:47:12 -04:00
if config . Endpoints [ 0 ]. Interval != 60 * time . Second {
2020-09-01 12:46:23 -04:00
t . Errorf ( "Interval should have been %s, because it is the default value" , 60 * time . Second )
2019-11-16 15:47:56 -05:00
}
2021-10-23 16:47:12 -04:00
if config . Endpoints [ 0 ]. ClientConfig . Insecure != client . GetDefaultConfig (). Insecure {
t . Errorf ( "ClientConfig.Insecure should have been %v by default, got %v" , true , config . Endpoints [ 0 ]. ClientConfig . Insecure )
2020-11-19 19:39:48 +01:00
}
2021-10-23 16:47:12 -04:00
if config . Endpoints [ 0 ]. ClientConfig . IgnoreRedirect != client . GetDefaultConfig (). IgnoreRedirect {
t . Errorf ( "ClientConfig.IgnoreRedirect should have been %v by default, got %v" , true , config . Endpoints [ 0 ]. ClientConfig . IgnoreRedirect )
2021-07-28 21:41:26 -04:00
}
2021-10-23 16:47:12 -04:00
if config . Endpoints [ 0 ]. ClientConfig . Timeout != client . GetDefaultConfig (). Timeout {
t . Errorf ( "ClientConfig.Timeout should have been %v by default, got %v" , client . GetDefaultConfig (). Timeout , config . Endpoints [ 0 ]. ClientConfig . Timeout )
2020-11-19 19:39:48 +01:00
}
}
func TestParseAndValidateConfigBytesWithAddress ( t * testing . T ) {
config , err := parseAndValidateConfigBytes ([] byte ( `
web:
address: 127.0.0.1
2021-10-23 16:47:12 -04:00
endpoints:
2021-09-18 12:42:11 -04:00
- name: website
url: https://twin.sh/actuator/health
2020-11-19 19:39:48 +01:00
conditions:
- "[STATUS] == 200"
` ))
if err != nil {
2021-02-02 23:06:34 -05:00
t . Error ( "expected no error, got" , err . Error ())
2020-11-19 19:39:48 +01:00
}
if config == nil {
t . Fatal ( "Config shouldn't have been nil" )
}
if config . Metrics {
t . Error ( "Metrics should've been false by default" )
}
2021-10-23 16:47:12 -04:00
if config . Endpoints [ 0 ]. URL != "https://twin.sh/actuator/health" {
2021-09-18 12:42:11 -04:00
t . Errorf ( "URL should have been %s" , "https://twin.sh/actuator/health" )
2020-11-19 19:39:48 +01:00
}
2021-10-23 16:47:12 -04:00
if config . Endpoints [ 0 ]. Interval != 60 * time . Second {
2020-11-19 19:39:48 +01:00
t . Errorf ( "Interval should have been %s, because it is the default value" , 60 * time . Second )
}
if config . Web . Address != "127.0.0.1" {
t . Errorf ( "Bind address should have been %s, because it is specified in config" , "127.0.0.1" )
}
2021-09-22 00:47:51 -04:00
if config . Web . Port != web . DefaultPort {
t . Errorf ( "Port should have been %d, because it is the default value" , web . DefaultPort )
2020-11-19 19:39:48 +01:00
}
}
func TestParseAndValidateConfigBytesWithPort ( t * testing . T ) {
config , err := parseAndValidateConfigBytes ([] byte ( `
web:
port: 12345
2021-10-23 16:47:12 -04:00
endpoints:
2021-09-18 12:42:11 -04:00
- name: website
url: https://twin.sh/health
2020-11-19 19:39:48 +01:00
conditions:
- "[STATUS] == 200"
` ))
if err != nil {
2021-02-02 23:06:34 -05:00
t . Error ( "expected no error, got" , err . Error ())
2020-11-19 19:39:48 +01:00
}
if config == nil {
t . Fatal ( "Config shouldn't have been nil" )
}
if config . Metrics {
t . Error ( "Metrics should've been false by default" )
}
2021-10-23 16:47:12 -04:00
if config . Endpoints [ 0 ]. URL != "https://twin.sh/health" {
2021-09-18 12:42:11 -04:00
t . Errorf ( "URL should have been %s" , "https://twin.sh/health" )
2020-11-19 19:39:48 +01:00
}
2021-10-23 16:47:12 -04:00
if config . Endpoints [ 0 ]. Interval != 60 * time . Second {
2020-11-19 19:39:48 +01:00
t . Errorf ( "Interval should have been %s, because it is the default value" , 60 * time . Second )
}
2021-09-22 00:47:51 -04:00
if config . Web . Address != web . DefaultAddress {
t . Errorf ( "Bind address should have been %s, because it is the default value" , web . DefaultAddress )
2020-11-19 19:39:48 +01:00
}
if config . Web . Port != 12345 {
t . Errorf ( "Port should have been %d, because it is specified in config" , 12345 )
}
}
func TestParseAndValidateConfigBytesWithPortAndHost ( t * testing . T ) {
config , err := parseAndValidateConfigBytes ([] byte ( `
web:
port: 12345
address: 127.0.0.1
2021-10-23 16:47:12 -04:00
endpoints:
2021-09-18 12:42:11 -04:00
- name: website
url: https://twin.sh/health
2020-11-19 19:39:48 +01:00
conditions:
- "[STATUS] == 200"
` ))
if err != nil {
2021-02-02 23:06:34 -05:00
t . Error ( "expected no error, got" , err . Error ())
2020-11-19 19:39:48 +01:00
}
if config == nil {
t . Fatal ( "Config shouldn't have been nil" )
}
if config . Metrics {
t . Error ( "Metrics should've been false by default" )
}
2021-10-23 16:47:12 -04:00
if config . Endpoints [ 0 ]. URL != "https://twin.sh/health" {
2021-09-18 12:42:11 -04:00
t . Errorf ( "URL should have been %s" , "https://twin.sh/health" )
2020-11-19 19:39:48 +01:00
}
2021-10-23 16:47:12 -04:00
if config . Endpoints [ 0 ]. Interval != 60 * time . Second {
2020-11-19 19:39:48 +01:00
t . Errorf ( "Interval should have been %s, because it is the default value" , 60 * time . Second )
}
if config . Web . Address != "127.0.0.1" {
t . Errorf ( "Bind address should have been %s, because it is specified in config" , "127.0.0.1" )
}
if config . Web . Port != 12345 {
t . Errorf ( "Port should have been %d, because it is specified in config" , 12345 )
}
2019-11-16 15:47:56 -05:00
}
2020-11-19 20:03:30 +01:00
func TestParseAndValidateConfigBytesWithInvalidPort ( t * testing . T ) {
2021-05-18 23:27:43 -04:00
_ , err := parseAndValidateConfigBytes ([] byte ( `
2020-11-19 20:03:30 +01:00
web:
port: 65536
address: 127.0.0.1
2021-10-23 16:47:12 -04:00
endpoints:
2021-09-18 12:42:11 -04:00
- name: website
url: https://twin.sh/health
2020-11-19 20:03:30 +01:00
conditions:
- "[STATUS] == 200"
` ))
2021-05-18 23:27:43 -04:00
if err == nil {
t . Fatal ( "Should've returned an error because the configuration specifies an invalid port value" )
}
2019-11-16 15:47:56 -05:00
}
2020-12-30 20:07:07 -05:00
func TestParseAndValidateConfigBytesWithMetricsAndCustomUserAgentHeader ( t * testing . T ) {
2019-11-16 15:47:56 -05:00
config , err := parseAndValidateConfigBytes ([] byte ( `
metrics: true
2021-10-23 16:47:12 -04:00
endpoints:
2021-09-18 12:42:11 -04:00
- name: website
url: https://twin.sh/health
2020-12-30 20:07:07 -05:00
headers:
User-Agent: Test/2.0
2019-11-16 15:47:56 -05:00
conditions:
2019-12-04 17:27:27 -05:00
- "[STATUS] == 200"
2019-11-16 15:47:56 -05:00
` ))
if err != nil {
2021-02-02 23:06:34 -05:00
t . Error ( "expected no error, got" , err . Error ())
2019-11-16 15:47:56 -05:00
}
2020-08-22 14:15:21 -04:00
if config == nil {
t . Fatal ( "Config shouldn't have been nil" )
}
2019-11-16 15:47:56 -05:00
if ! config . Metrics {
t . Error ( "Metrics should have been true" )
}
2021-10-23 16:47:12 -04:00
if config . Endpoints [ 0 ]. URL != "https://twin.sh/health" {
2021-09-18 12:42:11 -04:00
t . Errorf ( "URL should have been %s" , "https://twin.sh/health" )
2019-09-09 22:21:18 -04:00
}
2021-10-23 16:47:12 -04:00
if config . Endpoints [ 0 ]. Interval != 60 * time . Second {
2020-09-01 12:46:23 -04:00
t . Errorf ( "Interval should have been %s, because it is the default value" , 60 * time . Second )
2019-09-09 22:21:18 -04:00
}
2021-09-22 00:47:51 -04:00
if config . Web . Address != web . DefaultAddress {
t . Errorf ( "Bind address should have been %s, because it is the default value" , web . DefaultAddress )
2020-11-19 19:39:48 +01:00
}
2021-09-22 00:47:51 -04:00
if config . Web . Port != web . DefaultPort {
t . Errorf ( "Port should have been %d, because it is the default value" , web . DefaultPort )
2020-11-19 19:39:48 +01:00
}
2021-10-23 16:47:12 -04:00
if userAgent := config . Endpoints [ 0 ]. Headers [ "User-Agent" ]; userAgent != "Test/2.0" {
2020-12-30 20:07:07 -05:00
t . Errorf ( "User-Agent should've been %s, got %s" , "Test/2.0" , userAgent )
}
2020-11-19 19:39:48 +01:00
}
func TestParseAndValidateConfigBytesWithMetricsAndHostAndPort ( t * testing . T ) {
config , err := parseAndValidateConfigBytes ([] byte ( `
metrics: true
2020-12-30 20:07:07 -05:00
web:
address: 192.168.0.1
port: 9090
2021-10-23 16:47:12 -04:00
endpoints:
2021-09-18 12:42:11 -04:00
- name: website
url: https://twin.sh/health
2020-11-19 19:39:48 +01:00
conditions:
- "[STATUS] == 200"
` ))
if err != nil {
2021-02-02 23:06:34 -05:00
t . Error ( "expected no error, got" , err . Error ())
2020-11-19 19:39:48 +01:00
}
if config == nil {
t . Fatal ( "Config shouldn't have been nil" )
}
if ! config . Metrics {
t . Error ( "Metrics should have been true" )
}
2020-12-30 20:07:07 -05:00
if config . Web . Address != "192.168.0.1" {
t . Errorf ( "Bind address should have been %s, because it is the default value" , "192.168.0.1" )
}
if config . Web . Port != 9090 {
t . Errorf ( "Port should have been %d, because it is specified in config" , 9090 )
}
2021-10-23 16:47:12 -04:00
if config . Endpoints [ 0 ]. URL != "https://twin.sh/health" {
2021-09-18 12:42:11 -04:00
t . Errorf ( "URL should have been %s" , "https://twin.sh/health" )
2020-11-19 19:39:48 +01:00
}
2021-10-23 16:47:12 -04:00
if config . Endpoints [ 0 ]. Interval != 60 * time . Second {
2020-11-19 19:39:48 +01:00
t . Errorf ( "Interval should have been %s, because it is the default value" , 60 * time . Second )
}
2024-05-09 22:56:16 -04:00
if userAgent := config . Endpoints [ 0 ]. Headers [ "User-Agent" ]; userAgent != endpoint . GatusUserAgent {
t . Errorf ( "User-Agent should've been %s because it's the default value, got %s" , endpoint . GatusUserAgent , userAgent )
2020-11-19 19:39:48 +01:00
}
2019-09-09 22:21:18 -04:00
}
2019-10-19 22:03:55 -04:00
func TestParseAndValidateBadConfigBytes ( t * testing . T ) {
_ , err := parseAndValidateConfigBytes ([] byte ( `
badconfig:
- asdsa: w0w
2025-08-06 00:26:50 +08:00
usadasdrl: asdxzczxc
2019-10-19 22:03:55 -04:00
asdas:
- soup
` ))
if err == nil {
t . Error ( "An error should've been returned" )
}
2025-09-16 16:56:36 -04:00
if err != ErrNoEndpointOrSuiteInConfig {
t . Error ( "The error returned should have been of type ErrNoEndpointOrSuiteInConfig" )
2019-10-19 22:03:55 -04:00
}
}
2020-08-22 14:15:21 -04:00
func TestParseAndValidateConfigBytesWithAlerting ( t * testing . T ) {
config , err := parseAndValidateConfigBytes ([] byte ( `
alerting:
2020-09-19 16:22:12 -04:00
slack:
webhook-url: "http://example.com"
2021-03-04 21:26:17 -05:00
discord:
webhook-url: "http://example.org"
2020-09-24 19:52:59 -04:00
pagerduty:
integration-key: "00000000000000000000000000000000"
2023-01-29 23:32:16 +01:00
pushover:
application-token: "000000000000000000000000000000"
user-key: "000000000000000000000000000000"
2021-05-15 21:31:32 -04:00
mattermost:
webhook-url: "http://example.com"
2021-07-28 21:41:26 -04:00
client:
insecure: true
2020-11-23 22:20:06 +01:00
messagebird:
2020-11-23 22:29:57 +01:00
access-key: "1"
2020-11-25 00:31:51 +01:00
originator: "31619191918"
recipients: "31619191919"
2021-03-31 01:38:34 +02:00
telegram:
token: 123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11
id: 0123456789
2021-05-15 21:31:32 -04:00
twilio:
sid: "1234"
token: "5678"
from: "+1-234-567-8901"
to: "+1-234-567-8901"
2021-07-28 14:15:56 +02:00
teams:
webhook-url: "http://example.com"
2021-05-15 21:31:32 -04:00
2021-10-23 16:47:12 -04:00
endpoints:
2021-09-18 12:42:11 -04:00
- name: website
url: https://twin.sh/health
2020-08-22 14:15:21 -04:00
alerts:
- type: slack
2020-10-21 21:56:07 -04:00
- type: pagerduty
2020-09-16 20:22:33 -04:00
failure-threshold: 7
2020-10-21 21:56:07 -04:00
success-threshold: 5
2020-08-22 14:15:21 -04:00
description: "Healthcheck failed 7 times in a row"
2021-05-15 21:31:32 -04:00
- type: mattermost
2020-11-23 22:20:06 +01:00
- type: messagebird
2022-12-05 23:15:19 -05:00
enabled: false
2021-03-04 21:26:17 -05:00
- type: discord
failure-threshold: 10
2021-03-31 01:38:34 +02:00
- type: telegram
enabled: true
2021-05-15 21:31:32 -04:00
- type: twilio
failure-threshold: 12
success-threshold: 15
2021-07-28 14:15:56 +02:00
- type: teams
2023-01-29 23:32:16 +01:00
- type: pushover
2020-08-22 14:15:21 -04:00
conditions:
- "[STATUS] == 200"
2021-05-15 21:31:32 -04:00
` ))
if err != nil {
t . Error ( "expected no error, got" , err . Error ())
}
if config == nil {
t . Fatal ( "Config shouldn't have been nil" )
}
// Alerting providers
if config . Alerting == nil {
t . Fatal ( "config.Alerting shouldn't have been nil" )
}
2024-12-16 20:32:13 -05:00
if config . Alerting . Slack == nil || config . Alerting . Slack . Validate () != nil {
2021-05-15 21:31:32 -04:00
t . Fatal ( "Slack alerting config should've been valid" )
}
2021-10-23 16:47:12 -04:00
// Endpoints
if len ( config . Endpoints ) != 1 {
t . Error ( "There should've been 1 endpoint" )
2021-05-15 21:31:32 -04:00
}
2021-10-23 16:47:12 -04:00
if config . Endpoints [ 0 ]. URL != "https://twin.sh/health" {
2021-09-18 12:42:11 -04:00
t . Errorf ( "URL should have been %s" , "https://twin.sh/health" )
2021-05-15 21:31:32 -04:00
}
2021-10-23 16:47:12 -04:00
if config . Endpoints [ 0 ]. Interval != 60 * time . Second {
2021-05-15 21:31:32 -04:00
t . Errorf ( "Interval should have been %s, because it is the default value" , 60 * time . Second )
}
2025-10-13 19:47:05 -05:00
if len ( config . Endpoints [ 0 ]. Alerts ) != 9 {
t . Fatal ( "There should've been 9 alerts configured" )
2021-05-15 21:31:32 -04:00
}
2021-10-23 16:47:12 -04:00
if config . Endpoints [ 0 ]. Alerts [ 0 ]. Type != alert . TypeSlack {
t . Errorf ( "The type of the alert should've been %s, but it was %s" , alert . TypeSlack , config . Endpoints [ 0 ]. Alerts [ 0 ]. Type )
2021-05-15 21:31:32 -04:00
}
2021-10-23 16:47:12 -04:00
if ! config . Endpoints [ 0 ]. Alerts [ 0 ]. IsEnabled () {
2021-05-15 21:31:32 -04:00
t . Error ( "The alert should've been enabled" )
}
2021-10-23 16:47:12 -04:00
if config . Endpoints [ 0 ]. Alerts [ 0 ]. FailureThreshold != 3 {
t . Errorf ( "The default failure threshold of the alert should've been %d, but it was %d" , 3 , config . Endpoints [ 0 ]. Alerts [ 0 ]. FailureThreshold )
2021-05-15 21:31:32 -04:00
}
2021-10-23 16:47:12 -04:00
if config . Endpoints [ 0 ]. Alerts [ 0 ]. SuccessThreshold != 2 {
t . Errorf ( "The default success threshold of the alert should've been %d, but it was %d" , 2 , config . Endpoints [ 0 ]. Alerts [ 0 ]. SuccessThreshold )
2021-05-15 21:31:32 -04:00
}
2021-10-23 16:47:12 -04:00
if config . Endpoints [ 0 ]. Alerts [ 1 ]. Type != alert . TypePagerDuty {
t . Errorf ( "The type of the alert should've been %s, but it was %s" , alert . TypePagerDuty , config . Endpoints [ 0 ]. Alerts [ 1 ]. Type )
2021-05-15 21:31:32 -04:00
}
2021-10-23 16:47:12 -04:00
if config . Endpoints [ 0 ]. Alerts [ 1 ]. GetDescription () != "Healthcheck failed 7 times in a row" {
t . Errorf ( "The description of the alert should've been %s, but it was %s" , "Healthcheck failed 7 times in a row" , config . Endpoints [ 0 ]. Alerts [ 1 ]. GetDescription ())
2021-05-15 21:31:32 -04:00
}
2021-10-23 16:47:12 -04:00
if config . Endpoints [ 0 ]. Alerts [ 1 ]. FailureThreshold != 7 {
t . Errorf ( "The failure threshold of the alert should've been %d, but it was %d" , 7 , config . Endpoints [ 0 ]. Alerts [ 1 ]. FailureThreshold )
2021-05-15 21:31:32 -04:00
}
2021-10-23 16:47:12 -04:00
if config . Endpoints [ 0 ]. Alerts [ 1 ]. SuccessThreshold != 5 {
t . Errorf ( "The success threshold of the alert should've been %d, but it was %d" , 5 , config . Endpoints [ 0 ]. Alerts [ 1 ]. SuccessThreshold )
2021-05-15 21:31:32 -04:00
}
2021-10-23 16:47:12 -04:00
if config . Endpoints [ 0 ]. Alerts [ 2 ]. Type != alert . TypeMattermost {
t . Errorf ( "The type of the alert should've been %s, but it was %s" , alert . TypeMattermost , config . Endpoints [ 0 ]. Alerts [ 2 ]. Type )
2021-05-15 21:31:32 -04:00
}
2021-10-23 16:47:12 -04:00
if ! config . Endpoints [ 0 ]. Alerts [ 2 ]. IsEnabled () {
2021-05-15 21:31:32 -04:00
t . Error ( "The alert should've been enabled" )
}
2021-10-23 16:47:12 -04:00
if config . Endpoints [ 0 ]. Alerts [ 2 ]. FailureThreshold != 3 {
t . Errorf ( "The default failure threshold of the alert should've been %d, but it was %d" , 3 , config . Endpoints [ 0 ]. Alerts [ 2 ]. FailureThreshold )
2021-05-15 21:31:32 -04:00
}
2021-10-23 16:47:12 -04:00
if config . Endpoints [ 0 ]. Alerts [ 2 ]. SuccessThreshold != 2 {
t . Errorf ( "The default success threshold of the alert should've been %d, but it was %d" , 2 , config . Endpoints [ 0 ]. Alerts [ 2 ]. SuccessThreshold )
2021-05-15 21:31:32 -04:00
}
2021-10-23 16:47:12 -04:00
if config . Endpoints [ 0 ]. Alerts [ 3 ]. Type != alert . TypeMessagebird {
t . Errorf ( "The type of the alert should've been %s, but it was %s" , alert . TypeMessagebird , config . Endpoints [ 0 ]. Alerts [ 3 ]. Type )
2021-05-15 21:31:32 -04:00
}
2021-10-23 16:47:12 -04:00
if config . Endpoints [ 0 ]. Alerts [ 3 ]. IsEnabled () {
2021-05-15 21:31:32 -04:00
t . Error ( "The alert should've been disabled" )
}
2021-10-23 16:47:12 -04:00
if config . Endpoints [ 0 ]. Alerts [ 4 ]. Type != alert . TypeDiscord {
t . Errorf ( "The type of the alert should've been %s, but it was %s" , alert . TypeDiscord , config . Endpoints [ 0 ]. Alerts [ 4 ]. Type )
2021-05-15 21:31:32 -04:00
}
2021-10-23 16:47:12 -04:00
if ! config . Endpoints [ 0 ]. Alerts [ 4 ]. IsEnabled () {
2021-05-15 21:31:32 -04:00
t . Error ( "The alert should've been enabled" )
}
2021-10-23 16:47:12 -04:00
if config . Endpoints [ 0 ]. Alerts [ 4 ]. FailureThreshold != 10 {
t . Errorf ( "The failure threshold of the alert should've been %d, but it was %d" , 10 , config . Endpoints [ 0 ]. Alerts [ 4 ]. FailureThreshold )
2021-05-15 21:31:32 -04:00
}
2021-10-23 16:47:12 -04:00
if config . Endpoints [ 0 ]. Alerts [ 4 ]. SuccessThreshold != 2 {
t . Errorf ( "The default success threshold of the alert should've been %d, but it was %d" , 2 , config . Endpoints [ 0 ]. Alerts [ 4 ]. SuccessThreshold )
2021-05-15 21:31:32 -04:00
}
2021-10-23 16:47:12 -04:00
if config . Endpoints [ 0 ]. Alerts [ 5 ]. Type != alert . TypeTelegram {
t . Errorf ( "The type of the alert should've been %s, but it was %s" , alert . TypeTelegram , config . Endpoints [ 0 ]. Alerts [ 5 ]. Type )
2021-05-15 21:31:32 -04:00
}
2021-10-23 16:47:12 -04:00
if ! config . Endpoints [ 0 ]. Alerts [ 5 ]. IsEnabled () {
2021-05-15 21:31:32 -04:00
t . Error ( "The alert should've been enabled" )
}
2021-10-23 16:47:12 -04:00
if config . Endpoints [ 0 ]. Alerts [ 5 ]. FailureThreshold != 3 {
t . Errorf ( "The default failure threshold of the alert should've been %d, but it was %d" , 3 , config . Endpoints [ 0 ]. Alerts [ 5 ]. FailureThreshold )
2021-05-15 21:31:32 -04:00
}
2021-10-23 16:47:12 -04:00
if config . Endpoints [ 0 ]. Alerts [ 5 ]. SuccessThreshold != 2 {
t . Errorf ( "The default success threshold of the alert should've been %d, but it was %d" , 2 , config . Endpoints [ 0 ]. Alerts [ 5 ]. SuccessThreshold )
2021-05-15 21:31:32 -04:00
}
2021-10-23 16:47:12 -04:00
if config . Endpoints [ 0 ]. Alerts [ 6 ]. Type != alert . TypeTwilio {
t . Errorf ( "The type of the alert should've been %s, but it was %s" , alert . TypeTwilio , config . Endpoints [ 0 ]. Alerts [ 6 ]. Type )
2021-05-15 21:31:32 -04:00
}
2021-10-23 16:47:12 -04:00
if ! config . Endpoints [ 0 ]. Alerts [ 6 ]. IsEnabled () {
2021-05-15 21:31:32 -04:00
t . Error ( "The alert should've been enabled" )
}
2021-10-23 16:47:12 -04:00
if config . Endpoints [ 0 ]. Alerts [ 6 ]. FailureThreshold != 12 {
t . Errorf ( "The default failure threshold of the alert should've been %d, but it was %d" , 12 , config . Endpoints [ 0 ]. Alerts [ 6 ]. FailureThreshold )
2021-05-15 21:31:32 -04:00
}
2021-10-23 16:47:12 -04:00
if config . Endpoints [ 0 ]. Alerts [ 6 ]. SuccessThreshold != 15 {
t . Errorf ( "The default success threshold of the alert should've been %d, but it was %d" , 15 , config . Endpoints [ 0 ]. Alerts [ 6 ]. SuccessThreshold )
2021-05-15 21:31:32 -04:00
}
2021-07-28 14:15:56 +02:00
2021-10-23 16:47:12 -04:00
if config . Endpoints [ 0 ]. Alerts [ 7 ]. Type != alert . TypeTeams {
t . Errorf ( "The type of the alert should've been %s, but it was %s" , alert . TypeTeams , config . Endpoints [ 0 ]. Alerts [ 7 ]. Type )
2021-07-28 14:15:56 +02:00
}
2021-10-23 16:47:12 -04:00
if ! config . Endpoints [ 0 ]. Alerts [ 7 ]. IsEnabled () {
2021-07-28 14:15:56 +02:00
t . Error ( "The alert should've been enabled" )
}
2021-10-23 16:47:12 -04:00
if config . Endpoints [ 0 ]. Alerts [ 7 ]. FailureThreshold != 3 {
t . Errorf ( "The default failure threshold of the alert should've been %d, but it was %d" , 3 , config . Endpoints [ 0 ]. Alerts [ 7 ]. FailureThreshold )
2021-07-28 14:15:56 +02:00
}
2021-10-23 16:47:12 -04:00
if config . Endpoints [ 0 ]. Alerts [ 7 ]. SuccessThreshold != 2 {
t . Errorf ( "The default success threshold of the alert should've been %d, but it was %d" , 2 , config . Endpoints [ 0 ]. Alerts [ 7 ]. SuccessThreshold )
2021-07-28 14:15:56 +02:00
}
2023-01-29 23:32:16 +01:00
if config . Endpoints [ 0 ]. Alerts [ 8 ]. Type != alert . TypePushover {
t . Errorf ( "The type of the alert should've been %s, but it was %s" , alert . TypePushover , config . Endpoints [ 0 ]. Alerts [ 8 ]. Type )
}
if ! config . Endpoints [ 0 ]. Alerts [ 8 ]. IsEnabled () {
t . Error ( "The alert should've been enabled" )
}
2021-05-15 21:31:32 -04:00
}
func TestParseAndValidateConfigBytesWithAlertingAndDefaultAlert ( t * testing . T ) {
config , err := parseAndValidateConfigBytes ([] byte ( `
alerting:
slack:
webhook-url: "http://example.com"
default-alert:
enabled: true
discord:
webhook-url: "http://example.org"
default-alert:
enabled: true
failure-threshold: 10
2024-05-19 18:31:01 -04:00
success-threshold: 15
2021-05-15 21:31:32 -04:00
pagerduty:
integration-key: "00000000000000000000000000000000"
default-alert:
enabled: true
description: default description
failure-threshold: 7
success-threshold: 5
2023-01-29 23:32:16 +01:00
pushover:
application-token: "000000000000000000000000000000"
user-key: "000000000000000000000000000000"
default-alert:
enabled: true
description: default description
failure-threshold: 5
success-threshold: 3
2021-05-15 21:31:32 -04:00
mattermost:
webhook-url: "http://example.com"
default-alert:
enabled: true
messagebird:
access-key: "1"
originator: "31619191918"
recipients: "31619191919"
default-alert:
enabled: false
send-on-resolved: true
telegram:
token: 123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11
id: 0123456789
default-alert:
enabled: true
twilio:
sid: "1234"
token: "5678"
from: "+1-234-567-8901"
to: "+1-234-567-8901"
default-alert:
enabled: true
failure-threshold: 12
success-threshold: 15
2021-07-28 14:15:56 +02:00
teams:
webhook-url: "http://example.com"
default-alert:
enabled: true
2024-05-15 21:29:45 -04:00
email:
from: "from@example.com"
username: "from@example.com"
password: "hunter2"
host: "mail.example.com"
port: 587
to: "recipient1@example.com,recipient2@example.com"
client:
insecure: false
default-alert:
enabled: true
gotify:
server-url: "https://gotify.example"
token: "**************"
default-alert:
enabled: true
2021-05-15 21:31:32 -04:00
2024-05-19 18:31:01 -04:00
external-endpoints:
- name: ext-ep-test
group: core
token: potato
alerts:
- type: discord
2021-10-23 16:47:12 -04:00
endpoints:
2024-05-19 18:31:01 -04:00
- name: website
url: https://twin.sh/health
alerts:
- type: slack
- type: pagerduty
- type: mattermost
- type: messagebird
- type: discord
success-threshold: 8 # test endpoint alert override
- type: telegram
- type: twilio
- type: teams
- type: pushover
- type: email
- type: gotify
conditions:
- "[STATUS] == 200"
2020-08-22 14:15:21 -04:00
` ))
if err != nil {
2021-02-02 23:06:34 -05:00
t . Error ( "expected no error, got" , err . Error ())
2020-08-22 14:15:21 -04:00
}
if config == nil {
t . Fatal ( "Config shouldn't have been nil" )
}
if config . Metrics {
t . Error ( "Metrics should've been false by default" )
}
2021-03-04 21:26:17 -05:00
// Alerting providers
2020-08-22 14:15:21 -04:00
if config . Alerting == nil {
2020-09-24 19:52:59 -04:00
t . Fatal ( "config.Alerting shouldn't have been nil" )
}
2024-12-16 20:32:13 -05:00
if config . Alerting . Slack == nil || config . Alerting . Slack . Validate () != nil {
2020-09-24 19:52:59 -04:00
t . Fatal ( "Slack alerting config should've been valid" )
2020-08-22 14:15:21 -04:00
}
2021-05-15 22:09:58 -04:00
if config . Alerting . Slack . GetDefaultAlert () == nil {
t . Fatal ( "Slack.GetDefaultAlert() shouldn't have returned nil" )
}
2024-12-16 20:32:13 -05:00
if config . Alerting . Slack . DefaultConfig . WebhookURL != "http://example.com" {
t . Errorf ( "Slack webhook should've been %s, but was %s" , "http://example.com" , config . Alerting . Slack . DefaultConfig . WebhookURL )
2020-09-24 19:52:59 -04:00
}
2021-05-15 22:09:58 -04:00
2024-12-16 20:32:13 -05:00
if config . Alerting . PagerDuty == nil || config . Alerting . PagerDuty . Validate () != nil {
2020-09-24 19:52:59 -04:00
t . Fatal ( "PagerDuty alerting config should've been valid" )
}
2021-05-15 22:09:58 -04:00
if config . Alerting . PagerDuty . GetDefaultAlert () == nil {
t . Fatal ( "PagerDuty.GetDefaultAlert() shouldn't have returned nil" )
}
2024-12-16 20:32:13 -05:00
if config . Alerting . PagerDuty . DefaultConfig . IntegrationKey != "00000000000000000000000000000000" {
t . Errorf ( "PagerDuty integration key should've been %s, but was %s" , "00000000000000000000000000000000" , config . Alerting . PagerDuty . DefaultConfig . IntegrationKey )
2020-08-22 14:15:21 -04:00
}
2021-03-31 01:38:34 +02:00
2024-12-16 20:32:13 -05:00
if config . Alerting . Pushover == nil || config . Alerting . Pushover . Validate () != nil {
2023-01-29 23:32:16 +01:00
t . Fatal ( "Pushover alerting config should've been valid" )
}
if config . Alerting . Pushover . GetDefaultAlert () == nil {
t . Fatal ( "Pushover.GetDefaultAlert() shouldn't have returned nil" )
}
2024-12-16 20:32:13 -05:00
if config . Alerting . Pushover . DefaultConfig . ApplicationToken != "000000000000000000000000000000" {
t . Errorf ( "Pushover application token should've been %s, but was %s" , "000000000000000000000000000000" , config . Alerting . Pushover . DefaultConfig . ApplicationToken )
2023-01-29 23:32:16 +01:00
}
2024-12-16 20:32:13 -05:00
if config . Alerting . Pushover . DefaultConfig . UserKey != "000000000000000000000000000000" {
t . Errorf ( "Pushover user key should've been %s, but was %s" , "000000000000000000000000000000" , config . Alerting . Pushover . DefaultConfig . UserKey )
2023-01-29 23:32:16 +01:00
}
2024-12-16 20:32:13 -05:00
if config . Alerting . Mattermost == nil || config . Alerting . Mattermost . Validate () != nil {
2021-05-15 22:09:58 -04:00
t . Fatal ( "Mattermost alerting config should've been valid" )
}
if config . Alerting . Mattermost . GetDefaultAlert () == nil {
t . Fatal ( "Mattermost.GetDefaultAlert() shouldn't have returned nil" )
}
2024-12-16 20:32:13 -05:00
if config . Alerting . Messagebird == nil || config . Alerting . Messagebird . Validate () != nil {
2021-03-31 01:38:34 +02:00
t . Fatal ( "Messagebird alerting config should've been valid" )
}
2021-05-15 22:09:58 -04:00
if config . Alerting . Messagebird . GetDefaultAlert () == nil {
t . Fatal ( "Messagebird.GetDefaultAlert() shouldn't have returned nil" )
}
2024-12-16 20:32:13 -05:00
if config . Alerting . Messagebird . DefaultConfig . AccessKey != "1" {
t . Errorf ( "Messagebird access key should've been %s, but was %s" , "1" , config . Alerting . Messagebird . DefaultConfig . AccessKey )
2020-11-23 22:20:06 +01:00
}
2024-12-16 20:32:13 -05:00
if config . Alerting . Messagebird . DefaultConfig . Originator != "31619191918" {
t . Errorf ( "Messagebird originator field should've been %s, but was %s" , "31619191918" , config . Alerting . Messagebird . DefaultConfig . Originator )
2020-11-23 22:20:06 +01:00
}
2024-12-16 20:32:13 -05:00
if config . Alerting . Messagebird . DefaultConfig . Recipients != "31619191919" {
t . Errorf ( "Messagebird to recipients should've been %s, but was %s" , "31619191919" , config . Alerting . Messagebird . DefaultConfig . Recipients )
2020-11-23 22:20:06 +01:00
}
2021-03-31 01:38:34 +02:00
2024-12-16 20:32:13 -05:00
if config . Alerting . Discord == nil || config . Alerting . Discord . Validate () != nil {
2021-03-04 21:26:17 -05:00
t . Fatal ( "Discord alerting config should've been valid" )
}
2021-05-15 22:09:58 -04:00
if config . Alerting . Discord . GetDefaultAlert () == nil {
t . Fatal ( "Discord.GetDefaultAlert() shouldn't have returned nil" )
}
2024-05-19 18:31:01 -04:00
if config . Alerting . Discord . GetDefaultAlert (). FailureThreshold != 10 {
t . Errorf ( "Discord default alert failure threshold should've been %d, but was %d" , 10 , config . Alerting . Discord . GetDefaultAlert (). FailureThreshold )
}
if config . Alerting . Discord . GetDefaultAlert (). SuccessThreshold != 15 {
t . Errorf ( "Discord default alert success threshold should've been %d, but was %d" , 15 , config . Alerting . Discord . GetDefaultAlert (). SuccessThreshold )
}
2024-12-16 20:32:13 -05:00
if config . Alerting . Discord . DefaultConfig . WebhookURL != "http://example.org" {
t . Errorf ( "Discord webhook should've been %s, but was %s" , "http://example.org" , config . Alerting . Discord . DefaultConfig . WebhookURL )
2021-03-04 21:26:17 -05:00
}
2021-05-18 22:29:15 -04:00
if config . Alerting . GetAlertingProviderByAlertType ( alert . TypeDiscord ) != config . Alerting . Discord {
2021-05-15 22:09:58 -04:00
t . Error ( "expected discord configuration" )
}
2021-03-31 01:38:34 +02:00
2024-12-16 20:32:13 -05:00
if config . Alerting . Telegram == nil || config . Alerting . Telegram . Validate () != nil {
2021-05-15 22:09:58 -04:00
t . Fatal ( "Telegram alerting config should've been valid" )
}
if config . Alerting . Telegram . GetDefaultAlert () == nil {
t . Fatal ( "Telegram.GetDefaultAlert() shouldn't have returned nil" )
}
2024-12-16 20:32:13 -05:00
if config . Alerting . Telegram . DefaultConfig . Token != "123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11" {
t . Errorf ( "Telegram token should've been %s, but was %s" , "123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11" , config . Alerting . Telegram . DefaultConfig . Token )
2021-03-31 01:38:34 +02:00
}
2024-12-16 20:32:13 -05:00
if config . Alerting . Telegram . DefaultConfig . ID != "0123456789" {
t . Errorf ( "Telegram ID should've been %s, but was %s" , "012345689" , config . Alerting . Telegram . DefaultConfig . ID )
2021-03-31 01:38:34 +02:00
}
2021-05-15 22:09:58 -04:00
2024-12-16 20:32:13 -05:00
if config . Alerting . Twilio == nil || config . Alerting . Twilio . Validate () != nil {
2021-05-15 22:09:58 -04:00
t . Fatal ( "Twilio alerting config should've been valid" )
}
if config . Alerting . Twilio . GetDefaultAlert () == nil {
t . Fatal ( "Twilio.GetDefaultAlert() shouldn't have returned nil" )
2021-03-04 21:26:17 -05:00
}
2021-07-28 14:15:56 +02:00
2024-12-16 20:32:13 -05:00
if config . Alerting . Teams == nil || config . Alerting . Teams . Validate () != nil {
2021-07-28 14:15:56 +02:00
t . Fatal ( "Teams alerting config should've been valid" )
}
if config . Alerting . Teams . GetDefaultAlert () == nil {
t . Fatal ( "Teams.GetDefaultAlert() shouldn't have returned nil" )
}
2024-12-16 20:32:13 -05:00
if config . Alerting . Email == nil || config . Alerting . Email . Validate () != nil {
2024-05-15 21:29:45 -04:00
t . Fatal ( "Email alerting config should've been valid" )
}
if config . Alerting . Email . GetDefaultAlert () == nil {
t . Fatal ( "Email.GetDefaultAlert() shouldn't have returned nil" )
}
2024-12-16 20:32:13 -05:00
if config . Alerting . Email . DefaultConfig . From != "from@example.com" {
t . Errorf ( "Email from should've been %s, but was %s" , "from@example.com" , config . Alerting . Email . DefaultConfig . From )
2024-05-15 21:29:45 -04:00
}
2024-12-16 20:32:13 -05:00
if config . Alerting . Email . DefaultConfig . Username != "from@example.com" {
t . Errorf ( "Email username should've been %s, but was %s" , "from@example.com" , config . Alerting . Email . DefaultConfig . Username )
2024-05-15 21:29:45 -04:00
}
2024-12-16 20:32:13 -05:00
if config . Alerting . Email . DefaultConfig . Password != "hunter2" {
t . Errorf ( "Email password should've been %s, but was %s" , "hunter2" , config . Alerting . Email . DefaultConfig . Password )
2024-05-15 21:29:45 -04:00
}
2024-12-16 20:32:13 -05:00
if config . Alerting . Email . DefaultConfig . Host != "mail.example.com" {
t . Errorf ( "Email host should've been %s, but was %s" , "mail.example.com" , config . Alerting . Email . DefaultConfig . Host )
2024-05-15 21:29:45 -04:00
}
2024-12-16 20:32:13 -05:00
if config . Alerting . Email . DefaultConfig . Port != 587 {
t . Errorf ( "Email port should've been %d, but was %d" , 587 , config . Alerting . Email . DefaultConfig . Port )
2024-05-15 21:29:45 -04:00
}
2024-12-16 20:32:13 -05:00
if config . Alerting . Email . DefaultConfig . To != "recipient1@example.com,recipient2@example.com" {
t . Errorf ( "Email to should've been %s, but was %s" , "recipient1@example.com,recipient2@example.com" , config . Alerting . Email . DefaultConfig . To )
2024-05-15 21:29:45 -04:00
}
2024-12-16 20:32:13 -05:00
if config . Alerting . Email . DefaultConfig . ClientConfig == nil {
2024-05-15 21:29:45 -04:00
t . Fatal ( "Email client config should've been set" )
}
2024-12-16 20:32:13 -05:00
if config . Alerting . Email . DefaultConfig . ClientConfig . Insecure {
2024-05-15 21:29:45 -04:00
t . Error ( "Email client config should've been secure" )
}
2024-12-16 20:32:13 -05:00
if config . Alerting . Gotify == nil || config . Alerting . Gotify . Validate () != nil {
2024-05-15 21:29:45 -04:00
t . Fatal ( "Gotify alerting config should've been valid" )
}
if config . Alerting . Gotify . GetDefaultAlert () == nil {
t . Fatal ( "Gotify.GetDefaultAlert() shouldn't have returned nil" )
}
2024-12-16 20:32:13 -05:00
if config . Alerting . Gotify . DefaultConfig . ServerURL != "https://gotify.example" {
t . Errorf ( "Gotify server URL should've been %s, but was %s" , "https://gotify.example" , config . Alerting . Gotify . DefaultConfig . ServerURL )
2024-05-15 21:29:45 -04:00
}
2024-12-16 20:32:13 -05:00
if config . Alerting . Gotify . DefaultConfig . Token != "**************" {
t . Errorf ( "Gotify token should've been %s, but was %s" , "**************" , config . Alerting . Gotify . DefaultConfig . Token )
2024-05-15 21:29:45 -04:00
}
2024-05-19 18:31:01 -04:00
// External endpoints
if len ( config . ExternalEndpoints ) != 1 {
t . Error ( "There should've been 1 external endpoint" )
}
if config . ExternalEndpoints [ 0 ]. Alerts [ 0 ]. Type != alert . TypeDiscord {
t . Errorf ( "The type of the alert should've been %s, but it was %s" , alert . TypeDiscord , config . ExternalEndpoints [ 0 ]. Alerts [ 0 ]. Type )
}
if ! config . ExternalEndpoints [ 0 ]. Alerts [ 0 ]. IsEnabled () {
t . Error ( "The alert should've been enabled" )
}
if config . ExternalEndpoints [ 0 ]. Alerts [ 0 ]. FailureThreshold != 10 {
t . Errorf ( "The failure threshold of the alert should've been %d, but it was %d" , 10 , config . ExternalEndpoints [ 0 ]. Alerts [ 0 ]. FailureThreshold )
}
if config . ExternalEndpoints [ 0 ]. Alerts [ 0 ]. SuccessThreshold != 15 {
t . Errorf ( "The default success threshold of the alert should've been %d, but it was %d" , 15 , config . ExternalEndpoints [ 0 ]. Alerts [ 0 ]. SuccessThreshold )
}
2021-10-23 16:47:12 -04:00
// Endpoints
if len ( config . Endpoints ) != 1 {
t . Error ( "There should've been 1 endpoint" )
2020-08-22 14:15:21 -04:00
}
2021-10-23 16:47:12 -04:00
if config . Endpoints [ 0 ]. URL != "https://twin.sh/health" {
2021-09-18 12:42:11 -04:00
t . Errorf ( "URL should have been %s" , "https://twin.sh/health" )
2020-08-22 14:15:21 -04:00
}
2021-10-23 16:47:12 -04:00
if config . Endpoints [ 0 ]. Interval != 60 * time . Second {
2020-09-01 12:46:23 -04:00
t . Errorf ( "Interval should have been %s, because it is the default value" , 60 * time . Second )
2020-08-22 14:15:21 -04:00
}
2025-10-13 19:47:05 -05:00
if len ( config . Endpoints [ 0 ]. Alerts ) != 11 {
t . Fatalf ( "There should've been 11 alerts configured, got %d" , len ( config . Endpoints [ 0 ]. Alerts ))
2020-08-22 14:15:21 -04:00
}
2021-03-04 21:26:17 -05:00
2021-10-23 16:47:12 -04:00
if config . Endpoints [ 0 ]. Alerts [ 0 ]. Type != alert . TypeSlack {
t . Errorf ( "The type of the alert should've been %s, but it was %s" , alert . TypeSlack , config . Endpoints [ 0 ]. Alerts [ 0 ]. Type )
2020-08-22 14:15:21 -04:00
}
2021-10-23 16:47:12 -04:00
if ! config . Endpoints [ 0 ]. Alerts [ 0 ]. IsEnabled () {
2020-08-22 14:15:21 -04:00
t . Error ( "The alert should've been enabled" )
}
2021-10-23 16:47:12 -04:00
if config . Endpoints [ 0 ]. Alerts [ 0 ]. FailureThreshold != 3 {
t . Errorf ( "The default failure threshold of the alert should've been %d, but it was %d" , 3 , config . Endpoints [ 0 ]. Alerts [ 0 ]. FailureThreshold )
2020-10-21 21:56:07 -04:00
}
2021-10-23 16:47:12 -04:00
if config . Endpoints [ 0 ]. Alerts [ 0 ]. SuccessThreshold != 2 {
t . Errorf ( "The default success threshold of the alert should've been %d, but it was %d" , 2 , config . Endpoints [ 0 ]. Alerts [ 0 ]. SuccessThreshold )
2020-09-16 20:22:33 -04:00
}
2021-03-04 21:26:17 -05:00
2021-10-23 16:47:12 -04:00
if config . Endpoints [ 0 ]. Alerts [ 1 ]. Type != alert . TypePagerDuty {
t . Errorf ( "The type of the alert should've been %s, but it was %s" , alert . TypePagerDuty , config . Endpoints [ 0 ]. Alerts [ 1 ]. Type )
2021-03-04 21:26:17 -05:00
}
2021-10-23 16:47:12 -04:00
if config . Endpoints [ 0 ]. Alerts [ 1 ]. GetDescription () != "default description" {
t . Errorf ( "The description of the alert should've been %s, but it was %s" , "default description" , config . Endpoints [ 0 ]. Alerts [ 1 ]. GetDescription ())
2021-05-15 21:31:32 -04:00
}
2021-10-23 16:47:12 -04:00
if config . Endpoints [ 0 ]. Alerts [ 1 ]. FailureThreshold != 7 {
t . Errorf ( "The failure threshold of the alert should've been %d, but it was %d" , 7 , config . Endpoints [ 0 ]. Alerts [ 1 ]. FailureThreshold )
2021-03-04 21:26:17 -05:00
}
2021-10-23 16:47:12 -04:00
if config . Endpoints [ 0 ]. Alerts [ 1 ]. SuccessThreshold != 5 {
t . Errorf ( "The success threshold of the alert should've been %d, but it was %d" , 5 , config . Endpoints [ 0 ]. Alerts [ 1 ]. SuccessThreshold )
2020-08-22 14:15:21 -04:00
}
2021-03-04 21:26:17 -05:00
2021-10-23 16:47:12 -04:00
if config . Endpoints [ 0 ]. Alerts [ 2 ]. Type != alert . TypeMattermost {
t . Errorf ( "The type of the alert should've been %s, but it was %s" , alert . TypeMattermost , config . Endpoints [ 0 ]. Alerts [ 2 ]. Type )
2021-05-15 21:31:32 -04:00
}
2021-10-23 16:47:12 -04:00
if ! config . Endpoints [ 0 ]. Alerts [ 2 ]. IsEnabled () {
2021-05-15 21:31:32 -04:00
t . Error ( "The alert should've been enabled" )
}
2021-10-23 16:47:12 -04:00
if config . Endpoints [ 0 ]. Alerts [ 2 ]. FailureThreshold != 3 {
t . Errorf ( "The default failure threshold of the alert should've been %d, but it was %d" , 3 , config . Endpoints [ 0 ]. Alerts [ 2 ]. FailureThreshold )
2020-08-22 14:15:21 -04:00
}
2021-10-23 16:47:12 -04:00
if config . Endpoints [ 0 ]. Alerts [ 2 ]. SuccessThreshold != 2 {
t . Errorf ( "The default success threshold of the alert should've been %d, but it was %d" , 2 , config . Endpoints [ 0 ]. Alerts [ 2 ]. SuccessThreshold )
2021-05-15 21:31:32 -04:00
}
2021-10-23 16:47:12 -04:00
if config . Endpoints [ 0 ]. Alerts [ 3 ]. Type != alert . TypeMessagebird {
t . Errorf ( "The type of the alert should've been %s, but it was %s" , alert . TypeMessagebird , config . Endpoints [ 0 ]. Alerts [ 3 ]. Type )
2021-05-15 21:31:32 -04:00
}
2021-10-23 16:47:12 -04:00
if config . Endpoints [ 0 ]. Alerts [ 3 ]. IsEnabled () {
2021-03-04 21:26:17 -05:00
t . Error ( "The alert should've been disabled" )
2020-10-21 21:56:07 -04:00
}
2021-10-23 16:47:12 -04:00
if ! config . Endpoints [ 0 ]. Alerts [ 3 ]. IsSendingOnResolved () {
2021-05-15 21:31:32 -04:00
t . Error ( "The alert should be sending on resolve" )
}
2021-10-23 16:47:12 -04:00
if config . Endpoints [ 0 ]. Alerts [ 4 ]. Type != alert . TypeDiscord {
t . Errorf ( "The type of the alert should've been %s, but it was %s" , alert . TypeDiscord , config . Endpoints [ 0 ]. Alerts [ 4 ]. Type )
2021-05-15 21:31:32 -04:00
}
2021-10-23 16:47:12 -04:00
if ! config . Endpoints [ 0 ]. Alerts [ 4 ]. IsEnabled () {
2021-05-15 21:31:32 -04:00
t . Error ( "The alert should've been enabled" )
}
2021-10-23 16:47:12 -04:00
if config . Endpoints [ 0 ]. Alerts [ 4 ]. FailureThreshold != 10 {
t . Errorf ( "The failure threshold of the alert should've been %d, but it was %d" , 10 , config . Endpoints [ 0 ]. Alerts [ 4 ]. FailureThreshold )
2021-05-15 21:31:32 -04:00
}
2024-05-19 18:31:01 -04:00
if config . Endpoints [ 0 ]. Alerts [ 4 ]. SuccessThreshold != 8 {
t . Errorf ( "The default success threshold of the alert should've been %d because it was explicitly overriden, but it was %d" , 8 , config . Endpoints [ 0 ]. Alerts [ 4 ]. SuccessThreshold )
2021-05-15 21:31:32 -04:00
}
2021-03-04 21:26:17 -05:00
2021-10-23 16:47:12 -04:00
if config . Endpoints [ 0 ]. Alerts [ 5 ]. Type != alert . TypeTelegram {
t . Errorf ( "The type of the alert should've been %s, but it was %s" , alert . TypeTelegram , config . Endpoints [ 0 ]. Alerts [ 5 ]. Type )
2021-05-15 21:31:32 -04:00
}
2021-10-23 16:47:12 -04:00
if ! config . Endpoints [ 0 ]. Alerts [ 5 ]. IsEnabled () {
2021-05-15 21:31:32 -04:00
t . Error ( "The alert should've been enabled" )
}
2021-10-23 16:47:12 -04:00
if config . Endpoints [ 0 ]. Alerts [ 5 ]. FailureThreshold != 3 {
t . Errorf ( "The default failure threshold of the alert should've been %d, but it was %d" , 3 , config . Endpoints [ 0 ]. Alerts [ 5 ]. FailureThreshold )
2021-05-15 21:31:32 -04:00
}
2021-10-23 16:47:12 -04:00
if config . Endpoints [ 0 ]. Alerts [ 5 ]. SuccessThreshold != 2 {
t . Errorf ( "The default success threshold of the alert should've been %d, but it was %d" , 2 , config . Endpoints [ 0 ]. Alerts [ 5 ]. SuccessThreshold )
2021-05-15 21:31:32 -04:00
}
2021-10-23 16:47:12 -04:00
if config . Endpoints [ 0 ]. Alerts [ 6 ]. Type != alert . TypeTwilio {
t . Errorf ( "The type of the alert should've been %s, but it was %s" , alert . TypeTwilio , config . Endpoints [ 0 ]. Alerts [ 6 ]. Type )
2021-05-15 21:31:32 -04:00
}
2021-10-23 16:47:12 -04:00
if ! config . Endpoints [ 0 ]. Alerts [ 6 ]. IsEnabled () {
2021-05-15 21:31:32 -04:00
t . Error ( "The alert should've been enabled" )
}
2021-10-23 16:47:12 -04:00
if config . Endpoints [ 0 ]. Alerts [ 6 ]. FailureThreshold != 12 {
t . Errorf ( "The default failure threshold of the alert should've been %d, but it was %d" , 12 , config . Endpoints [ 0 ]. Alerts [ 6 ]. FailureThreshold )
2021-05-15 21:31:32 -04:00
}
2021-10-23 16:47:12 -04:00
if config . Endpoints [ 0 ]. Alerts [ 6 ]. SuccessThreshold != 15 {
t . Errorf ( "The default success threshold of the alert should've been %d, but it was %d" , 15 , config . Endpoints [ 0 ]. Alerts [ 6 ]. SuccessThreshold )
2021-05-15 21:31:32 -04:00
}
2021-07-28 14:15:56 +02:00
2021-10-23 16:47:12 -04:00
if config . Endpoints [ 0 ]. Alerts [ 7 ]. Type != alert . TypeTeams {
t . Errorf ( "The type of the alert should've been %s, but it was %s" , alert . TypeTeams , config . Endpoints [ 0 ]. Alerts [ 7 ]. Type )
2021-07-28 14:15:56 +02:00
}
2021-10-23 16:47:12 -04:00
if ! config . Endpoints [ 0 ]. Alerts [ 7 ]. IsEnabled () {
2021-07-28 14:15:56 +02:00
t . Error ( "The alert should've been enabled" )
}
2021-10-23 16:47:12 -04:00
if config . Endpoints [ 0 ]. Alerts [ 7 ]. FailureThreshold != 3 {
t . Errorf ( "The default failure threshold of the alert should've been %d, but it was %d" , 3 , config . Endpoints [ 0 ]. Alerts [ 7 ]. FailureThreshold )
2021-07-28 14:15:56 +02:00
}
2021-10-23 16:47:12 -04:00
if config . Endpoints [ 0 ]. Alerts [ 7 ]. SuccessThreshold != 2 {
t . Errorf ( "The default success threshold of the alert should've been %d, but it was %d" , 2 , config . Endpoints [ 0 ]. Alerts [ 7 ]. SuccessThreshold )
2021-07-28 14:15:56 +02:00
}
2023-01-29 23:32:16 +01:00
if config . Endpoints [ 0 ]. Alerts [ 8 ]. Type != alert . TypePushover {
t . Errorf ( "The type of the alert should've been %s, but it was %s" , alert . TypePushover , config . Endpoints [ 0 ]. Alerts [ 8 ]. Type )
}
if ! config . Endpoints [ 0 ]. Alerts [ 8 ]. IsEnabled () {
t . Error ( "The alert should've been enabled" )
}
if config . Endpoints [ 0 ]. Alerts [ 8 ]. FailureThreshold != 5 {
t . Errorf ( "The default failure threshold of the alert should've been %d, but it was %d" , 3 , config . Endpoints [ 0 ]. Alerts [ 8 ]. FailureThreshold )
}
if config . Endpoints [ 0 ]. Alerts [ 8 ]. SuccessThreshold != 3 {
t . Errorf ( "The default success threshold of the alert should've been %d, but it was %d" , 2 , config . Endpoints [ 0 ]. Alerts [ 8 ]. SuccessThreshold )
}
2025-10-13 19:47:05 -05:00
if config . Endpoints [ 0 ]. Alerts [ 9 ]. Type != alert . TypeEmail {
t . Errorf ( "The type of the alert should've been %s, but it was %s" , alert . TypeEmail , config . Endpoints [ 0 ]. Alerts [ 9 ]. Type )
2024-03-28 17:36:22 -05:00
}
if ! config . Endpoints [ 0 ]. Alerts [ 9 ]. IsEnabled () {
t . Error ( "The alert should've been enabled" )
}
2025-10-13 19:47:05 -05:00
if config . Endpoints [ 0 ]. Alerts [ 9 ]. FailureThreshold != 3 {
t . Errorf ( "The default failure threshold of the alert should've been %d, but it was %d" , 3 , config . Endpoints [ 0 ]. Alerts [ 9 ]. FailureThreshold )
2024-03-28 17:36:22 -05:00
}
2025-10-13 19:47:05 -05:00
if config . Endpoints [ 0 ]. Alerts [ 9 ]. SuccessThreshold != 2 {
t . Errorf ( "The default success threshold of the alert should've been %d, but it was %d" , 2 , config . Endpoints [ 0 ]. Alerts [ 9 ]. SuccessThreshold )
2024-05-15 21:29:45 -04:00
}
2025-10-13 19:47:05 -05:00
if config . Endpoints [ 0 ]. Alerts [ 10 ]. Type != alert . TypeGotify {
t . Errorf ( "The type of the alert should've been %s, but it was %s" , alert . TypeGotify , config . Endpoints [ 0 ]. Alerts [ 10 ]. Type )
2024-05-15 21:29:45 -04:00
}
if ! config . Endpoints [ 0 ]. Alerts [ 10 ]. IsEnabled () {
t . Error ( "The alert should've been enabled" )
}
if config . Endpoints [ 0 ]. Alerts [ 10 ]. FailureThreshold != 3 {
t . Errorf ( "The default failure threshold of the alert should've been %d, but it was %d" , 3 , config . Endpoints [ 0 ]. Alerts [ 10 ]. FailureThreshold )
}
if config . Endpoints [ 0 ]. Alerts [ 10 ]. SuccessThreshold != 2 {
t . Errorf ( "The default success threshold of the alert should've been %d, but it was %d" , 2 , config . Endpoints [ 0 ]. Alerts [ 10 ]. SuccessThreshold )
}
2021-05-15 21:31:32 -04:00
}
func TestParseAndValidateConfigBytesWithAlertingAndDefaultAlertAndMultipleAlertsOfSameTypeWithOverriddenParameters ( t * testing . T ) {
config , err := parseAndValidateConfigBytes ([] byte ( `
alerting:
slack:
2021-10-23 16:47:12 -04:00
webhook-url: "https://example.com"
2021-05-15 21:31:32 -04:00
default-alert:
enabled: true
description: "description"
2021-10-23 16:47:12 -04:00
endpoints:
2021-09-18 12:42:11 -04:00
- name: website
url: https://twin.sh/health
2021-05-15 21:31:32 -04:00
alerts:
- type: slack
failure-threshold: 10
- type: slack
failure-threshold: 20
description: "wow"
- type: slack
enabled: false
failure-threshold: 30
2024-12-16 20:32:13 -05:00
provider-override:
webhook-url: https://example.com
2021-05-15 21:31:32 -04:00
conditions:
- "[STATUS] == 200"
` ))
if err != nil {
t . Error ( "expected no error, got" , err . Error ())
}
if config == nil {
t . Fatal ( "Config shouldn't have been nil" )
}
// Alerting providers
if config . Alerting == nil {
t . Fatal ( "config.Alerting shouldn't have been nil" )
}
2024-12-16 20:32:13 -05:00
if config . Alerting . Slack == nil || config . Alerting . Slack . Validate () != nil {
2021-05-15 21:31:32 -04:00
t . Fatal ( "Slack alerting config should've been valid" )
}
2021-10-23 16:47:12 -04:00
// Endpoints
if len ( config . Endpoints ) != 1 {
t . Error ( "There should've been 2 endpoints" )
2021-05-15 21:31:32 -04:00
}
2021-10-23 16:47:12 -04:00
if config . Endpoints [ 0 ]. Alerts [ 0 ]. Type != alert . TypeSlack {
t . Errorf ( "The type of the alert should've been %s, but it was %s" , alert . TypeSlack , config . Endpoints [ 0 ]. Alerts [ 0 ]. Type )
2021-05-15 21:31:32 -04:00
}
2021-10-23 16:47:12 -04:00
if config . Endpoints [ 0 ]. Alerts [ 1 ]. Type != alert . TypeSlack {
t . Errorf ( "The type of the alert should've been %s, but it was %s" , alert . TypeSlack , config . Endpoints [ 0 ]. Alerts [ 1 ]. Type )
2021-05-15 21:31:32 -04:00
}
2021-10-23 16:47:12 -04:00
if config . Endpoints [ 0 ]. Alerts [ 2 ]. Type != alert . TypeSlack {
t . Errorf ( "The type of the alert should've been %s, but it was %s" , alert . TypeSlack , config . Endpoints [ 0 ]. Alerts [ 2 ]. Type )
2021-05-15 21:31:32 -04:00
}
2021-10-23 16:47:12 -04:00
if ! config . Endpoints [ 0 ]. Alerts [ 0 ]. IsEnabled () {
2021-05-15 21:31:32 -04:00
t . Error ( "The alert should've been enabled" )
}
2021-10-23 16:47:12 -04:00
if ! config . Endpoints [ 0 ]. Alerts [ 1 ]. IsEnabled () {
2021-05-15 21:31:32 -04:00
t . Error ( "The alert should've been enabled" )
}
2021-10-23 16:47:12 -04:00
if config . Endpoints [ 0 ]. Alerts [ 2 ]. IsEnabled () {
2021-05-15 21:31:32 -04:00
t . Error ( "The alert should've been disabled" )
}
2021-10-23 16:47:12 -04:00
if config . Endpoints [ 0 ]. Alerts [ 0 ]. GetDescription () != "description" {
t . Errorf ( "The description of the alert should've been %s, but it was %s" , "description" , config . Endpoints [ 0 ]. Alerts [ 0 ]. GetDescription ())
2021-05-15 21:31:32 -04:00
}
2021-10-23 16:47:12 -04:00
if config . Endpoints [ 0 ]. Alerts [ 1 ]. GetDescription () != "wow" {
t . Errorf ( "The description of the alert should've been %s, but it was %s" , "description" , config . Endpoints [ 0 ]. Alerts [ 1 ]. GetDescription ())
2021-05-15 21:31:32 -04:00
}
2021-10-23 16:47:12 -04:00
if config . Endpoints [ 0 ]. Alerts [ 2 ]. GetDescription () != "description" {
t . Errorf ( "The description of the alert should've been %s, but it was %s" , "description" , config . Endpoints [ 0 ]. Alerts [ 2 ]. GetDescription ())
2021-05-15 21:31:32 -04:00
}
2021-10-23 16:47:12 -04:00
if config . Endpoints [ 0 ]. Alerts [ 0 ]. FailureThreshold != 10 {
t . Errorf ( "The failure threshold of the alert should've been %d, but it was %d" , 10 , config . Endpoints [ 0 ]. Alerts [ 0 ]. FailureThreshold )
2020-08-22 14:15:21 -04:00
}
2021-10-23 16:47:12 -04:00
if config . Endpoints [ 0 ]. Alerts [ 1 ]. FailureThreshold != 20 {
t . Errorf ( "The failure threshold of the alert should've been %d, but it was %d" , 20 , config . Endpoints [ 0 ]. Alerts [ 1 ]. FailureThreshold )
2021-03-04 21:26:17 -05:00
}
2021-10-23 16:47:12 -04:00
if config . Endpoints [ 0 ]. Alerts [ 2 ]. FailureThreshold != 30 {
t . Errorf ( "The failure threshold of the alert should've been %d, but it was %d" , 30 , config . Endpoints [ 0 ]. Alerts [ 2 ]. FailureThreshold )
2020-11-23 22:20:06 +01:00
}
2020-08-22 14:15:21 -04:00
}
2020-09-24 19:52:59 -04:00
func TestParseAndValidateConfigBytesWithInvalidPagerDutyAlertingConfig ( t * testing . T ) {
config , err := parseAndValidateConfigBytes ([] byte ( `
alerting:
pagerduty:
integration-key: "INVALID_KEY"
2021-10-23 16:47:12 -04:00
endpoints:
2021-09-18 12:42:11 -04:00
- name: website
url: https://twin.sh/health
2020-10-21 21:56:07 -04:00
alerts:
- type: pagerduty
2020-09-24 19:52:59 -04:00
conditions:
- "[STATUS] == 200"
` ))
if err != nil {
2021-02-02 23:06:34 -05:00
t . Error ( "expected no error, got" , err . Error ())
2020-09-24 19:52:59 -04:00
}
if config == nil {
t . Fatal ( "Config shouldn't have been nil" )
}
if config . Alerting == nil {
t . Fatal ( "config.Alerting shouldn't have been nil" )
}
2022-12-15 20:54:38 -05:00
if config . Alerting . PagerDuty != nil {
t . Fatal ( "PagerDuty alerting config should've been set to nil, because its IsValid() method returned false and therefore alerting.Config.SetAlertingProviderToNil() should've been called" )
2020-09-24 19:52:59 -04:00
}
}
2024-11-13 23:54:00 -05:00
2023-01-29 23:32:16 +01:00
func TestParseAndValidateConfigBytesWithInvalidPushoverAlertingConfig ( t * testing . T ) {
config , err := parseAndValidateConfigBytes ([] byte ( `
alerting:
pushover:
application-token: "INVALID_TOKEN"
endpoints:
- name: website
url: https://twin.sh/health
alerts:
- type: pushover
conditions:
- "[STATUS] == 200"
` ))
if err != nil {
t . Error ( "expected no error, got" , err . Error ())
}
if config == nil {
t . Fatal ( "Config shouldn't have been nil" )
}
if config . Alerting == nil {
t . Fatal ( "config.Alerting shouldn't have been nil" )
}
if config . Alerting . Pushover != nil {
t . Fatal ( "Pushover alerting config should've been set to nil, because its IsValid() method returned false and therefore alerting.Config.SetAlertingProviderToNil() should've been called" )
}
}
2020-10-14 19:22:58 -04:00
2020-11-13 15:01:21 -05:00
func TestParseAndValidateConfigBytesWithCustomAlertingConfig ( t * testing . T ) {
config , err := parseAndValidateConfigBytes ([] byte ( `
alerting:
custom:
url: "https://example.com"
body: |
{
2021-10-23 16:47:12 -04:00
"text": "[ALERT_TRIGGERED_OR_RESOLVED]: [ENDPOINT_NAME] - [ALERT_DESCRIPTION]"
2020-11-13 15:01:21 -05:00
}
2021-10-23 16:47:12 -04:00
endpoints:
2021-09-18 12:42:11 -04:00
- name: website
url: https://twin.sh/health
2020-11-13 15:01:21 -05:00
alerts:
- type: custom
conditions:
- "[STATUS] == 200"
` ))
if err != nil {
2021-02-02 23:06:34 -05:00
t . Error ( "expected no error, got" , err . Error ())
2020-11-13 15:01:21 -05:00
}
if config == nil {
t . Fatal ( "Config shouldn't have been nil" )
}
if config . Alerting == nil {
t . Fatal ( "config.Alerting shouldn't have been nil" )
}
if config . Alerting . Custom == nil {
2023-01-29 23:32:16 +01:00
t . Fatal ( "Custom alerting config shouldn't have been nil" )
2020-11-13 15:01:21 -05:00
}
2024-12-16 20:32:13 -05:00
if err = config . Alerting . Custom . Validate (); err != nil {
2020-11-13 15:01:21 -05:00
t . Fatal ( "Custom alerting config should've been valid" )
}
2024-12-16 20:32:13 -05:00
cfg , _ := config . Alerting . Custom . GetConfig ( "" , & alert . Alert { ProviderOverride : map [ string ] any { "client" : map [ string ] any { "insecure" : true }}})
if config . Alerting . Custom . GetAlertStatePlaceholderValue ( cfg , true ) != "RESOLVED" {
t . Fatal ( "ALERT_TRIGGERED_OR_RESOLVED placeholder value for RESOLVED should've been 'RESOLVED', got" , config . Alerting . Custom . GetAlertStatePlaceholderValue ( cfg , true ))
2021-02-18 23:17:51 -05:00
}
2024-12-16 20:32:13 -05:00
if config . Alerting . Custom . GetAlertStatePlaceholderValue ( cfg , false ) != "TRIGGERED" {
t . Fatal ( "ALERT_TRIGGERED_OR_RESOLVED placeholder value for TRIGGERED should've been 'TRIGGERED', got" , config . Alerting . Custom . GetAlertStatePlaceholderValue ( cfg , false ))
2021-02-18 23:17:51 -05:00
}
2024-12-16 20:32:13 -05:00
if ! cfg . ClientConfig . Insecure {
t . Errorf ( "ClientConfig.Insecure should have been %v, got %v" , true , cfg . ClientConfig . Insecure )
2021-07-28 21:41:26 -04:00
}
2020-11-13 15:01:21 -05:00
}
2021-02-18 23:17:51 -05:00
func TestParseAndValidateConfigBytesWithCustomAlertingConfigAndCustomPlaceholderValues ( t * testing . T ) {
config , err := parseAndValidateConfigBytes ([] byte ( `
alerting:
custom:
placeholders:
ALERT_TRIGGERED_OR_RESOLVED:
TRIGGERED: "partial_outage"
RESOLVED: "operational"
url: "https://example.com"
insecure: true
2021-10-23 16:47:12 -04:00
body: "[ALERT_TRIGGERED_OR_RESOLVED]: [ENDPOINT_NAME] - [ALERT_DESCRIPTION]"
endpoints:
2021-09-18 12:42:11 -04:00
- name: website
url: https://twin.sh/health
2021-02-18 23:17:51 -05:00
alerts:
- type: custom
conditions:
- "[STATUS] == 200"
` ))
if err != nil {
t . Error ( "expected no error, got" , err . Error ())
}
if config == nil {
2024-12-16 20:32:13 -05:00
t . Fatal ( "DefaultConfig shouldn't have been nil" )
2021-02-18 23:17:51 -05:00
}
if config . Alerting == nil {
t . Fatal ( "config.Alerting shouldn't have been nil" )
}
if config . Alerting . Custom == nil {
2023-01-29 23:32:16 +01:00
t . Fatal ( "Custom alerting config shouldn't have been nil" )
2021-02-18 23:17:51 -05:00
}
2024-12-16 20:32:13 -05:00
if err = config . Alerting . Custom . Validate (); err != nil {
2021-02-18 23:17:51 -05:00
t . Fatal ( "Custom alerting config should've been valid" )
}
2024-12-16 20:32:13 -05:00
cfg , _ := config . Alerting . Custom . GetConfig ( "" , & alert . Alert {})
if config . Alerting . Custom . GetAlertStatePlaceholderValue ( cfg , true ) != "operational" {
2021-02-18 23:17:51 -05:00
t . Fatal ( "ALERT_TRIGGERED_OR_RESOLVED placeholder value for RESOLVED should've been 'operational'" )
}
2024-12-16 20:32:13 -05:00
if config . Alerting . Custom . GetAlertStatePlaceholderValue ( cfg , false ) != "partial_outage" {
2021-02-18 23:17:51 -05:00
t . Fatal ( "ALERT_TRIGGERED_OR_RESOLVED placeholder value for TRIGGERED should've been 'partial_outage'" )
}
}
func TestParseAndValidateConfigBytesWithCustomAlertingConfigAndOneCustomPlaceholderValue ( t * testing . T ) {
config , err := parseAndValidateConfigBytes ([] byte ( `
alerting:
custom:
placeholders:
ALERT_TRIGGERED_OR_RESOLVED:
TRIGGERED: "partial_outage"
url: "https://example.com"
2021-10-23 16:47:12 -04:00
body: "[ALERT_TRIGGERED_OR_RESOLVED]: [ENDPOINT_NAME] - [ALERT_DESCRIPTION]"
endpoints:
2021-09-18 12:42:11 -04:00
- name: website
url: https://twin.sh/health
2021-02-18 23:17:51 -05:00
alerts:
- type: custom
conditions:
- "[STATUS] == 200"
` ))
if err != nil {
t . Error ( "expected no error, got" , err . Error ())
}
if config == nil {
2024-12-16 20:32:13 -05:00
t . Fatal ( "DefaultConfig shouldn't have been nil" )
2021-02-18 23:17:51 -05:00
}
if config . Alerting == nil {
t . Fatal ( "config.Alerting shouldn't have been nil" )
}
if config . Alerting . Custom == nil {
2023-01-29 23:32:16 +01:00
t . Fatal ( "Custom alerting config shouldn't have been nil" )
2021-02-18 23:17:51 -05:00
}
2024-12-16 20:32:13 -05:00
if err := config . Alerting . Custom . Validate (); err != nil {
2021-02-18 23:17:51 -05:00
t . Fatal ( "Custom alerting config should've been valid" )
}
2024-12-16 20:32:13 -05:00
cfg , _ := config . Alerting . Custom . GetConfig ( "" , & alert . Alert {})
if config . Alerting . Custom . GetAlertStatePlaceholderValue ( cfg , true ) != "RESOLVED" {
2021-02-18 23:17:51 -05:00
t . Fatal ( "ALERT_TRIGGERED_OR_RESOLVED placeholder value for RESOLVED should've been 'RESOLVED'" )
}
2024-12-16 20:32:13 -05:00
if config . Alerting . Custom . GetAlertStatePlaceholderValue ( cfg , false ) != "partial_outage" {
2021-02-18 23:17:51 -05:00
t . Fatal ( "ALERT_TRIGGERED_OR_RESOLVED placeholder value for TRIGGERED should've been 'partial_outage'" )
}
2020-11-13 15:01:21 -05:00
}
2021-10-23 16:47:12 -04:00
func TestParseAndValidateConfigBytesWithInvalidEndpointName ( t * testing . T ) {
2021-07-15 22:07:30 -04:00
_ , err := parseAndValidateConfigBytes ([] byte ( `
2021-10-23 16:47:12 -04:00
endpoints:
2021-07-15 22:07:30 -04:00
- name: ""
2021-09-18 12:42:11 -04:00
url: https://twin.sh/health
2021-07-15 22:07:30 -04:00
conditions:
- "[STATUS] == 200"
` ))
2022-09-01 20:59:09 -04:00
if err == nil {
2021-07-15 22:07:30 -04:00
t . Error ( "should've returned an error" )
}
}
2024-05-15 21:29:45 -04:00
func TestParseAndValidateConfigBytesWithDuplicateEndpointName ( t * testing . T ) {
scenarios := [] struct {
name string
shouldError bool
config string
}{
{
name : "same-name-no-group" ,
shouldError : true ,
config : `
endpoints:
- name: ep1
url: https://twin.sh/health
conditions:
- "[STATUS] == 200"
- name: ep1
url: https://twin.sh/health
conditions:
- "[STATUS] == 200"` ,
},
{
name : "same-name-different-group" ,
shouldError : false ,
config : `
endpoints:
- name: ep1
url: https://twin.sh/health
conditions:
- "[STATUS] == 200"
- name: ep1
group: g1
url: https://twin.sh/health
conditions:
- "[STATUS] == 200"` ,
},
{
name : "same-name-same-group" ,
shouldError : true ,
config : `
endpoints:
- name: ep1
group: g1
url: https://twin.sh/health
conditions:
- "[STATUS] == 200"
- name: ep1
group: g1
url: https://twin.sh/health
conditions:
- "[STATUS] == 200"` ,
},
{
name : "same-name-different-endpoint-type" ,
shouldError : true ,
config : `
external-endpoints:
- name: ep1
token: "12345678"
endpoints:
- name: ep1
url: https://twin.sh/health
conditions:
- "[STATUS] == 200"` ,
},
{
name : "same-name-different-group-different-endpoint-type" ,
shouldError : false ,
config : `
external-endpoints:
- name: ep1
group: gr1
token: "12345678"
endpoints:
- name: ep1
url: https://twin.sh/health
conditions:
- "[STATUS] == 200"` ,
},
}
for _ , scenario := range scenarios {
t . Run ( scenario . name , func ( t * testing . T ) {
_ , err := parseAndValidateConfigBytes ([] byte ( scenario . config ))
if scenario . shouldError && err == nil {
t . Error ( "should've returned an error" )
} else if ! scenario . shouldError && err != nil {
t . Error ( "shouldn't have returned an error" )
}
})
}
}
2021-07-15 22:07:30 -04:00
func TestParseAndValidateConfigBytesWithInvalidStorageConfig ( t * testing . T ) {
_ , err := parseAndValidateConfigBytes ([] byte ( `
storage:
type: sqlite
2021-10-23 16:47:12 -04:00
endpoints:
2021-07-15 22:07:30 -04:00
- name: example
url: https://example.org
conditions:
- "[STATUS] == 200"
` ))
if err == nil {
t . Error ( "should've returned an error, because a file must be specified for a storage of type sqlite" )
}
}
func TestParseAndValidateConfigBytesWithInvalidYAML ( t * testing . T ) {
_ , err := parseAndValidateConfigBytes ([] byte ( `
storage:
invalid yaml
2021-10-23 16:47:12 -04:00
endpoints:
2021-07-15 22:07:30 -04:00
- name: example
url: https://example.org
conditions:
- "[STATUS] == 200"
` ))
if err == nil {
t . Error ( "should've returned an error" )
}
}
2020-10-14 19:22:58 -04:00
func TestParseAndValidateConfigBytesWithInvalidSecurityConfig ( t * testing . T ) {
2021-05-18 23:27:43 -04:00
_ , err := parseAndValidateConfigBytes ([] byte ( `
2020-10-14 19:22:58 -04:00
security:
basic:
username: "admin"
password-sha512: "invalid-sha512-hash"
2021-10-23 16:47:12 -04:00
endpoints:
2021-09-18 12:42:11 -04:00
- name: website
url: https://twin.sh/health
2020-10-14 19:22:58 -04:00
conditions:
- "[STATUS] == 200"
` ))
2021-05-18 23:27:43 -04:00
if err == nil {
2021-07-15 22:07:30 -04:00
t . Error ( "should've returned an error" )
2021-05-18 23:27:43 -04:00
}
2020-10-14 19:22:58 -04:00
}
func TestParseAndValidateConfigBytesWithValidSecurityConfig ( t * testing . T ) {
const expectedUsername = "admin"
2022-01-17 11:55:05 -05:00
const expectedPasswordHash = "JDJhJDEwJHRiMnRFakxWazZLdXBzRERQazB1TE8vckRLY05Yb1hSdnoxWU0yQ1FaYXZRSW1McmladDYu"
2024-11-13 23:54:00 -05:00
config , err := parseAndValidateConfigBytes ([] byte ( fmt . Sprintf ( `
2020-10-14 19:22:58 -04:00
security:
basic:
username: "%s"
2022-01-17 11:55:05 -05:00
password-bcrypt-base64: "%s"
2021-10-23 16:47:12 -04:00
endpoints:
2021-09-18 12:42:11 -04:00
- name: website
url: https://twin.sh/health
2020-10-14 19:22:58 -04:00
conditions:
- "[STATUS] == 200"
` , expectedUsername , expectedPasswordHash )))
if err != nil {
2021-02-02 23:06:34 -05:00
t . Error ( "expected no error, got" , err . Error ())
2020-10-14 19:22:58 -04:00
}
if config == nil {
2024-12-16 20:32:13 -05:00
t . Fatal ( "DefaultConfig shouldn't have been nil" )
2020-10-14 19:22:58 -04:00
}
if config . Security == nil {
t . Fatal ( "config.Security shouldn't have been nil" )
}
2025-09-20 07:29:25 -04:00
if ! config . Security . ValidateAndSetDefaults () {
2020-10-14 19:22:58 -04:00
t . Error ( "Security config should've been valid" )
}
if config . Security . Basic == nil {
t . Fatal ( "config.Security.Basic shouldn't have been nil" )
}
if config . Security . Basic . Username != expectedUsername {
t . Errorf ( "config.Security.Basic.Username should've been %s, but was %s" , expectedUsername , config . Security . Basic . Username )
}
2022-01-17 11:55:05 -05:00
if config . Security . Basic . PasswordBcryptHashBase64Encoded != expectedPasswordHash {
2022-06-14 23:48:28 -04:00
t . Errorf ( "config.Security.Basic.PasswordBcryptHashBase64Encoded should've been %s, but was %s" , expectedPasswordHash , config . Security . Basic . PasswordBcryptHashBase64Encoded )
2020-10-14 19:22:58 -04:00
}
}
2020-11-11 18:05:18 -05:00
2023-02-11 22:43:13 -05:00
func TestParseAndValidateConfigBytesWithLiteralDollarSign ( t * testing . T ) {
os . Setenv ( "GATUS_TestParseAndValidateConfigBytesWithLiteralDollarSign" , "whatever" )
config , err := parseAndValidateConfigBytes ([] byte ( `
endpoints:
- name: website
url: https://twin.sh/health
conditions:
- "[BODY] == $$GATUS_TestParseAndValidateConfigBytesWithLiteralDollarSign"
- "[BODY] == $GATUS_TestParseAndValidateConfigBytesWithLiteralDollarSign"
` ))
if err != nil {
t . Error ( "expected no error, got" , err . Error ())
}
if config == nil {
2024-12-16 20:32:13 -05:00
t . Fatal ( "DefaultConfig shouldn't have been nil" )
2023-02-11 22:43:13 -05:00
}
if config . Endpoints [ 0 ]. URL != "https://twin.sh/health" {
t . Errorf ( "URL should have been %s" , "https://twin.sh/health" )
}
if config . Endpoints [ 0 ]. Conditions [ 0 ] != "[BODY] == $GATUS_TestParseAndValidateConfigBytesWithLiteralDollarSign" {
t . Errorf ( "Condition should have been %s" , "[BODY] == $GATUS_TestParseAndValidateConfigBytesWithLiteralDollarSign" )
}
if config . Endpoints [ 0 ]. Conditions [ 1 ] != "[BODY] == whatever" {
t . Errorf ( "Condition should have been %s" , "[BODY] == whatever" )
}
}
func TestParseAndValidateConfigBytesWithNoEndpoints ( t * testing . T ) {
2020-11-11 18:05:18 -05:00
_ , err := parseAndValidateConfigBytes ([] byte ( `` ))
2025-09-16 16:56:36 -04:00
if ! errors . Is ( err , ErrNoEndpointOrSuiteInConfig ) {
t . Error ( "The error returned should have been of type ErrNoEndpointOrSuiteInConfig" )
2020-11-11 18:05:18 -05:00
}
}
2021-03-04 21:26:17 -05:00
func TestGetAlertingProviderByAlertType ( t * testing . T ) {
2021-05-18 22:29:15 -04:00
alertingConfig := & alerting . Config {
2024-12-16 20:32:13 -05:00
AWSSimpleEmailService : & awsses . AlertProvider {},
2025-12-27 06:09:28 +05:30
ClickUp : & clickup . AlertProvider {},
2024-12-16 20:32:13 -05:00
Custom : & custom . AlertProvider {},
feat(alerting): Add new providers for Datadog, IFTTT, Line, NewRelic, Plivo, RocketChat, SendGrid, Signal, SIGNL4, Splunk, Squadcast, Vonage, Webex and Zapier ( #1224 )
2025-08-25 13:22:17 -04:00
Datadog : & datadog . AlertProvider {},
2024-12-16 20:32:13 -05:00
Discord : & discord . AlertProvider {},
Email : & email . AlertProvider {},
Gitea : & gitea . AlertProvider {},
GitHub : & github . AlertProvider {},
GitLab : & gitlab . AlertProvider {},
GoogleChat : & googlechat . AlertProvider {},
Gotify : & gotify . AlertProvider {},
feat(alerting): Add new providers for Datadog, IFTTT, Line, NewRelic, Plivo, RocketChat, SendGrid, Signal, SIGNL4, Splunk, Squadcast, Vonage, Webex and Zapier ( #1224 )
2025-08-25 13:22:17 -04:00
HomeAssistant : & homeassistant . AlertProvider {},
IFTTT : & ifttt . AlertProvider {},
Ilert : & ilert . AlertProvider {},
IncidentIO : & incidentio . AlertProvider {},
Line : & line . AlertProvider {},
2024-12-16 20:32:13 -05:00
Matrix : & matrix . AlertProvider {},
Mattermost : & mattermost . AlertProvider {},
Messagebird : & messagebird . AlertProvider {},
feat(alerting): Add new providers for Datadog, IFTTT, Line, NewRelic, Plivo, RocketChat, SendGrid, Signal, SIGNL4, Splunk, Squadcast, Vonage, Webex and Zapier ( #1224 )
2025-08-25 13:22:17 -04:00
NewRelic : & newrelic . AlertProvider {},
2024-12-16 20:32:13 -05:00
Ntfy : & ntfy . AlertProvider {},
Opsgenie : & opsgenie . AlertProvider {},
PagerDuty : & pagerduty . AlertProvider {},
feat(alerting): Add new providers for Datadog, IFTTT, Line, NewRelic, Plivo, RocketChat, SendGrid, Signal, SIGNL4, Splunk, Squadcast, Vonage, Webex and Zapier ( #1224 )
2025-08-25 13:22:17 -04:00
Plivo : & plivo . AlertProvider {},
2024-12-16 20:32:13 -05:00
Pushover : & pushover . AlertProvider {},
feat(alerting): Add new providers for Datadog, IFTTT, Line, NewRelic, Plivo, RocketChat, SendGrid, Signal, SIGNL4, Splunk, Squadcast, Vonage, Webex and Zapier ( #1224 )
2025-08-25 13:22:17 -04:00
RocketChat : & rocketchat . AlertProvider {},
SendGrid : & sendgrid . AlertProvider {},
Signal : & signal . AlertProvider {},
SIGNL4 : & signl4 . AlertProvider {},
2024-12-16 20:32:13 -05:00
Slack : & slack . AlertProvider {},
feat(alerting): Add new providers for Datadog, IFTTT, Line, NewRelic, Plivo, RocketChat, SendGrid, Signal, SIGNL4, Splunk, Squadcast, Vonage, Webex and Zapier ( #1224 )
2025-08-25 13:22:17 -04:00
Splunk : & splunk . AlertProvider {},
Squadcast : & squadcast . AlertProvider {},
2024-12-16 20:32:13 -05:00
Telegram : & telegram . AlertProvider {},
Teams : & teams . AlertProvider {},
TeamsWorkflows : & teamsworkflows . AlertProvider {},
Twilio : & twilio . AlertProvider {},
feat(alerting): Add new providers for Datadog, IFTTT, Line, NewRelic, Plivo, RocketChat, SendGrid, Signal, SIGNL4, Splunk, Squadcast, Vonage, Webex and Zapier ( #1224 )
2025-08-25 13:22:17 -04:00
Vonage : & vonage . AlertProvider {},
Webex : & webex . AlertProvider {},
Zapier : & zapier . AlertProvider {},
2024-12-16 20:32:13 -05:00
Zulip : & zulip . AlertProvider {},
2021-05-18 22:29:15 -04:00
}
2022-12-06 01:27:31 -05:00
scenarios := [] struct {
alertType alert . Type
expected provider . AlertProvider
}{
2024-12-16 20:32:13 -05:00
{ alertType : alert . TypeAWSSES , expected : alertingConfig . AWSSimpleEmailService },
2025-12-27 06:09:28 +05:30
{ alertType : alert . TypeClickUp , expected : alertingConfig . ClickUp },
2022-12-06 01:27:31 -05:00
{ alertType : alert . TypeCustom , expected : alertingConfig . Custom },
feat(alerting): Add new providers for Datadog, IFTTT, Line, NewRelic, Plivo, RocketChat, SendGrid, Signal, SIGNL4, Splunk, Squadcast, Vonage, Webex and Zapier ( #1224 )
2025-08-25 13:22:17 -04:00
{ alertType : alert . TypeDatadog , expected : alertingConfig . Datadog },
2022-12-06 01:27:31 -05:00
{ alertType : alert . TypeDiscord , expected : alertingConfig . Discord },
{ alertType : alert . TypeEmail , expected : alertingConfig . Email },
2024-12-16 20:32:13 -05:00
{ alertType : alert . TypeGitea , expected : alertingConfig . Gitea },
2023-01-08 17:53:37 -05:00
{ alertType : alert . TypeGitHub , expected : alertingConfig . GitHub },
2024-12-16 20:32:13 -05:00
{ alertType : alert . TypeGitLab , expected : alertingConfig . GitLab },
2022-12-06 01:27:31 -05:00
{ alertType : alert . TypeGoogleChat , expected : alertingConfig . GoogleChat },
2024-05-15 21:29:45 -04:00
{ alertType : alert . TypeGotify , expected : alertingConfig . Gotify },
feat(alerting): Add new providers for Datadog, IFTTT, Line, NewRelic, Plivo, RocketChat, SendGrid, Signal, SIGNL4, Splunk, Squadcast, Vonage, Webex and Zapier ( #1224 )
2025-08-25 13:22:17 -04:00
{ alertType : alert . TypeHomeAssistant , expected : alertingConfig . HomeAssistant },
{ alertType : alert . TypeIFTTT , expected : alertingConfig . IFTTT },
{ alertType : alert . TypeIlert , expected : alertingConfig . Ilert },
{ alertType : alert . TypeIncidentIO , expected : alertingConfig . IncidentIO },
{ alertType : alert . TypeLine , expected : alertingConfig . Line },
2022-12-06 01:27:31 -05:00
{ alertType : alert . TypeMatrix , expected : alertingConfig . Matrix },
{ alertType : alert . TypeMattermost , expected : alertingConfig . Mattermost },
{ alertType : alert . TypeMessagebird , expected : alertingConfig . Messagebird },
feat(alerting): Add new providers for Datadog, IFTTT, Line, NewRelic, Plivo, RocketChat, SendGrid, Signal, SIGNL4, Splunk, Squadcast, Vonage, Webex and Zapier ( #1224 )
2025-08-25 13:22:17 -04:00
{ alertType : alert . TypeNewRelic , expected : alertingConfig . NewRelic },
2022-12-06 01:27:31 -05:00
{ alertType : alert . TypeNtfy , expected : alertingConfig . Ntfy },
{ alertType : alert . TypeOpsgenie , expected : alertingConfig . Opsgenie },
{ alertType : alert . TypePagerDuty , expected : alertingConfig . PagerDuty },
feat(alerting): Add new providers for Datadog, IFTTT, Line, NewRelic, Plivo, RocketChat, SendGrid, Signal, SIGNL4, Splunk, Squadcast, Vonage, Webex and Zapier ( #1224 )
2025-08-25 13:22:17 -04:00
{ alertType : alert . TypePlivo , expected : alertingConfig . Plivo },
2023-01-29 23:32:16 +01:00
{ alertType : alert . TypePushover , expected : alertingConfig . Pushover },
feat(alerting): Add new providers for Datadog, IFTTT, Line, NewRelic, Plivo, RocketChat, SendGrid, Signal, SIGNL4, Splunk, Squadcast, Vonage, Webex and Zapier ( #1224 )
2025-08-25 13:22:17 -04:00
{ alertType : alert . TypeRocketChat , expected : alertingConfig . RocketChat },
{ alertType : alert . TypeSendGrid , expected : alertingConfig . SendGrid },
{ alertType : alert . TypeSignal , expected : alertingConfig . Signal },
{ alertType : alert . TypeSIGNL4 , expected : alertingConfig . SIGNL4 },
2022-12-06 01:27:31 -05:00
{ alertType : alert . TypeSlack , expected : alertingConfig . Slack },
feat(alerting): Add new providers for Datadog, IFTTT, Line, NewRelic, Plivo, RocketChat, SendGrid, Signal, SIGNL4, Splunk, Squadcast, Vonage, Webex and Zapier ( #1224 )
2025-08-25 13:22:17 -04:00
{ alertType : alert . TypeSplunk , expected : alertingConfig . Splunk },
{ alertType : alert . TypeSquadcast , expected : alertingConfig . Squadcast },
2022-12-06 01:27:31 -05:00
{ alertType : alert . TypeTelegram , expected : alertingConfig . Telegram },
{ alertType : alert . TypeTeams , expected : alertingConfig . Teams },
2024-11-13 23:54:00 -05:00
{ alertType : alert . TypeTeamsWorkflows , expected : alertingConfig . TeamsWorkflows },
2024-12-16 20:32:13 -05:00
{ alertType : alert . TypeTwilio , expected : alertingConfig . Twilio },
feat(alerting): Add new providers for Datadog, IFTTT, Line, NewRelic, Plivo, RocketChat, SendGrid, Signal, SIGNL4, Splunk, Squadcast, Vonage, Webex and Zapier ( #1224 )
2025-08-25 13:22:17 -04:00
{ alertType : alert . TypeVonage , expected : alertingConfig . Vonage },
{ alertType : alert . TypeWebex , expected : alertingConfig . Webex },
{ alertType : alert . TypeZapier , expected : alertingConfig . Zapier },
2024-12-16 20:32:13 -05:00
{ alertType : alert . TypeZulip , expected : alertingConfig . Zulip },
2022-12-06 01:27:31 -05:00
}
for _ , scenario := range scenarios {
t . Run ( string ( scenario . alertType ), func ( t * testing . T ) {
if alertingConfig . GetAlertingProviderByAlertType ( scenario . alertType ) != scenario . expected {
t . Errorf ( "expected %s configuration" , scenario . alertType )
}
})
2021-10-23 16:47:12 -04:00
}
}
2025-08-06 00:26:50 +08:00
func TestConfig_GetUniqueExtraMetricLabels ( t * testing . T ) {
tests := [] struct {
name string
config * Config
expected [] string
}{
{
name : "no-endpoints" ,
config : & Config {
Endpoints : [] * endpoint . Endpoint {},
},
expected : [] string {},
},
{
name : "single-endpoint-no-labels" ,
config : & Config {
Endpoints : [] * endpoint . Endpoint {
{
Name : "endpoint1" ,
URL : "https://example.com" ,
},
},
},
expected : [] string {},
},
{
name : "single-endpoint-with-labels" ,
config : & Config {
Endpoints : [] * endpoint . Endpoint {
{
Name : "endpoint1" ,
URL : "https://example.com" ,
Enabled : toPtr ( true ),
ExtraLabels : map [ string ] string {
"env" : "production" ,
"team" : "backend" ,
},
},
},
},
expected : [] string { "env" , "team" },
},
{
name : "multiple-endpoints-with-labels" ,
config : & Config {
Endpoints : [] * endpoint . Endpoint {
{
Name : "endpoint1" ,
URL : "https://example.com" ,
Enabled : toPtr ( true ),
ExtraLabels : map [ string ] string {
"env" : "production" ,
"team" : "backend" ,
"module" : "auth" ,
},
},
{
Name : "endpoint2" ,
URL : "https://example.org" ,
Enabled : toPtr ( true ),
ExtraLabels : map [ string ] string {
"env" : "staging" ,
"team" : "frontend" ,
},
},
},
},
expected : [] string { "env" , "team" , "module" },
},
{
name : "multiple-endpoints-with-some-disabled" ,
config : & Config {
Endpoints : [] * endpoint . Endpoint {
{
Name : "endpoint1" ,
URL : "https://example.com" ,
Enabled : toPtr ( true ),
ExtraLabels : map [ string ] string {
"env" : "production" ,
"team" : "backend" ,
},
},
{
Name : "endpoint2" ,
URL : "https://example.org" ,
Enabled : toPtr ( false ),
ExtraLabels : map [ string ] string {
"module" : "auth" ,
},
},
},
},
expected : [] string { "env" , "team" },
},
}
for _ , tt := range tests {
t . Run ( tt . name , func ( t * testing . T ) {
labels := tt . config . GetUniqueExtraMetricLabels ()
if len ( labels ) != len ( tt . expected ) {
t . Errorf ( "expected %d labels, got %d" , len ( tt . expected ), len ( labels ))
}
for _ , label := range tt . expected {
2026-02-09 01:08:59 +01:00
if ! slices . Contains ( labels , label ) {
2025-08-06 00:26:50 +08:00
t . Errorf ( "expected label %s to be present" , label )
}
}
})
}
}
2025-09-05 15:39:12 -04:00
func TestParseAndValidateConfigBytesWithDuplicateKeysAcrossEntityTypes ( t * testing . T ) {
scenarios := [] struct {
name string
shouldError bool
expectedErr string
config string
}{
{
name : "endpoint-suite-same-key" ,
shouldError : true ,
expectedErr : "duplicate key 'backend_test-api': suite 'backend_test-api' conflicts with endpoint 'backend_test-api'" ,
config : `
endpoints:
- name: test-api
group: backend
url: https://example.com/api
conditions:
- "[STATUS] == 200"
suites:
- name: test-api
group: backend
interval: 30s
endpoints:
- name: step1
url: https://example.com/test
conditions:
- "[STATUS] == 200"` ,
},
{
name : "endpoint-suite-different-keys" ,
shouldError : false ,
config : `
endpoints:
- name: api-service
group: backend
url: https://example.com/api
conditions:
- "[STATUS] == 200"
suites:
- name: integration-tests
group: testing
interval: 30s
endpoints:
- name: step1
url: https://example.com/test
conditions:
- "[STATUS] == 200"` ,
},
{
name : "endpoint-external-endpoint-suite-unique-keys" ,
shouldError : false ,
config : `
endpoints:
- name: api-service
group: backend
url: https://example.com/api
conditions:
- "[STATUS] == 200"
external-endpoints:
- name: monitoring-agent
group: infrastructure
token: "secret-token"
heartbeat:
interval: 5m
suites:
- name: integration-tests
group: testing
interval: 30s
endpoints:
- name: step1
url: https://example.com/test
conditions:
- "[STATUS] == 200"` ,
},
{
name : "suite-with-same-key-as-external-endpoint" ,
shouldError : true ,
expectedErr : "duplicate key 'monitoring_health-check': suite 'monitoring_health-check' conflicts with external endpoint 'monitoring_health-check'" ,
config : `
endpoints:
- name: dummy
url: https://example.com/dummy
conditions:
- "[STATUS] == 200"
external-endpoints:
- name: health-check
group: monitoring
token: "secret-token"
heartbeat:
interval: 5m
suites:
- name: health-check
group: monitoring
interval: 30s
endpoints:
- name: step1
url: https://example.com/test
conditions:
- "[STATUS] == 200"` ,
},
{
name : "endpoint-with-same-name-as-suite-endpoint-different-groups" ,
shouldError : false ,
config : `
endpoints:
- name: api-health
group: backend
url: https://example.com/health
conditions:
- "[STATUS] == 200"
suites:
- name: integration-suite
group: testing
interval: 30s
endpoints:
- name: api-health
url: https://example.com/api/health
conditions:
- "[STATUS] == 200"` ,
},
{
name : "endpoint-conflicting-with-suite-endpoint" ,
shouldError : true ,
expectedErr : "duplicate key 'backend_api-health': endpoint 'backend_api-health' in suite 'backend_integration-suite' conflicts with endpoint 'backend_api-health'" ,
config : `
endpoints:
- name: api-health
group: backend
url: https://example.com/health
conditions:
- "[STATUS] == 200"
suites:
- name: integration-suite
group: backend
interval: 30s
endpoints:
- name: api-health
url: https://example.com/api/health
conditions:
- "[STATUS] == 200"` ,
},
}
for _ , scenario := range scenarios {
t . Run ( scenario . name , func ( t * testing . T ) {
_ , err := parseAndValidateConfigBytes ([] byte ( scenario . config ))
if scenario . shouldError {
if err == nil {
t . Error ( "should've returned an error" )
} else if scenario . expectedErr != "" && err . Error () != scenario . expectedErr {
t . Errorf ( "expected error message '%s', got '%s'" , scenario . expectedErr , err . Error ())
}
} else if err != nil {
t . Errorf ( "shouldn't have returned an error, got: %v" , err )
}
})
}
}
func TestParseAndValidateConfigBytesWithSuites ( t * testing . T ) {
scenarios := [] struct {
name string
shouldError bool
expectedErr string
config string
}{
{
name : "suite-with-no-name" ,
shouldError : true ,
expectedErr : "invalid suite 'testing_': suite must have a name" ,
config : `
endpoints:
- name: dummy
url: https://example.com/dummy
conditions:
- "[STATUS] == 200"
suites:
- group: testing
interval: 30s
endpoints:
- name: step1
url: https://example.com/test
conditions:
- "[STATUS] == 200"` ,
},
{
name : "suite-with-no-endpoints" ,
shouldError : true ,
expectedErr : "invalid suite 'testing_empty-suite': suite must have at least one endpoint" ,
config : `
endpoints:
- name: dummy
url: https://example.com/dummy
conditions:
- "[STATUS] == 200"
suites:
- name: empty-suite
group: testing
interval: 30s
endpoints: []` ,
},
{
name : "suite-with-duplicate-endpoint-names" ,
shouldError : true ,
expectedErr : "invalid suite 'testing_duplicate-test': suite cannot have duplicate endpoint names: duplicate endpoint name 'step1'" ,
config : `
endpoints:
- name: dummy
url: https://example.com/dummy
conditions:
- "[STATUS] == 200"
suites:
- name: duplicate-test
group: testing
interval: 30s
endpoints:
- name: step1
url: https://example.com/test1
conditions:
- "[STATUS] == 200"
- name: step1
url: https://example.com/test2
conditions:
- "[STATUS] == 200"` ,
},
{
name : "suite-with-invalid-negative-timeout" ,
shouldError : true ,
expectedErr : "invalid suite 'testing_negative-timeout-suite': suite timeout must be positive" ,
config : `
endpoints:
- name: dummy
url: https://example.com/dummy
conditions:
- "[STATUS] == 200"
suites:
- name: negative-timeout-suite
group: testing
interval: 30s
timeout: -5m
endpoints:
- name: step1
url: https://example.com/test
conditions:
- "[STATUS] == 200"` ,
},
{
name : "valid-suite-with-defaults" ,
shouldError : false ,
config : `
endpoints:
- name: api-service
group: backend
url: https://example.com/api
conditions:
- "[STATUS] == 200"
suites:
- name: integration-test
group: testing
endpoints:
- name: step1
url: https://example.com/test
conditions:
- "[STATUS] == 200"
- name: step2
url: https://example.com/validate
conditions:
- "[STATUS] == 200"` ,
},
{
name : "valid-suite-with-all-fields" ,
shouldError : false ,
config : `
endpoints:
- name: api-service
group: backend
url: https://example.com/api
conditions:
- "[STATUS] == 200"
suites:
- name: full-integration-test
group: testing
enabled: true
interval: 15m
timeout: 10m
context:
base_url: "https://example.com"
user_id: 12345
endpoints:
- name: authentication
url: https://example.com/auth
conditions:
- "[STATUS] == 200"
- name: user-profile
url: https://example.com/profile
conditions:
- "[STATUS] == 200"
- "[BODY].user_id == 12345"` ,
},
{
name : "valid-suite-with-endpoint-inheritance" ,
shouldError : false ,
config : `
endpoints:
- name: api-service
group: backend
url: https://example.com/api
conditions:
- "[STATUS] == 200"
suites:
- name: inheritance-test
group: parent-group
endpoints:
- name: child-endpoint
url: https://example.com/test
conditions:
- "[STATUS] == 200"` ,
},
{
name : "valid-suite-with-store-functionality" ,
shouldError : false ,
config : `
endpoints:
- name: api-service
group: backend
url: https://example.com/api
conditions:
- "[STATUS] == 200"
suites:
- name: store-test
group: testing
endpoints:
- name: get-token
url: https://example.com/auth
conditions:
- "[STATUS] == 200"
store:
auth_token: "[BODY].token"
- name: use-token
url: https://example.com/data
headers:
Authorization: "Bearer {auth_token}"
conditions:
- "[STATUS] == 200"` ,
},
}
for _ , scenario := range scenarios {
t . Run ( scenario . name , func ( t * testing . T ) {
_ , err := parseAndValidateConfigBytes ([] byte ( scenario . config ))
if scenario . shouldError {
if err == nil {
t . Error ( "should've returned an error" )
} else if scenario . expectedErr != "" && err . Error () != scenario . expectedErr {
t . Errorf ( "expected error message '%s', got '%s'" , scenario . expectedErr , err . Error ())
}
} else if err != nil {
t . Errorf ( "shouldn't have returned an error, got: %v" , err )
}
})
}
}
2025-09-28 14:26:12 -04:00
func TestValidateTunnelingConfig ( t * testing . T ) {
tests := [] struct {
name string
config * Config
wantErr bool
errMsg string
}{
{
name : "valid tunneling config" ,
config : & Config {
Tunneling : & tunneling . Config {
Tunnels : map [ string ] * sshtunnel . Config {
"test" : {
Type : "SSH" ,
Host : "example.com" ,
Username : "test" ,
Password : "secret" ,
},
},
},
Endpoints : [] * endpoint . Endpoint {
{
Name : "test-endpoint" ,
URL : "http://example.com/health" ,
ClientConfig : & client . Config {
Tunnel : "test" ,
},
Conditions : [] endpoint . Condition { "[STATUS] == 200" },
},
},
},
wantErr : false ,
},
{
name : "invalid tunnel reference in endpoint" ,
config : & Config {
Tunneling : & tunneling . Config {
Tunnels : map [ string ] * sshtunnel . Config {
"test" : {
Type : "SSH" ,
Host : "example.com" ,
Username : "test" ,
Password : "secret" ,
},
},
},
Endpoints : [] * endpoint . Endpoint {
{
Name : "test-endpoint" ,
URL : "http://example.com/health" ,
ClientConfig : & client . Config {
Tunnel : "nonexistent" ,
},
Conditions : [] endpoint . Condition { "[STATUS] == 200" },
},
},
},
wantErr : true ,
errMsg : "endpoint '_test-endpoint': tunnel 'nonexistent' not found in tunneling configuration" ,
},
{
name : "invalid tunnel reference in suite endpoint" ,
config : & Config {
Tunneling : & tunneling . Config {
Tunnels : map [ string ] * sshtunnel . Config {
"test" : {
Type : "SSH" ,
Host : "example.com" ,
Username : "test" ,
Password : "secret" ,
},
},
},
Suites : [] * suite . Suite {
{
Name : "test-suite" ,
Endpoints : [] * endpoint . Endpoint {
{
Name : "suite-endpoint" ,
URL : "http://example.com/health" ,
ClientConfig : & client . Config {
Tunnel : "invalid" ,
},
Conditions : [] endpoint . Condition { "[STATUS] == 200" },
},
},
},
},
},
wantErr : true ,
errMsg : "suite '_test-suite' endpoint '_suite-endpoint': tunnel 'invalid' not found in tunneling configuration" ,
},
{
name : "no tunneling config" ,
config : & Config {
Endpoints : [] * endpoint . Endpoint {
{
Name : "test-endpoint" ,
URL : "http://example.com/health" ,
Conditions : [] endpoint . Condition { "[STATUS] == 200" },
},
},
},
wantErr : false ,
},
}
for _ , tt := range tests {
t . Run ( tt . name , func ( t * testing . T ) {
2025-09-29 23:01:27 -04:00
err := ValidateTunnelingConfig ( tt . config )
2025-09-28 14:26:12 -04:00
if tt . wantErr {
if err == nil {
2025-09-29 23:01:27 -04:00
t . Error ( "ValidateTunnelingConfig() expected error but got none" )
2025-09-28 14:26:12 -04:00
return
}
if err . Error () != tt . errMsg {
2025-09-29 23:01:27 -04:00
t . Errorf ( "ValidateTunnelingConfig() error = %v, want %v" , err . Error (), tt . errMsg )
2025-09-28 14:26:12 -04:00
}
return
}
if err != nil {
2025-09-29 23:01:27 -04:00
t . Errorf ( "ValidateTunnelingConfig() unexpected error = %v" , err )
2025-09-28 14:26:12 -04:00
}
})
}
}
func TestResolveTunnelForClientConfig ( t * testing . T ) {
config := & Config {
Tunneling : & tunneling . Config {
Tunnels : map [ string ] * sshtunnel . Config {
"test" : {
Type : "SSH" ,
Host : "example.com" ,
Username : "test" ,
Password : "secret" ,
},
},
},
}
err := config . Tunneling . ValidateAndSetDefaults ()
if err != nil {
t . Fatalf ( "Failed to validate tunnel config: %v" , err )
}
tests := [] struct {
name string
clientConfig * client . Config
wantErr bool
errMsg string
}{
{
name : "valid tunnel reference" ,
clientConfig : & client . Config {
Tunnel : "test" ,
},
wantErr : false ,
},
{
name : "invalid tunnel reference" ,
clientConfig : & client . Config {
Tunnel : "nonexistent" ,
},
wantErr : true ,
errMsg : "tunnel 'nonexistent' not found in tunneling configuration" ,
},
{
name : "no tunnel reference" ,
clientConfig : & client . Config {},
wantErr : false ,
},
}
for _ , tt := range tests {
t . Run ( tt . name , func ( t * testing . T ) {
err := resolveTunnelForClientConfig ( config , tt . clientConfig )
if tt . wantErr {
if err == nil {
t . Error ( "resolveTunnelForClientConfig() expected error but got none" )
return
}
if err . Error () != tt . errMsg {
t . Errorf ( "resolveTunnelForClientConfig() error = %v, want %v" , err . Error (), tt . errMsg )
}
return
}
if err != nil {
t . Errorf ( "resolveTunnelForClientConfig() unexpected error = %v" , err )
}
})
}
}