Repository: abahmed/kwatch
Pull request #373 (2025-02-18 15:41:55)

🐛 fix teams testing
Changes proposed in this pull request:
- fix teams testing



Patch:
diff --git a/alertmanager/teams/teams.go b/alertmanager/teams/teams.go
index 048ca69..0b921fd 100644
--- a/alertmanager/teams/teams.go
+++ b/alertmanager/teams/teams.go
@@ -1,214 +1,238 @@
 package teams
 
 import (
-    "bytes"
-    "encoding/json"
-    "fmt"
-    "io"
-    "net/http"
-    "strings"
-    "time"
-
-    "github.com/abahmed/kwatch/config"
-    "github.com/abahmed/kwatch/event"
-    "github.com/sirupsen/logrus"
+	"bytes"
+	"encoding/json"
+	"fmt"
+	"io"
+	"net/http"
+	"strings"
+	"time"
+
+	"github.com/abahmed/kwatch/config"
+	"github.com/abahmed/kwatch/event"
+	"github.com/sirupsen/logrus"
 )
 
 const (
-    defaultTeamsTitle = "&#9937; Kwatch detected a crash in pod"
+	defaultTeamsTitle = "&#9937; Kwatch detected a crash in pod"
+	defaultMaxRetries = 3
+	defaultRetryDelay = 5
 )
 
 type Teams struct {
-    // The HTTP trigger URL for the Power Automate flow
-    flowURL string
-    title   string
-    text    string
-
-    // reference for general app configuration
-    appCfg *config.App
+	// The HTTP trigger URL for the Power Automate flow
+	webhook    string
+	title      string
+	text       string
+	maxRetries int
+	retryDelay int
+
+	// reference for general app configuration
+	appCfg *config.App
 }
 
 type teamsFlowPayload struct {
-    Title      string                   `json:"title"`
-    Text       string                   `json:"text"`
-    Attachment []map[string]interface{} `json:"attachment"`
+	Title      string                   `json:"title"`
+	Text       string                   `json:"text"`
+	Attachment []map[string]interface{} `json:"attachment"`
 }
 
 // NewTeams returns new team instance
 func NewTeams(config map[string]interface{}, appCfg *config.App) *Teams {
-    flowURL, ok := config["flowURL"].(string)
-    if !ok || len(flowURL) == 0 {
-        logrus.Warnf("initializing Teams with empty flow url")
-        return nil
-    }
-
-    logrus.Infof("initializing Teams with flow url: %s", flowURL)
-
-    title, _ := config["title"].(string)
-    text, _ := config["text"].(string)
-
-    return &Teams{
-        flowURL: flowURL,
-        title:   title,
-        text:    text,
-        appCfg:  appCfg,
-    }
+	webhook, ok := config["webhook"].(string)
+	if !ok || len(webhook) == 0 {
+		logrus.Warnf("initializing Teams with empty flow url")
+		return nil
+	}
+
+	logrus.Infof("initializing Teams with flow url: %s", webhook)
+
+	title, _ := config["title"].(string)
+	text, _ := config["text"].(string)
+
+	maxRetries, mxOk := config["maxRetries"].(int)
+	if !mxOk || maxRetries == 0 {
+		maxRetries = defaultMaxRetries
+	}
+
+	retryDelay, dlOk := config["retryDelay"].(int)
+	if !dlOk || retryDelay == 0 {
+		retryDelay = defaultRetryDelay
+	}
+
+	return &Teams{
+		webhook:    webhook,
+		title:      title,
+		text:       text,
+		maxRetries: maxRetries,
+		retryDelay: retryDelay,
+		appCfg:     appCfg,
+	}
 }
 
 // Name returns name of the provider
 func (t *Teams) Name() string {
-    return "Microsoft Teams"
+	return "Microsoft Teams"
 }
 
 // SendEvent sends event to the Power Automate flow
 func (t *Teams) SendEvent(e *event.Event) error {
-    payload := t.buildRequestBodyTeams(e)
-    return t.sendAPI(payload)
+	payload := t.buildRequestBodyTeams(e)
+	return t.sendAPI(payload)
 }
 
 // SendMessage sends plain text message to the Power Automate flow
 func (t *Teams) SendMessage(msg string) error {
-    payload := t.buildRequestBodyMessage(msg)
-    return t.sendAPI(payload)
+	payload := t.buildRequestBodyMessage(msg)
+	return t.sendAPI(payload)
 }
 
 // SendApi send the given payload to the Power Automate flow with retry logic
 func (t *Teams) sendAPI(payload []byte) error {
-    // Number of retry attempts
-    maxRetries := 3
-    retryDelay := 5 * time.Second
-
-    // try to send the message up to "maxRetries" times
-    for attempts := 0; attempts < maxRetries; attempts++ {
-        request, err := http.NewRequest(http.MethodPost, t.flowURL, bytes.NewBuffer(payload))
-        if err != nil {
-            return fmt.Errorf("error creating HTTP request: %v", err)
-        }
-
-        request.Header.Set("Content-Type", "application/json")
-
-        client := &http.Client{}
-        resp, err := client.Do(request)
-        if err != nil {
-            return fmt.Errorf("failed to create HTTP response: %w", err)
-        }
-        defer resp.Body.Close()
-
-        // Check for success (HTTP 200 OK)
-        if resp.StatusCode == http.StatusOK {
-            return nil
-        }
-
-        // Handle specific 400 errors (TriggerInputSchemaMismatch)
-        if resp.StatusCode == http.StatusBadRequest {
-            body, _ := io.ReadAll(resp.Body)
-            // Check for error (TriggerInputSchemaMismatch)
-            if strings.Contains(string(body), "TriggerInputSchemaMismatch") {
-                return fmt.Errorf("failed to send message due to schema mismatch: %s", string(body))
-            }
-            return fmt.Errorf("call to power automate flow returned status %d: %s", resp.StatusCode, string(body))
-        }
-
-        // Handle 202 status and retry
-        if resp.StatusCode == http.StatusAccepted {
-            logrus.Warnf("Request accepted by Power Automate flow, but not processed immediately. Attempt %d of %d.", attempts+1, maxRetries)
-        } else {
-            // For other non-200 status codes, log the error
-            body, _ := io.ReadAll(resp.Body)
-            return fmt.Errorf("call to power automate flow returned status %d: %s", resp.StatusCode, string(body))
-        }
-
-        // Wait for a delay before retrying
-        if attempts < maxRetries-1 {
-            time.Sleep(retryDelay)
-        }
-    }
-
-    // After all retries, return an error
-    return fmt.Errorf("failed to send message after %d attempts", maxRetries)
+	// try to send the message up to "maxRetries" times
+	for attempts := 0; attempts < t.maxRetries; attempts++ {
+		request, err :=
+			http.NewRequest(
+				http.MethodPost,
+				t.webhook,
+				bytes.NewBuffer(payload))
+		if err != nil {
+			return fmt.Errorf("error creating HTTP request: %v", err)
+		}
+
+		request.Header.Set("Content-Type", "application/json")
+
+		client := &http.Client{}
+		resp, err := client.Do(request)
+		if err != nil {
+			return fmt.Errorf("failed to create HTTP response: %w", err)
+		}
+		defer resp.Body.Close()
+
+		// Check for success (HTTP 200 OK)
+		if resp.StatusCode == http.StatusOK {
+			return nil
+		}
+
+		// Handle specific 400 errors (TriggerInputSchemaMismatch)
+		if resp.StatusCode == http.StatusBadRequest {
+			body, _ := io.ReadAll(resp.Body)
+			// Check for error (TriggerInputSchemaMismatch)
+			if strings.Contains(string(body), "TriggerInputSchemaMismatch") {
+				return fmt.Errorf(
+					"failed to send message due to schema mismatch: %s",
+					string(body))
+			}
+			return fmt.Errorf(
+				"call to power automate flow returned status %d: %s",
+				resp.StatusCode,
+				string(body))
+		}
+
+		// Handle 202 status and retry
+		if resp.StatusCode == http.StatusAccepted {
+			logrus.Warnf("Request accepted by Power Automate flow, "+
+				"but not processed immediately. Attempt %d of %d.",
+				attempts+1,
+				t.maxRetries)
+		} else {
+			// For other non-200 status codes, log the error
+			body, _ := io.ReadAll(resp.Body)
+			return fmt.Errorf(
+				"call to power automate flow returned status %d: %s",
+				resp.StatusCode,
+				string(body))
+		}
+
+		// Wait for a delay before retrying
+		if attempts < t.maxRetries-1 {
+			time.Sleep(time.Duration(t.retryDelay) * time.Second)
+		}
+	}
+
+	// After all retries, return an error
+	return fmt.Errorf("failed to send message after %d attempts", t.maxRetries)
 }
 
 // buildRequestBodyTeams builds the request body for the Power Automate flow
 func (t *Teams) buildRequestBodyTeams(e *event.Event) []byte {
-    // Use custom title if it's provided, otherwise use the default title
-    title := t.title
-    if len(title) == 0 {
-        title = defaultTeamsTitle
-    }
-
-    // Format the message with markdown
-    msg := e.FormatMarkdown(t.appCfg.ClusterName, t.text, "\n\n")
-
-    // Create the attachment for the message with full event details
-    attachments := []map[string]interface{}{
-        {
-            "contentType": "application/vnd.microsoft.card.adaptive",
-            "content": map[string]interface{}{
-                "$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
-                "type":    "AdaptiveCard",
-                "version": "1.2",
-                "body": []map[string]interface{}{
-                    {
-                        "type": "TextBlock",
-                        "text": title,
-                    },
-                    {
-                        "type": "TextBlock",
-                        "text": fmt.Sprintf("Pod Name: %s", e.PodName),
-                    },
-                    {
-                        "type": "TextBlock",
-                        "text": fmt.Sprintf("Namespace: %s", e.Namespace),
-                    },
-                    {
-                        "type": "TextBlock",
-                        "text": fmt.Sprintf("Reason: %s", e.Reason),
-                    },
-                    {
-                        "type": "TextBlock",
-                        "text": fmt.Sprintf("Logs: %s", e.Logs),
-                    },
-                    {
-                        "type": "TextBlock",
-                        "text": fmt.Sprintf("Events: \n%s", e.Events),
-                    },
-                    {
-                        "type": "TextBlock",
-                        "text": fmt.Sprintf("Time: %s", time.Now().Format(time.RFC1123)),
-                    },
-                },
-            },
-        },
-    }
-
-    // Prepare the payload for the Power Automate flow
-    payload := &teamsFlowPayload{
-        Title:      title,
-        Text:       msg,
-        Attachment: attachments, // Attachment should be an array
-    }
-
-    jsonBytes, err := json.Marshal(payload)
-    if err != nil {
-        logrus.Errorf("failed to marshal payload: %v", err)
-        return nil
-    }
-    return jsonBytes
+	// Use custom title if it's provided, otherwise use the default title
+	title := t.title
+	if len(title) == 0 {
+		title = defaultTeamsTitle
+	}
+
+	// Format the message with markdown
+	msg := e.FormatMarkdown(t.appCfg.ClusterName, t.text, "\n\n")
+
+	// Create the attachment for the message with full event details
+	attachments := []map[string]interface{}{
+		{
+			"contentType": "application/vnd.microsoft.card.adaptive",
+			"content": map[string]interface{}{
+				"$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
+				"type":    "AdaptiveCard",
+				"version": "1.2",
+				"body": []map[string]interface{}{
+					{
+						"type": "TextBlock",
+						"text": title,
+					},
+					{
+						"type": "TextBlock",
+						"text": fmt.Sprintf("Pod Name: %s", e.PodName),
+					},
+					{
+						"type": "TextBlock",
+						"text": fmt.Sprintf("Namespace: %s", e.Namespace),
+					},
+					{
+						"type": "TextBlock",
+						"text": fmt.Sprintf("Reason: %s", e.Reason),
+					},
+					{
+						"type": "TextBlock",
+						"text": fmt.Sprintf("Logs: %s", e.Logs),
+					},
+					{
+						"type": "TextBlock",
+						"text": fmt.Sprintf("Events: \n%s", e.Events),
+					},
+					{
+						"type": "TextBlock",
+						"text": fmt.Sprintf(
+							"Time: %s",
+							time.Now().Format(time.RFC1123)),
+					},
+				},
+			},
+		},
+	}
+
+	// Prepare the payload for the Power Automate flow
+	payload := &teamsFlowPayload{
+		Title:      title,
+		Text:       msg,
+		Attachment: attachments, // Attachment should be an array
+	}
+
+	jsonBytes, _ := json.Marshal(payload)
+
+	return jsonBytes
 }
 
-// buildRequestBodyMessage builds plain message payload for the Power Automate flow
+// buildRequestBodyMessage builds plain message payload for the Power
+// Automate flow
 func (t *Teams) buildRequestBodyMessage(msg string) []byte {
-    payload := &teamsFlowPayload{
-        Title: "New Alert",
-        Text:  msg,
-        // Empty attachments array to prevent schema mismatch error
-        Attachment: []map[string]interface{}{},
-    }
-
-    jsonBytes, err := json.Marshal(payload)
-    if err != nil {
-        logrus.Errorf("failed to marshal payload: %v", err)
-        return nil
-    }
-    return jsonBytes
-}
\ No newline at end of file
+	payload := &teamsFlowPayload{
+		Title: "New Alert",
+		Text:  msg,
+		// Empty attachments array to prevent schema mismatch error
+		Attachment: []map[string]interface{}{},
+	}
+
+	jsonBytes, _ := json.Marshal(payload)
+
+	return jsonBytes
+}
diff --git a/go.mod b/go.mod
index 38362a7..7f8f4f8 100644
--- a/go.mod
+++ b/go.mod
@@ -10,23 +10,23 @@ require (
 	github.com/stretchr/testify v1.10.0
 	gopkg.in/mail.v2 v2.3.1
 	gopkg.in/yaml.v3 v3.0.1
-	k8s.io/api v0.32.2
-	k8s.io/apimachinery v0.32.2
-	k8s.io/client-go v0.32.2
+	k8s.io/api v0.32.3
+	k8s.io/apimachinery v0.32.3
+	k8s.io/client-go v0.32.3
 )
 
 require (
 	github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
-	github.com/emicklei/go-restful/v3 v3.12.1 // indirect
+	github.com/emicklei/go-restful/v3 v3.12.2 // indirect
 	github.com/fxamacker/cbor/v2 v2.7.0 // indirect
 	github.com/go-logr/logr v1.4.2 // indirect
-	github.com/go-openapi/jsonpointer v0.21.0 // indirect
+	github.com/go-openapi/jsonpointer v0.21.1 // indirect
 	github.com/go-openapi/jsonreference v0.21.0 // indirect
-	github.com/go-openapi/swag v0.23.0 // indirect
+	github.com/go-openapi/swag v0.23.1 // indirect
 	github.com/gogo/protobuf v1.3.2 // indirect
 	github.com/golang/protobuf v1.5.4 // indirect
 	github.com/google/gnostic-models v0.6.9 // indirect
-	github.com/google/go-cmp v0.6.0 // indirect
+	github.com/google/go-cmp v0.7.0 // indirect
 	github.com/google/go-querystring v1.1.0 // indirect
 	github.com/google/gofuzz v1.2.0 // indirect
 	github.com/google/uuid v1.6.0 // indirect
@@ -41,22 +41,23 @@ require (
 	github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
 	github.com/spf13/pflag v1.0.6 // indirect
 	github.com/x448/float16 v0.8.4 // indirect
-	golang.org/x/crypto v0.33.0 // indirect
-	golang.org/x/exp v0.0.0-20250215185904-eff6e970281f
-	golang.org/x/net v0.35.0 // indirect
-	golang.org/x/oauth2 v0.26.0 // indirect
-	golang.org/x/sys v0.30.0 // indirect
-	golang.org/x/term v0.29.0 // indirect
-	golang.org/x/text v0.22.0 // indirect
-	golang.org/x/time v0.10.0 // indirect
+	golang.org/x/crypto v0.36.0 // indirect
+	golang.org/x/exp v0.0.0-20250305212735-054e65f0b394
+	golang.org/x/net v0.37.0 // indirect
+	golang.org/x/oauth2 v0.28.0 // indirect
+	golang.org/x/sys v0.31.0 // indirect
+	golang.org/x/term v0.30.0 // indirect
+	golang.org/x/text v0.23.0 // indirect
+	golang.org/x/time v0.11.0 // indirect
 	google.golang.org/protobuf v1.36.5 // indirect
 	gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect
 	gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect
 	gopkg.in/inf.v0 v0.9.1 // indirect
 	k8s.io/klog/v2 v2.130.1 // indirect
-	k8s.io/kube-openapi v0.0.0-20241212222426-2c72e554b1e7 // indirect
+	k8s.io/kube-openapi v0.0.0-20250304201544-e5f78fe3ede9 // indirect
 	k8s.io/utils v0.0.0-20241210054802-24370beab758 // indirect
 	sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 // indirect
-	sigs.k8s.io/structured-merge-diff/v4 v4.5.0 // indirect
+	sigs.k8s.io/randfill v1.0.0 // indirect
+	sigs.k8s.io/structured-merge-diff/v4 v4.6.0 // indirect
 	sigs.k8s.io/yaml v1.4.0 // indirect
 )
diff --git a/go.sum b/go.sum
index 957b436..6a7d017 100644
--- a/go.sum
+++ b/go.sum
@@ -4,18 +4,18 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
 github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
 github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
-github.com/emicklei/go-restful/v3 v3.12.1 h1:PJMDIM/ak7btuL8Ex0iYET9hxM3CI2sjZtzpL63nKAU=
-github.com/emicklei/go-restful/v3 v3.12.1/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
+github.com/emicklei/go-restful/v3 v3.12.2 h1:DhwDP0vY3k8ZzE0RunuJy8GhNpPL6zqLkDf9B/a0/xU=
+github.com/emicklei/go-restful/v3 v3.12.2/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
 github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E=
 github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ=
 github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
 github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
-github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ=
-github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY=
+github.com/go-openapi/jsonpointer v0.21.1 h1:whnzv/pNXtK2FbX/W9yJfRmE2gsmkfahjMKB0fZvcic=
+github.com/go-openapi/jsonpointer v0.21.1/go.mod h1:50I1STOfbY1ycR8jGz8DaMeLCdXiI6aDteEdRNNzpdk=
 github.com/go-openapi/jsonreference v0.21.0 h1:Rs+Y7hSXT83Jacb7kFyjn4ijOuVGSvOdF2+tg1TRrwQ=
 github.com/go-openapi/jsonreference v0.21.0/go.mod h1:LmZmgsrTkVg9LG4EaHeY8cBDslNPMo06cago5JNLkm4=
-github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE=
-github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ=
+github.com/go-openapi/swag v0.23.1 h1:lpsStH0n2ittzTnbaSloVZLuB5+fvSY/+hnagBjSNZU=
+github.com/go-openapi/swag v0.23.1/go.mod h1:STZs8TbRvEQQKUA+JZNAm3EWlgaOBGpyFDqQnDHMef0=
 github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
 github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
 github.com/go-test/deep v1.0.4 h1:u2CU3YKy9I2pmu9pX0eq50wCgjfGIt539SqR7FbHiho=
@@ -29,8 +29,8 @@ github.com/google/gnostic-models v0.6.9/go.mod h1:CiWsm0s6BSQd1hRn8/QmxqB6BesYcb
 github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
 github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE=
 github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
-github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
-github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
+github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
+github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
 github.com/google/go-github/v41 v41.0.1-0.20211227215900-a899e0fadbec h1:PnbyvdIp56CT6HddxfLNO9eRcvc6jhq6vcJlw8lwXv0=
 github.com/google/go-github/v41 v41.0.1-0.20211227215900-a899e0fadbec/go.mod h1:tcwqOpiCK8G6jo+O7eRWrfmWuXjjWImM+L9egAMfKLE=
 github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=
@@ -95,10 +95,10 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk
 golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
 golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
 golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
-golang.org/x/crypto v0.33.0 h1:IOBPskki6Lysi0lo9qQvbxiQ+FvsCC/YWOecCHAixus=
-golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M=
-golang.org/x/exp v0.0.0-20250215185904-eff6e970281f h1:oFMYAjX0867ZD2jcNiLBrI9BdpmEkvPyi5YrBGXbamg=
-golang.org/x/exp v0.0.0-20250215185904-eff6e970281f/go.mod h1:BHOTPb3L19zxehTsLoJXVaTktb06DFgmdW6Wb9s8jqk=
+golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34=
+golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc=
+golang.org/x/exp v0.0.0-20250305212735-054e65f0b394 h1:nDVHiLt8aIbd/VzvPWN6kSOPE7+F/fNFDSXLVYkE/Iw=
+golang.org/x/exp v0.0.0-20250305212735-054e65f0b394/go.mod h1:sIifuuw/Yco/y6yb6+bDNfyeQ/MdPUy/hKEMYQV17cM=
 golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
 golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
 golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
@@ -106,10 +106,10 @@ golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLL
 golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
 golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
-golang.org/x/net v0.35.0 h1:T5GQRQb2y08kTAByq9L4/bz8cipCdA8FbRTXewonqY8=
-golang.org/x/net v0.35.0/go.mod h1:EglIi67kWsHKlRzzVMUD93VMSWGFOMSZgxFjparz1Qk=
-golang.org/x/oauth2 v0.26.0 h1:afQXWNNaeC4nvZ0Ed9XvCCzXM6UHJG7iCg0W4fPqSBE=
-golang.org/x/oauth2 v0.26.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
+golang.org/x/net v0.37.0 h1:1zLorHbz+LYj7MQlSf1+2tPIIgibq2eL5xkrGk6f+2c=
+golang.org/x/net v0.37.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8=
+golang.org/x/oauth2 v0.28.0 h1:CrgCKl8PPAVtLnU3c+EDw6x11699EWlsDeWNWKdIOkc=
+golang.org/x/oauth2 v0.28.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8=
 golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -118,23 +118,23 @@ golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7w
 golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=
-golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
+golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik=
+golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
 golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
-golang.org/x/term v0.29.0 h1:L6pJp37ocefwRRtYPKSWOWzOtWSxVajvz2ldH/xi3iU=
-golang.org/x/term v0.29.0/go.mod h1:6bl4lRlvVuDgSf3179VpIxBF0o10JUpXWOnI7nErv7s=
+golang.org/x/term v0.30.0 h1:PQ39fJZ+mfadBm0y5WlL4vlM7Sx1Hgf13sMIY2+QS9Y=
+golang.org/x/term v0.30.0/go.mod h1:NYYFdzHoI5wRh/h5tDMdMqCqPJZEuNqVR5xJLd/n67g=
 golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
-golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM=
-golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY=
-golang.org/x/time v0.10.0 h1:3usCWA8tQn0L8+hFJQNgzpWbd89begxN66o1Ojdn5L4=
-golang.org/x/time v0.10.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
+golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY=
+golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4=
+golang.org/x/time v0.11.0 h1:/bpjEDfN9tkoN/ryeYHnv5hcMlc8ncjMcM4XBk5NWV0=
+golang.org/x/time v0.11.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg=
 golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
 golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
 golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
-golang.org/x/tools v0.30.0 h1:BgcpHewrV5AUp2G9MebG4XPFI1E2W41zU1SaqVA9vJY=
-golang.org/x/tools v0.30.0/go.mod h1:c347cR/OJfw5TI+GfX7RUPNMdDRRbjvYTS0jPyvsVtY=
+golang.org/x/tools v0.31.0 h1:0EedkvKDbh+qistFTd0Bcwe/YLh4vHwWEkiI0toFIBU=
+golang.org/x/tools v0.31.0/go.mod h1:naFTU+Cev749tSJRXJlna0T3WxKvb1kWEx15xA4SdmQ=
 golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@@ -155,21 +155,24 @@ gopkg.in/mail.v2 v2.3.1/go.mod h1:htwXN1Qh09vZJ1NVKxQqHPBaCBbzKhp5GzuJEA4VJWw=
 gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
 gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
 gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
-k8s.io/api v0.32.2 h1:bZrMLEkgizC24G9eViHGOPbW+aRo9duEISRIJKfdJuw=
-k8s.io/api v0.32.2/go.mod h1:hKlhk4x1sJyYnHENsrdCWw31FEmCijNGPJO5WzHiJ6Y=
-k8s.io/apimachinery v0.32.2 h1:yoQBR9ZGkA6Rgmhbp/yuT9/g+4lxtsGYwW6dR6BDPLQ=
-k8s.io/apimachinery v0.32.2/go.mod h1:GpHVgxoKlTxClKcteaeuF1Ul/lDVb74KpZcxcmLDElE=
-k8s.io/client-go v0.32.2 h1:4dYCD4Nz+9RApM2b/3BtVvBHw54QjMFUl1OLcJG5yOA=
-k8s.io/client-go v0.32.2/go.mod h1:fpZ4oJXclZ3r2nDOv+Ux3XcJutfrwjKTCHz2H3sww94=
+k8s.io/api v0.32.3 h1:Hw7KqxRusq+6QSplE3NYG4MBxZw1BZnq4aP4cJVINls=
+k8s.io/api v0.32.3/go.mod h1:2wEDTXADtm/HA7CCMD8D8bK4yuBUptzaRhYcYEEYA3k=
+k8s.io/apimachinery v0.32.3 h1:JmDuDarhDmA/Li7j3aPrwhpNBA94Nvk5zLeOge9HH1U=
+k8s.io/apimachinery v0.32.3/go.mod h1:GpHVgxoKlTxClKcteaeuF1Ul/lDVb74KpZcxcmLDElE=
+k8s.io/client-go v0.32.3 h1:RKPVltzopkSgHS7aS98QdscAgtgah/+zmpAogooIqVU=
+k8s.io/client-go v0.32.3/go.mod h1:3v0+3k4IcT9bXTc4V2rt+d2ZPPG700Xy6Oi0Gdl2PaY=
 k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk=
 k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE=
-k8s.io/kube-openapi v0.0.0-20241212222426-2c72e554b1e7 h1:hcha5B1kVACrLujCKLbr8XWMxCxzQx42DY8QKYJrDLg=
-k8s.io/kube-openapi v0.0.0-20241212222426-2c72e554b1e7/go.mod h1:GewRfANuJ70iYzvn+i4lezLDAFzvjxZYK1gn1lWcfas=
+k8s.io/kube-openapi v0.0.0-20250304201544-e5f78fe3ede9 h1:t0huyHnz6HsokckRxAF1bY0cqPFwzINKCL7yltEjZQc=
+k8s.io/kube-openapi v0.0.0-20250304201544-e5f78fe3ede9/go.mod h1:5jIi+8yX4RIb8wk3XwBo5Pq2ccx4FP10ohkbSKCZoK8=
 k8s.io/utils v0.0.0-20241210054802-24370beab758 h1:sdbE21q2nlQtFh65saZY+rRM6x6aJJI8IUa1AmH/qa0=
 k8s.io/utils v0.0.0-20241210054802-24370beab758/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
 sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 h1:gBQPwqORJ8d8/YNZWEjoZs7npUVDpVXUUOFfW6CgAqE=
 sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8/go.mod h1:mdzfpAEoE6DHQEN0uh9ZbOCuHbLK5wOm7dK4ctXE9Tg=
-sigs.k8s.io/structured-merge-diff/v4 v4.5.0 h1:nbCitCK2hfnhyiKo6uf2HxUPTCodY6Qaf85SbDIaMBk=
-sigs.k8s.io/structured-merge-diff/v4 v4.5.0/go.mod h1:N8f93tFZh9U6vpxwRArLiikrE5/2tiu1w1AGfACIGE4=
+sigs.k8s.io/randfill v0.0.0-20250304075658-069ef1bbf016/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY=
+sigs.k8s.io/randfill v1.0.0 h1:JfjMILfT8A6RbawdsK2JXGBR5AQVfd+9TbzrlneTyrU=
+sigs.k8s.io/randfill v1.0.0/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY=
+sigs.k8s.io/structured-merge-diff/v4 v4.6.0 h1:IUA9nvMmnKWcj5jl84xn+T5MnlZKThmUW1TdblaLVAc=
+sigs.k8s.io/structured-merge-diff/v4 v4.6.0/go.mod h1:dDy58f92j70zLsuZVuUX5Wp9vtxXpaZnkPGWeqDfCps=
 sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E=
 sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY=
diff --git a/main.go b/main.go
index b76534f..2c92768 100644
--- a/main.go
+++ b/main.go
@@ -23,7 +23,7 @@ func main() {
 	}
 	setLogFormatter(config.App.LogFormatter)
 
-	logrus.Infof(fmt.Sprintf(constant.WelcomeMsg, version.Short()))
+	logrus.Info(fmt.Sprintf(constant.WelcomeMsg, version.Short()))
 
 	// create kubernetes client
 	client := client.Create(&config.App)


Test patch:
diff --git a/alertmanager/teams/teams_test.go b/alertmanager/teams/teams_test.go
index e382d33..de518ce 100644
--- a/alertmanager/teams/teams_test.go
+++ b/alertmanager/teams/teams_test.go
@@ -1,132 +1,256 @@
 package teams
 
 import (
-    "encoding/json"
-    "net/http"
-    "net/http/httptest"
-    "testing"
-
-    "github.com/abahmed/kwatch/event"
-    "github.com/stretchr/testify/assert"
-	"github.com/stretchr/kwatch/config"
+	"encoding/json"
+	"net/http"
+	"net/http/httptest"
+	"testing"
+
+	"github.com/abahmed/kwatch/config"
+	"github.com/abahmed/kwatch/event"
+	"github.com/stretchr/testify/assert"
 )
 
+func TestEmptyConfig(t *testing.T) {
+	assert := assert.New(t)
+
+	c := NewTeams(map[string]interface{}{}, &config.App{ClusterName: "dev"})
+	assert.Nil(c)
+}
+
+func TestTelegram(t *testing.T) {
+	assert := assert.New(t)
+
+	configMap := map[string]interface{}{
+		"webhook": "http://example.com",
+	}
+	c := NewTeams(configMap, &config.App{ClusterName: "dev"})
+	assert.NotNil(c)
+
+	assert.Equal(c.Name(), "Microsoft Teams")
+}
+
 func TestNewTeams(t *testing.T) {
-    config := map[string]interface{}{
-        "flowURL": "http://example.com",
-        "title":   "Test Title",
-        "text":    "Test Text",
-    }
-    appCfg := &config.App{}
-    teams := NewTeams(config, appCfg)
-    assert.NotNil(t, teams)
-    assert.Equal(t, "http://example.com", teams.flowURL)
-    assert.Equal(t, "Test Title", teams.title)
-    assert.Equal(t, "Test Text", teams.text)
+	configMap := map[string]interface{}{
+		"webhook": "http://example.com",
+		"title":   "Test Title",
+		"text":    "Test Text",
+	}
+	appCfg := &config.App{ClusterName: "dev"}
+	teams := NewTeams(configMap, appCfg)
+	assert.NotNil(t, teams)
+	assert.Equal(t, "http://example.com", teams.webhook)
+	assert.Equal(t, "Test Title", teams.title)
+	assert.Equal(t, "Test Text", teams.text)
 }
 
 func TestSendEvent(t *testing.T) {
-    config := map[string]interface{}{
-        "flowURL": "http://example.com",
-    }
-    appCfg := &config.App{}
-    teams := NewTeams(config, appCfg)
-
-    e := &event.Event{
-        PodName:   "test-pod",
-        Namespace: "test-namespace",
-        Reason:    "test-reason",
-        Logs:      "test-logs",
-        Events:    "test-events",
-    }
-
-    server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
-        w.WriteHeader(http.StatusOK)
-    }))
-    defer server.Close()
-
-    teams.flowURL = server.URL
-    err := teams.SendEvent(e)
-    assert.NoError(t, err)
+	configMap := map[string]interface{}{
+		"webhook": "http://example.com",
+	}
+	appCfg := &config.App{ClusterName: "dev"}
+	teams := NewTeams(configMap, appCfg)
+
+	e := &event.Event{
+		PodName:   "test-pod",
+		Namespace: "test-namespace",
+		Reason:    "test-reason",
+		Logs:      "test-logs",
+		Events:    "test-events",
+	}
+
+	server := httptest.NewServer(
+		http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+			w.WriteHeader(http.StatusOK)
+		}))
+	defer server.Close()
+
+	teams.webhook = server.URL
+	err := teams.SendEvent(e)
+	assert.NoError(t, err)
 }
 
 func TestSendMessage(t *testing.T) {
-    config := map[string]interface{}{
-        "flowURL": "http://example.com",
-    }
-    appCfg := &config.App{}
-    teams := NewTeams(config, appCfg)
-
-    server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
-        w.WriteHeader(http.StatusOK)
-    }))
-    defer server.Close()
-
-    teams.flowURL = server.URL
-    err := teams.SendMessage("test message")
-    assert.NoError(t, err)
+	configMap := map[string]interface{}{
+		"webhook": "http://localhost",
+	}
+	appCfg := &config.App{ClusterName: "dev"}
+	teams := NewTeams(configMap, appCfg)
+
+	server := httptest.NewServer(
+		http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+			w.WriteHeader(http.StatusOK)
+		}))
+	defer server.Close()
+
+	teams.webhook = server.URL
+	err := teams.SendMessage("test message")
+	assert.NoError(t, err)
+}
+
+func TestSendMessageErrorSchemaMismatch(t *testing.T) {
+	assert := assert.New(t)
+
+	s := httptest.NewServer(
+		http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+			w.WriteHeader(http.StatusBadRequest)
+			w.Write([]byte(`TriggerInputSchemaMismatch`))
+		}))
+
+	defer s.Close()
+
+	configMap := map[string]interface{}{
+		"webhook": s.URL,
+	}
+	appCfg := &config.App{ClusterName: "dev"}
+	c := NewTeams(configMap, appCfg)
+	assert.NotNil(c)
+
+	assert.NotNil(c.SendMessage("test"))
+}
+
+func TestSendMessageErrorBadRequest(t *testing.T) {
+	assert := assert.New(t)
+
+	s := httptest.NewServer(
+		http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+			w.WriteHeader(http.StatusBadRequest)
+		}))
+
+	defer s.Close()
+
+	configMap := map[string]interface{}{
+		"webhook": s.URL,
+	}
+	appCfg := &config.App{ClusterName: "dev"}
+	c := NewTeams(configMap, appCfg)
+	assert.NotNil(c)
+
+	assert.NotNil(c.SendMessage("test"))
+}
+
+func TestSendMessageErrorAccepted(t *testing.T) {
+	assert := assert.New(t)
+
+	s := httptest.NewServer(
+		http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+			w.WriteHeader(http.StatusAccepted)
+		}))
+
+	defer s.Close()
+
+	configMap := map[string]interface{}{
+		"webhook": s.URL,
+	}
+	appCfg := &config.App{ClusterName: "dev"}
+	c := NewTeams(configMap, appCfg)
+	assert.NotNil(c)
+
+	assert.NotNil(c.SendMessage("test"))
+}
+
+func TestSendMessageErrorServer(t *testing.T) {
+	assert := assert.New(t)
+
+	s := httptest.NewServer(
+		http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+			w.WriteHeader(http.StatusInternalServerError)
+		}))
+
+	defer s.Close()
+
+	configMap := map[string]interface{}{
+		"webhook": s.URL,
+	}
+	appCfg := &config.App{ClusterName: "dev"}
+	c := NewTeams(configMap, appCfg)
+	assert.NotNil(c)
+
+	assert.NotNil(c.SendMessage("test"))
 }
 
 func TestSendAPI(t *testing.T) {
-    config := map[string]interface{}{
-        "flowURL": "http://example.com",
-    }
-    appCfg := &config.App{}
-    teams := NewTeams(config, appCfg)
-
-    payload := []byte(`{"title":"Test Title","text":"Test Text","attachment":[]}`)
-
-    server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
-        w.WriteHeader(http.StatusOK)
-    }))
-    defer server.Close()
-
-    teams.flowURL = server.URL
-    err := teams.sendAPI(payload)
-    assert.NoError(t, err)
+	server := httptest.NewServer(
+		http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+			w.WriteHeader(http.StatusOK)
+		}))
+	defer server.Close()
+
+	configMap := map[string]interface{}{
+		"webhook": server.URL,
+	}
+	appCfg := &config.App{ClusterName: "dev"}
+	teams := NewTeams(configMap, appCfg)
+
+	payload :=
+		[]byte(`{"title":"Test Title","text":"Test Text","attachment":[]}`)
+	err := teams.sendAPI(payload)
+	assert.NoError(t, err)
+}
+
+func TestInvaildHttpRequest(t *testing.T) {
+	assert := assert.New(t)
+
+	appCfg := &config.App{ClusterName: "dev"}
+
+	configMap := map[string]interface{}{
+		"webhook": "h ttp://localhost/%s",
+	}
+
+	c := NewTeams(configMap, appCfg)
+	assert.NotNil(c)
+	assert.NotNil(c.SendMessage("test"))
+
+	configMap = map[string]interface{}{
+		"webhook": "http://localhost:132323",
+	}
+
+	c = NewTeams(configMap, appCfg)
+	assert.NotNil(c)
+	assert.NotNil(c.SendMessage("test"))
 }
 
 func TestBuildRequestBodyTeams(t *testing.T) {
-    config := map[string]interface{}{
-        "flowURL": "http://example.com",
-        "title":   "Test Title",
-        "text":    "Test Text",
-    }
-    appCfg := &config.App{}
-    teams := NewTeams(config, appCfg)
-
-    e := &event.Event{
-        PodName:   "test-pod",
-        Namespace: "test-namespace",
-        Reason:    "test-reason",
-        Logs:      "test-logs",
-        Events:    "test-events",
-    }
-
-    payload := teams.buildRequestBodyTeams(e)
-    var result teamsFlowPayload
-    err := json.Unmarshal(payload, &result)
-    assert.NoError(t, err)
-    assert.Equal(t, "Test Title", result.Title)
-    assert.Contains(t, result.Text, "test-pod")
-    assert.Contains(t, result.Text, "test-namespace")
-    assert.Contains(t, result.Text, "test-reason")
-    assert.Contains(t, result.Text, "test-logs")
-    assert.Contains(t, result.Text, "test-events")
+	configMap := map[string]interface{}{
+		"webhook": "http://example.com",
+		"title":   "Test Title",
+		"text":    "Test Text",
+	}
+	appCfg := &config.App{}
+	teams := NewTeams(configMap, appCfg)
+
+	e := &event.Event{
+		PodName:   "test-pod",
+		Namespace: "test-namespace",
+		Reason:    "test-reason",
+		Logs:      "test-logs",
+		Events:    "test-events",
+	}
+
+	payload := teams.buildRequestBodyTeams(e)
+	var result teamsFlowPayload
+	err := json.Unmarshal(payload, &result)
+	assert.NoError(t, err)
+	assert.Equal(t, "Test Title", result.Title)
+	assert.Contains(t, result.Text, "test-pod")
+	assert.Contains(t, result.Text, "test-namespace")
+	assert.Contains(t, result.Text, "test-reason")
+	assert.Contains(t, result.Text, "test-logs")
+	assert.Contains(t, result.Text, "test-events")
 }
 
 func TestBuildRequestBodyMessage(t *testing.T) {
-    config := map[string]interface{}{
-        "flowURL": "http://example.com",
-    }
-    appCfg := &config.App{}
-    teams := NewTeams(config, appCfg)
-
-    payload := teams.buildRequestBodyMessage("test message")
-    var result teamsFlowPayload
-    err := json.Unmarshal(payload, &result)
-    assert.NoError(t, err)
-    assert.Equal(t, "New Alert", result.Title)
-    assert.Equal(t, "test message", result.Text)
-    assert.Empty(t, result.Attachment)
-}
\ No newline at end of file
+	configMap := map[string]interface{}{
+		"webhook": "http://example.com",
+	}
+	appCfg := &config.App{}
+	teams := NewTeams(configMap, appCfg)
+
+	payload := teams.buildRequestBodyMessage("test message")
+	var result teamsFlowPayload
+	err := json.Unmarshal(payload, &result)
+	assert.NoError(t, err)
+	assert.Equal(t, "New Alert", result.Title)
+	assert.Equal(t, "test message", result.Text)
+	assert.Empty(t, result.Attachment)
+}