diff --git a/.github/assets/teams-workflows-alerts.png b/.github/assets/teams-workflows-alerts.png
new file mode 100644
index 00000000..45cc040e
Binary files /dev/null and b/.github/assets/teams-workflows-alerts.png differ
diff --git a/README.md b/README.md
index 748499ce..3fb3d62f 100644
--- a/README.md
+++ b/README.md
@@ -67,7 +67,8 @@ Have any feedback or questions? [Create a discussion](https://github.com/TwiN/ga
- [Configuring PagerDuty alerts](#configuring-pagerduty-alerts)
- [Configuring Pushover alerts](#configuring-pushover-alerts)
- [Configuring Slack alerts](#configuring-slack-alerts)
- - [Configuring Teams alerts](#configuring-teams-alerts)
+ - [Configuring Teams alerts *(Deprecated)*](#configuring-teams-alerts-deprecated)
+ - [Configuring Teams Workflow alerts](#configuring-teams-workflow-alerts)
- [Configuring Telegram alerts](#configuring-telegram-alerts)
- [Configuring Twilio alerts](#configuring-twilio-alerts)
- [Configuring AWS SES alerts](#configuring-aws-ses-alerts)
@@ -566,7 +567,8 @@ endpoints:
| `alerting.pagerduty` | Configuration for alerts of type `pagerduty`.
See [Configuring PagerDuty alerts](#configuring-pagerduty-alerts). | `{}` |
| `alerting.pushover` | Configuration for alerts of type `pushover`.
See [Configuring Pushover alerts](#configuring-pushover-alerts). | `{}` |
| `alerting.slack` | Configuration for alerts of type `slack`.
See [Configuring Slack alerts](#configuring-slack-alerts). | `{}` |
-| `alerting.teams` | Configuration for alerts of type `teams`.
See [Configuring Teams alerts](#configuring-teams-alerts). | `{}` |
+| `alerting.teams` | Configuration for alerts of type `teams`. *(Deprecated)*
See [Configuring Teams alerts](#configuring-teams-alerts-deprecated). | `{}` |
+| `alerting.teams-workflows` | Configuration for alerts of type `teams-workflows`.
See [Configuring Teams Workflow alerts](#configuring-teams-workflow-alerts). | `{}` |
| `alerting.telegram` | Configuration for alerts of type `telegram`.
See [Configuring Telegram alerts](#configuring-telegram-alerts). | `{}` |
| `alerting.twilio` | Settings for alerts of type `twilio`.
See [Configuring Twilio alerts](#configuring-twilio-alerts). | `{}` |
@@ -1176,7 +1178,12 @@ Here's an example of what the notifications look like:
![Slack notifications](.github/assets/slack-alerts.png)
-#### Configuring Teams alerts
+#### Configuring Teams alerts *(Deprecated)*
+
+> [!CAUTION]
+> **Deprecated:** Office 365 Connectors within Microsoft Teams are being retired ([Source: Microsoft DevBlog](https://devblogs.microsoft.com/microsoft365dev/retirement-of-office-365-connectors-within-microsoft-teams/)).
+> Existing connectors will continue to work until December 2025. The new [Teams Workflow Alerts](#configuring-teams-workflow-alerts) should be used with Microsoft Workflows instead of this legacy configuration.
+
| Parameter | Description | Default |
|:-----------------------------------------|:-------------------------------------------------------------------------------------------|:--------------------|
| `alerting.teams` | Configuration for alerts of type `teams` | `{}` |
@@ -1230,6 +1237,61 @@ Here's an example of what the notifications look like:
![Teams notifications](.github/assets/teams-alerts.png)
+#### Configuring Teams Workflow alerts
+
+> [!NOTE]
+> This alert is compatible with Workflows for Microsoft Teams. To setup the workflow and get the webhook URL, follow the [Microsoft Documentation](https://support.microsoft.com/en-us/office/create-incoming-webhooks-with-workflows-for-microsoft-teams-8ae491c7-0394-4861-ba59-055e33f75498).
+
+| Parameter | Description | Default |
+|:---------------------------------------------------|:-------------------------------------------------------------------------------------------|:-------------------|
+| `alerting.teams-workflows` | Configuration for alerts of type `teams` | `{}` |
+| `alerting.teams-workflows.webhook-url` | Teams Webhook URL | Required `""` |
+| `alerting.teams-workflows.default-alert` | Default alert configuration.
See [Setting a default alert](#setting-a-default-alert) | N/A |
+| `alerting.teams-workflows.overrides` | List of overrides that may be prioritized over the default configuration | `[]` |
+| `alerting.teams-workflows.title` | Title of the notification | `"⛑ Gatus"` |
+| `alerting.teams-workflows.overrides[].group` | Endpoint group for which the configuration will be overridden by this configuration | `""` |
+| `alerting.teams-workflows.overrides[].webhook-url` | Teams WorkFlow Webhook URL | `""` |
+
+```yaml
+alerting:
+ teams-workflows:
+ webhook-url: "https://********.webhook.office.com/webhookb2/************"
+ # You can also add group-specific to keys, which will
+ # override the to key above for the specified groups
+ overrides:
+ - group: "core"
+ webhook-url: "https://********.webhook.office.com/webhookb3/************"
+
+endpoints:
+ - name: website
+ url: "https://twin.sh/health"
+ interval: 30s
+ conditions:
+ - "[STATUS] == 200"
+ - "[BODY].status == UP"
+ - "[RESPONSE_TIME] < 300"
+ alerts:
+ - type: teams-workflows
+ description: "healthcheck failed"
+ send-on-resolved: true
+
+ - name: back-end
+ group: core
+ url: "https://example.org/"
+ interval: 5m
+ conditions:
+ - "[STATUS] == 200"
+ - "[CERTIFICATE_EXPIRATION] > 48h"
+ alerts:
+ - type: teams-workflows
+ description: "healthcheck failed"
+ send-on-resolved: true
+```
+
+Here's an example of what the notifications look like:
+
+![Teams Workflow notifications](.github/assets/teams-workflows-alerts.png)
+
#### Configuring Telegram alerts
| Parameter | Description | Default |
diff --git a/alerting/config.go b/alerting/config.go
index 040931eb..9148670f 100644
--- a/alerting/config.go
+++ b/alerting/config.go
@@ -26,6 +26,7 @@ import (
"github.com/TwiN/gatus/v5/alerting/provider/pushover"
"github.com/TwiN/gatus/v5/alerting/provider/slack"
"github.com/TwiN/gatus/v5/alerting/provider/teams"
+ "github.com/TwiN/gatus/v5/alerting/provider/teamsworkflows"
"github.com/TwiN/gatus/v5/alerting/provider/telegram"
"github.com/TwiN/gatus/v5/alerting/provider/twilio"
"github.com/TwiN/gatus/v5/alerting/provider/zulip"
@@ -90,6 +91,9 @@ type Config struct {
// Teams is the configuration for the teams alerting provider
Teams *teams.AlertProvider `yaml:"teams,omitempty"`
+ // TeamsWorkflows is the configuration for the teams alerting provider using the new Workflow App Webhook Connector
+ TeamsWorkflows *teamsworkflows.AlertProvider `yaml:"teams-workflows,omitempty"`
+
// Telegram is the configuration for the telegram alerting provider
Telegram *telegram.AlertProvider `yaml:"telegram,omitempty"`
diff --git a/alerting/provider/provider.go b/alerting/provider/provider.go
index 510e9f4e..5bccc8a4 100644
--- a/alerting/provider/provider.go
+++ b/alerting/provider/provider.go
@@ -20,6 +20,7 @@ import (
"github.com/TwiN/gatus/v5/alerting/provider/pushover"
"github.com/TwiN/gatus/v5/alerting/provider/slack"
"github.com/TwiN/gatus/v5/alerting/provider/teams"
+ "github.com/TwiN/gatus/v5/alerting/provider/teamsworkflows"
"github.com/TwiN/gatus/v5/alerting/provider/telegram"
"github.com/TwiN/gatus/v5/alerting/provider/twilio"
"github.com/TwiN/gatus/v5/alerting/provider/zulip"
@@ -80,6 +81,7 @@ var (
_ AlertProvider = (*pushover.AlertProvider)(nil)
_ AlertProvider = (*slack.AlertProvider)(nil)
_ AlertProvider = (*teams.AlertProvider)(nil)
+ _ AlertProvider = (*teamsworkflows.AlertProvider)(nil)
_ AlertProvider = (*telegram.AlertProvider)(nil)
_ AlertProvider = (*twilio.AlertProvider)(nil)
_ AlertProvider = (*zulip.AlertProvider)(nil)
diff --git a/alerting/provider/teamsworkflows/teamsworkflows.go b/alerting/provider/teamsworkflows/teamsworkflows.go
new file mode 100644
index 00000000..104e2d26
--- /dev/null
+++ b/alerting/provider/teamsworkflows/teamsworkflows.go
@@ -0,0 +1,182 @@
+package teamsworkflows
+
+import (
+ "bytes"
+ "encoding/json"
+ "fmt"
+ "io"
+ "net/http"
+
+ "github.com/TwiN/gatus/v5/alerting/alert"
+ "github.com/TwiN/gatus/v5/client"
+ "github.com/TwiN/gatus/v5/config/endpoint"
+)
+
+// AlertProvider is the configuration necessary for sending an alert using Teams
+type AlertProvider struct {
+ WebhookURL string `yaml:"webhook-url"`
+
+ // DefaultAlert is the default alert configuration to use for endpoints with an alert of the appropriate type
+ DefaultAlert *alert.Alert `yaml:"default-alert,omitempty"`
+
+ // Overrides is a list of Override that may be prioritized over the default configuration
+ Overrides []Override `yaml:"overrides,omitempty"`
+
+ // Title is the title of the message that will be sent
+ Title string `yaml:"title,omitempty"`
+}
+
+// Override is a case under which the default integration is overridden
+type Override struct {
+ Group string `yaml:"group"`
+ WebhookURL string `yaml:"webhook-url"`
+}
+
+// IsValid returns whether the provider's configuration is valid
+func (provider *AlertProvider) IsValid() bool {
+ registeredGroups := make(map[string]bool)
+ if provider.Overrides != nil {
+ for _, override := range provider.Overrides {
+ if isAlreadyRegistered := registeredGroups[override.Group]; isAlreadyRegistered || override.Group == "" || len(override.WebhookURL) == 0 {
+ return false
+ }
+ registeredGroups[override.Group] = true
+ }
+ }
+ return len(provider.WebhookURL) > 0
+}
+
+// Send an alert using the provider
+func (provider *AlertProvider) Send(ep *endpoint.Endpoint, alert *alert.Alert, result *endpoint.Result, resolved bool) error {
+ buffer := bytes.NewBuffer(provider.buildRequestBody(ep, alert, result, resolved))
+ request, err := http.NewRequest(http.MethodPost, provider.getWebhookURLForGroup(ep.Group), buffer)
+ if err != nil {
+ return err
+ }
+ request.Header.Set("Content-Type", "application/json")
+ response, err := client.GetHTTPClient(nil).Do(request)
+ if err != nil {
+ return err
+ }
+ defer response.Body.Close()
+ if response.StatusCode > 399 {
+ body, _ := io.ReadAll(response.Body)
+ return fmt.Errorf("call to provider alert returned status code %d: %s", response.StatusCode, string(body))
+ }
+ return err
+}
+
+// AdaptiveCardBody represents the structure of an Adaptive Card
+type AdaptiveCardBody struct {
+ Type string `json:"type"`
+ Version string `json:"version"`
+ Body []CardBody `json:"body"`
+}
+
+// CardBody represents the body of the Adaptive Card
+type CardBody struct {
+ Type string `json:"type"`
+ Text string `json:"text,omitempty"`
+ Wrap bool `json:"wrap"`
+ Separator bool `json:"separator,omitempty"`
+ Size string `json:"size,omitempty"`
+ Weight string `json:"weight,omitempty"`
+ Items []CardBody `json:"items,omitempty"`
+ Facts []Fact `json:"facts,omitempty"`
+ FactSet *FactSetBody `json:"factSet,omitempty"`
+}
+
+// FactSetBody represents the FactSet in the Adaptive Card
+type FactSetBody struct {
+ Type string `json:"type"`
+ Facts []Fact `json:"facts"`
+}
+
+// Fact represents an individual fact in the FactSet
+type Fact struct {
+ Title string `json:"title"`
+ Value string `json:"value"`
+}
+
+// buildRequestBody builds the request body for the provider
+func (provider *AlertProvider) buildRequestBody(ep *endpoint.Endpoint, alert *alert.Alert, result *endpoint.Result, resolved bool) []byte {
+ var message string
+ if resolved {
+ message = fmt.Sprintf("An alert for **%s** has been resolved after passing successfully %d time(s) in a row.", ep.DisplayName(), alert.SuccessThreshold)
+ } else {
+ message = fmt.Sprintf("An alert for **%s** has been triggered due to having failed %d time(s) in a row.", ep.DisplayName(), alert.FailureThreshold)
+ }
+
+ // Configure default title if it's not provided
+ title := "⛑ Gatus"
+ if provider.Title != "" {
+ title = provider.Title
+ }
+
+ // Build the facts from the condition results
+ var facts []Fact
+ for _, conditionResult := range result.ConditionResults {
+ var key string
+ if conditionResult.Success {
+ key = "✅"
+ } else {
+ key = "❌"
+ }
+ facts = append(facts, Fact{
+ Title: key,
+ Value: conditionResult.Condition,
+ })
+ }
+
+ cardContent := AdaptiveCardBody{
+ Type: "AdaptiveCard",
+ Version: "1.4", // Version 1.5 and 1.6 doesn't seem to be supported by Teams as of 27/08/2024
+ Body: []CardBody{
+ {
+ Type: "TextBlock",
+ Text: title,
+ Size: "Medium",
+ Weight: "Bolder",
+ },
+ {
+ Type: "TextBlock",
+ Text: message,
+ Wrap: true,
+ },
+ {
+ Type: "FactSet",
+ Facts: facts,
+ },
+ },
+ }
+
+ attachment := map[string]interface{}{
+ "contentType": "application/vnd.microsoft.card.adaptive",
+ "content": cardContent,
+ }
+
+ payload := map[string]interface{}{
+ "type": "message",
+ "attachments": []interface{}{attachment},
+ }
+
+ bodyAsJSON, _ := json.Marshal(payload)
+ return bodyAsJSON
+}
+
+// getWebhookURLForGroup returns the appropriate Webhook URL integration to for a given group
+func (provider *AlertProvider) getWebhookURLForGroup(group string) string {
+ if provider.Overrides != nil {
+ for _, override := range provider.Overrides {
+ if group == override.Group {
+ return override.WebhookURL
+ }
+ }
+ }
+ return provider.WebhookURL
+}
+
+// GetDefaultAlert returns the provider's default alert configuration
+func (provider *AlertProvider) GetDefaultAlert() *alert.Alert {
+ return provider.DefaultAlert
+}
diff --git a/alerting/provider/teamsworkflows/teamsworkflows_test.go b/alerting/provider/teamsworkflows/teamsworkflows_test.go
new file mode 100644
index 00000000..6e4a9940
--- /dev/null
+++ b/alerting/provider/teamsworkflows/teamsworkflows_test.go
@@ -0,0 +1,269 @@
+package teamsworkflows
+
+import (
+ "encoding/json"
+ "net/http"
+ "testing"
+
+ "github.com/TwiN/gatus/v5/alerting/alert"
+ "github.com/TwiN/gatus/v5/client"
+ "github.com/TwiN/gatus/v5/config/endpoint"
+ "github.com/TwiN/gatus/v5/test"
+)
+
+func TestAlertDefaultProvider_IsValid(t *testing.T) {
+ invalidProvider := AlertProvider{WebhookURL: ""}
+ if invalidProvider.IsValid() {
+ t.Error("provider shouldn't have been valid")
+ }
+ validProvider := AlertProvider{WebhookURL: "http://example.com"}
+ if !validProvider.IsValid() {
+ t.Error("provider should've been valid")
+ }
+}
+
+func TestAlertProvider_IsValidWithOverride(t *testing.T) {
+ providerWithInvalidOverrideGroup := AlertProvider{
+ Overrides: []Override{
+ {
+ WebhookURL: "http://example.com",
+ Group: "",
+ },
+ },
+ }
+ if providerWithInvalidOverrideGroup.IsValid() {
+ t.Error("provider Group shouldn't have been valid")
+ }
+ providerWithInvalidOverrideTo := AlertProvider{
+ Overrides: []Override{
+ {
+ WebhookURL: "",
+ Group: "group",
+ },
+ },
+ }
+ if providerWithInvalidOverrideTo.IsValid() {
+ t.Error("provider integration key shouldn't have been valid")
+ }
+ providerWithValidOverride := AlertProvider{
+ WebhookURL: "http://example.com",
+ Overrides: []Override{
+ {
+ WebhookURL: "http://example.com",
+ Group: "group",
+ },
+ },
+ }
+ if !providerWithValidOverride.IsValid() {
+ t.Error("provider should've been valid")
+ }
+}
+
+func TestAlertProvider_Send(t *testing.T) {
+ defer client.InjectHTTPClient(nil)
+ firstDescription := "description-1"
+ secondDescription := "description-2"
+ scenarios := []struct {
+ Name string
+ Provider AlertProvider
+ Alert alert.Alert
+ Resolved bool
+ MockRoundTripper test.MockRoundTripper
+ ExpectedError bool
+ }{
+ {
+ Name: "triggered",
+ Provider: AlertProvider{},
+ Alert: alert.Alert{Description: &firstDescription, SuccessThreshold: 5, FailureThreshold: 3},
+ Resolved: false,
+ MockRoundTripper: test.MockRoundTripper(func(r *http.Request) *http.Response {
+ return &http.Response{StatusCode: http.StatusOK, Body: http.NoBody}
+ }),
+ ExpectedError: false,
+ },
+ {
+ Name: "triggered-error",
+ Provider: AlertProvider{},
+ Alert: alert.Alert{Description: &firstDescription, SuccessThreshold: 5, FailureThreshold: 3},
+ Resolved: false,
+ MockRoundTripper: test.MockRoundTripper(func(r *http.Request) *http.Response {
+ return &http.Response{StatusCode: http.StatusInternalServerError, Body: http.NoBody}
+ }),
+ ExpectedError: true,
+ },
+ {
+ Name: "resolved",
+ Provider: AlertProvider{},
+ Alert: alert.Alert{Description: &secondDescription, SuccessThreshold: 5, FailureThreshold: 3},
+ Resolved: true,
+ MockRoundTripper: test.MockRoundTripper(func(r *http.Request) *http.Response {
+ return &http.Response{StatusCode: http.StatusOK, Body: http.NoBody}
+ }),
+ ExpectedError: false,
+ },
+ {
+ Name: "resolved-error",
+ Provider: AlertProvider{},
+ Alert: alert.Alert{Description: &secondDescription, SuccessThreshold: 5, FailureThreshold: 3},
+ Resolved: true,
+ MockRoundTripper: test.MockRoundTripper(func(r *http.Request) *http.Response {
+ return &http.Response{StatusCode: http.StatusInternalServerError, Body: http.NoBody}
+ }),
+ ExpectedError: true,
+ },
+ }
+ for _, scenario := range scenarios {
+ t.Run(scenario.Name, func(t *testing.T) {
+ client.InjectHTTPClient(&http.Client{Transport: scenario.MockRoundTripper})
+ err := scenario.Provider.Send(
+ &endpoint.Endpoint{Name: "endpoint-name"},
+ &scenario.Alert,
+ &endpoint.Result{
+ ConditionResults: []*endpoint.ConditionResult{
+ {Condition: "[CONNECTED] == true", Success: scenario.Resolved},
+ {Condition: "[STATUS] == 200", Success: scenario.Resolved},
+ },
+ },
+ scenario.Resolved,
+ )
+ if scenario.ExpectedError && err == nil {
+ t.Error("expected error, got none")
+ }
+ if !scenario.ExpectedError && err != nil {
+ t.Error("expected no error, got", err.Error())
+ }
+ })
+ }
+}
+
+func TestAlertProvider_buildRequestBody(t *testing.T) {
+ firstDescription := "description-1"
+ secondDescription := "description-2"
+ scenarios := []struct {
+ Name string
+ Provider AlertProvider
+ Alert alert.Alert
+ NoConditions bool
+ Resolved bool
+ ExpectedBody string
+ }{
+ {
+ Name: "triggered",
+ Provider: AlertProvider{},
+ Alert: alert.Alert{Description: &firstDescription, SuccessThreshold: 5, FailureThreshold: 3},
+ Resolved: false,
+ ExpectedBody: "{\"attachments\":[{\"content\":{\"type\":\"AdaptiveCard\",\"version\":\"1.4\",\"body\":[{\"type\":\"TextBlock\",\"text\":\"\\u0026#x26D1; Gatus\",\"wrap\":false,\"size\":\"Medium\",\"weight\":\"Bolder\"},{\"type\":\"TextBlock\",\"text\":\"An alert for **endpoint-name** has been triggered due to having failed 3 time(s) in a row.\",\"wrap\":true},{\"type\":\"FactSet\",\"wrap\":false,\"facts\":[{\"title\":\"\\u0026#x274C;\",\"value\":\"[CONNECTED] == true\"},{\"title\":\"\\u0026#x274C;\",\"value\":\"[STATUS] == 200\"}]}]},\"contentType\":\"application/vnd.microsoft.card.adaptive\"}],\"type\":\"message\"}",
+ },
+ {
+ Name: "resolved",
+ Provider: AlertProvider{},
+ Alert: alert.Alert{Description: &secondDescription, SuccessThreshold: 5, FailureThreshold: 3},
+ Resolved: true,
+ ExpectedBody: "{\"attachments\":[{\"content\":{\"type\":\"AdaptiveCard\",\"version\":\"1.4\",\"body\":[{\"type\":\"TextBlock\",\"text\":\"\\u0026#x26D1; Gatus\",\"wrap\":false,\"size\":\"Medium\",\"weight\":\"Bolder\"},{\"type\":\"TextBlock\",\"text\":\"An alert for **endpoint-name** has been resolved after passing successfully 5 time(s) in a row.\",\"wrap\":true},{\"type\":\"FactSet\",\"wrap\":false,\"facts\":[{\"title\":\"\\u0026#x2705;\",\"value\":\"[CONNECTED] == true\"},{\"title\":\"\\u0026#x2705;\",\"value\":\"[STATUS] == 200\"}]}]},\"contentType\":\"application/vnd.microsoft.card.adaptive\"}],\"type\":\"message\"}",
+ },
+ {
+ Name: "resolved-with-no-conditions",
+ NoConditions: true,
+ Provider: AlertProvider{},
+ Alert: alert.Alert{Description: &secondDescription, SuccessThreshold: 5, FailureThreshold: 3},
+ Resolved: true,
+ ExpectedBody: "{\"attachments\":[{\"content\":{\"type\":\"AdaptiveCard\",\"version\":\"1.4\",\"body\":[{\"type\":\"TextBlock\",\"text\":\"\\u0026#x26D1; Gatus\",\"wrap\":false,\"size\":\"Medium\",\"weight\":\"Bolder\"},{\"type\":\"TextBlock\",\"text\":\"An alert for **endpoint-name** has been resolved after passing successfully 5 time(s) in a row.\",\"wrap\":true},{\"type\":\"FactSet\",\"wrap\":false}]},\"contentType\":\"application/vnd.microsoft.card.adaptive\"}],\"type\":\"message\"}",
+ },
+ }
+ for _, scenario := range scenarios {
+ t.Run(scenario.Name, func(t *testing.T) {
+ var conditionResults []*endpoint.ConditionResult
+ if !scenario.NoConditions {
+ conditionResults = []*endpoint.ConditionResult{
+ {Condition: "[CONNECTED] == true", Success: scenario.Resolved},
+ {Condition: "[STATUS] == 200", Success: scenario.Resolved},
+ }
+ }
+ body := scenario.Provider.buildRequestBody(
+ &endpoint.Endpoint{Name: "endpoint-name"},
+ &scenario.Alert,
+ &endpoint.Result{ConditionResults: conditionResults},
+ scenario.Resolved,
+ )
+ if string(body) != scenario.ExpectedBody {
+ t.Errorf("expected:\n%s\ngot:\n%s", scenario.ExpectedBody, body)
+ }
+ out := make(map[string]interface{})
+ if err := json.Unmarshal(body, &out); err != nil {
+ t.Error("expected body to be valid JSON, got error:", err.Error())
+ }
+ })
+ }
+}
+
+func TestAlertProvider_GetDefaultAlert(t *testing.T) {
+ if (&AlertProvider{DefaultAlert: &alert.Alert{}}).GetDefaultAlert() == nil {
+ t.Error("expected default alert to be not nil")
+ }
+ if (&AlertProvider{DefaultAlert: nil}).GetDefaultAlert() != nil {
+ t.Error("expected default alert to be nil")
+ }
+}
+
+func TestAlertProvider_getWebhookURLForGroup(t *testing.T) {
+ tests := []struct {
+ Name string
+ Provider AlertProvider
+ InputGroup string
+ ExpectedOutput string
+ }{
+ {
+ Name: "provider-no-override-specify-no-group-should-default",
+ Provider: AlertProvider{
+ WebhookURL: "http://example.com",
+ Overrides: nil,
+ },
+ InputGroup: "",
+ ExpectedOutput: "http://example.com",
+ },
+ {
+ Name: "provider-no-override-specify-group-should-default",
+ Provider: AlertProvider{
+ WebhookURL: "http://example.com",
+ Overrides: nil,
+ },
+ InputGroup: "group",
+ ExpectedOutput: "http://example.com",
+ },
+ {
+ Name: "provider-with-override-specify-no-group-should-default",
+ Provider: AlertProvider{
+ WebhookURL: "http://example.com",
+ Overrides: []Override{
+ {
+ Group: "group",
+ WebhookURL: "http://example01.com",
+ },
+ },
+ },
+ InputGroup: "",
+ ExpectedOutput: "http://example.com",
+ },
+ {
+ Name: "provider-with-override-specify-group-should-override",
+ Provider: AlertProvider{
+ WebhookURL: "http://example.com",
+ Overrides: []Override{
+ {
+ Group: "group",
+ WebhookURL: "http://example01.com",
+ },
+ },
+ },
+ InputGroup: "group",
+ ExpectedOutput: "http://example01.com",
+ },
+ }
+ for _, tt := range tests {
+ t.Run(tt.Name, func(t *testing.T) {
+ if got := tt.Provider.getWebhookURLForGroup(tt.InputGroup); got != tt.ExpectedOutput {
+ t.Errorf("AlertProvider.getToForGroup() = %v, want %v", got, tt.ExpectedOutput)
+ }
+ })
+ }
+}