Skip to content

Commit

Permalink
Test MS Teams adaptive cards
Browse files Browse the repository at this point in the history
  • Loading branch information
alexander-akhmetov committed Sep 2, 2024
1 parent bf5d7a3 commit d1a070e
Show file tree
Hide file tree
Showing 2 changed files with 249 additions and 6 deletions.
128 changes: 128 additions & 0 deletions notify/msteams/adaptive_cards_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
// Copyright 2024 The Prometheus Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package msteams

import (
"testing"

"github.com/stretchr/testify/require"
)

func TestNewAdaptiveCard(t *testing.T) {
card := NewAdaptiveCard()

require.NotNil(t, card)
require.Equal(t, "http://adaptivecards.io/schemas/adaptive-card.json", card.Schema)
require.Equal(t, "AdaptiveCard", card.Type)
require.Equal(t, "1.4", card.Version)
require.Empty(t, card.Body)
}

func TestAdaptiveCard_MarshalJSON(t *testing.T) {
card := NewAdaptiveCard()
card.AppendItem(AdaptiveCardTextBlockItem{Text: "Text"})

bytes, err := card.MarshalJSON()
require.NoError(t, err)

expectedJSON := `
{
"body":[
{"type":"TextBlock","text":"Text"}
],
"msTeams":{"width":"Full"},
"$schema":"http://adaptivecards.io/schemas/adaptive-card.json",
"type":"AdaptiveCard",
"version":"1.4"
}`
require.JSONEq(t, expectedJSON, string(bytes))
}

func TestNewAdaptiveCardsMessage(t *testing.T) {
card := NewAdaptiveCard()
message := NewAdaptiveCardsMessage(card)

require.Equal(t, "message", message.Type)
require.Len(t, message.Attachments, 1)
require.Equal(t, "application/vnd.microsoft.card.adaptive", message.Attachments[0].ContentType)
require.Equal(t, card, message.Attachments[0].Content)
}

func TestAdaptiveCardTextBlockItem_MarshalJSON(t *testing.T) {
item := AdaptiveCardTextBlockItem{
Text: "hello world",
Color: "test-color",
Size: "medium",
Weight: "bold",
Wrap: true,
}

bytes, err := item.MarshalJSON()
require.NoError(t, err)

expectedJSON := `{
"type": "TextBlock",
"text": "hello world",
"color": "test-color",
"size": "medium",
"weight": "bold",
"wrap": true
}`
require.JSONEq(t, expectedJSON, string(bytes))
}

func AdaptiveCardActionSetItemMarshalJSON(t *testing.T) {
item := AdaptiveCardActionSetItem{
Actions: []AdaptiveCardActionItem{
AdaptiveCardOpenURLActionItem{
Title: "View URL",
URL: "https://example.com",
},
},
}

bytes, err := item.MarshalJSON()
require.NoError(t, err)

expectedJSON := `{
"type":"ActionSet",
"actions":[
{
"type":"Action.OpenUrl",
"title":"View URL",
"url":"https://example.com"
}
]
}`
require.JSONEq(t, expectedJSON, string(bytes))
}

func AdaptiveCardOpenURLActionItemMarshalJSON(t *testing.T) {
item := AdaptiveCardOpenURLActionItem{
IconURL: "https://example.com/icon.png",
Title: "View URL",
URL: "https://example.com",
}

bytes, err := item.MarshalJSON()
require.NoError(t, err)

expectedJSON := `{
"type":"Action.OpenUrl",
"title":"View URL",
"url":"https://example.com",
"iconUrl":"https://example.com/icon.png"
}`
require.JSONEq(t, expectedJSON, string(bytes))
}
127 changes: 121 additions & 6 deletions notify/msteams/msteams_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
package msteams

import (
"bytes"
"context"
"encoding/json"
"fmt"
Expand Down Expand Up @@ -146,13 +147,111 @@ func TestNotifier_Notify_WithReason(t *testing.T) {
statusCode int
responseContent string
expectedReason notify.Reason
expectedBody string
noError bool
isResolved bool
}{
{
name: "with a 2xx status code and response 1",
name: "Simple alerting message",
statusCode: http.StatusOK,
responseContent: "1",
noError: true,
responseContent: "",
expectedBody: `{
"attachments": [
{
"content": {
"body": [
{
"color":"attention",
"size":"large",
"text":"",
"type":"TextBlock",
"weight":"bolder",
"wrap":true
},
{
"text":"",
"type":"TextBlock",
"wrap":true
},
{
"actions":[
{
"title":"View URL",
"type":"Action.OpenUrl",
"url":"http://am"
}
],
"type":"ActionSet"
}
],
"$schema":"http://adaptivecards.io/schemas/adaptive-card.json",
"msTeams": {
"width": "Full"
},
"type":"AdaptiveCard",
"version":"1.4"
},
"contentType":"application/vnd.microsoft.card.adaptive"
}
],
"type":"message"
}`,
noError: true,
},
{
name: "Resolved message",
statusCode: http.StatusOK,
isResolved: true,
responseContent: "",
expectedBody: `{
"attachments": [
{
"content": {
"body": [
{
"color":"good",
"size":"large",
"text":"",
"type":"TextBlock",
"weight":"bolder",
"wrap":true
},
{
"text":"",
"type":"TextBlock",
"wrap":true
},
{
"actions":[
{
"title":"View URL",
"type":"Action.OpenUrl",
"url":"http://am"
}
],
"type":"ActionSet"
}
],
"$schema":"http://adaptivecards.io/schemas/adaptive-card.json",
"msTeams": {
"width": "Full"
},
"type":"AdaptiveCard",
"version":"1.4"
},
"contentType":"application/vnd.microsoft.card.adaptive"
}
],
"type":"message"
}`,
noError: true,
},
{
name: "Error response",
statusCode: http.StatusBadRequest,
responseContent: "error",
expectedReason: notify.ClientErrorReason,
noError: false,
},
}
for _, tt := range tests {
Expand All @@ -167,26 +266,42 @@ func TestNotifier_Notify_WithReason(t *testing.T) {
)
require.NoError(t, err)

var requestBody bytes.Buffer

notifier.postJSONFunc = func(ctx context.Context, client *http.Client, url string, body io.Reader) (*http.Response, error) {
_, err := io.Copy(&requestBody, body)
require.NoError(t, err)

resp := httptest.NewRecorder()
resp.WriteString(tt.responseContent)
resp.WriteHeader(tt.statusCode)
return resp.Result(), nil

result := resp.Result()
result.StatusCode = tt.statusCode

return result, nil
}
ctx := context.Background()
ctx = notify.WithGroupKey(ctx, "1")

alert1 := &types.Alert{
Alert: model.Alert{
StartsAt: time.Now(),
EndsAt: time.Now().Add(time.Hour),
},
}
if tt.isResolved {
alert1.Alert.EndsAt = time.Now()
} else {
alert1.Alert.EndsAt = time.Now().Add(time.Hour)
}

_, err = notifier.Notify(ctx, alert1)

if tt.noError {
require.NoError(t, err)
require.JSONEq(t, tt.expectedBody, requestBody.String())
} else {
var reasonError *notify.ErrorWithReason
require.NotNil(t, err)
require.ErrorAs(t, err, &reasonError)
require.Equal(t, tt.expectedReason, reasonError.Reason)
}
Expand Down

0 comments on commit d1a070e

Please sign in to comment.