mirror of
https://github.com/mattermost/mattermost.git
synced 2026-02-03 20:40:00 -05:00
1166 lines
37 KiB
Go
1166 lines
37 KiB
Go
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
|
// See LICENSE.txt for license information.
|
|
|
|
package app
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"fmt"
|
|
"io"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"net/url"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
|
|
"github.com/mattermost/mattermost/server/public/model"
|
|
"github.com/mattermost/mattermost/server/v8/channels/testlib"
|
|
)
|
|
|
|
func TestCreateIncomingWebhookForChannel(t *testing.T) {
|
|
mainHelper.Parallel(t)
|
|
th := Setup(t).InitBasic(t)
|
|
|
|
type TestCase struct {
|
|
EnableIncomingHooks bool
|
|
EnablePostUsernameOverride bool
|
|
EnablePostIconOverride bool
|
|
IncomingWebhook model.IncomingWebhook
|
|
|
|
ExpectedError bool
|
|
ExpectedIncomingWebhook *model.IncomingWebhook
|
|
}
|
|
|
|
for name, tc := range map[string]TestCase{
|
|
"webhooks not enabled": {
|
|
EnableIncomingHooks: false,
|
|
EnablePostUsernameOverride: false,
|
|
EnablePostIconOverride: false,
|
|
IncomingWebhook: model.IncomingWebhook{
|
|
DisplayName: "title",
|
|
Description: "description",
|
|
ChannelId: th.BasicChannel.Id,
|
|
},
|
|
|
|
ExpectedError: true,
|
|
ExpectedIncomingWebhook: nil,
|
|
},
|
|
"valid: username and post icon url ignored, since override not enabled": {
|
|
EnableIncomingHooks: true,
|
|
EnablePostUsernameOverride: false,
|
|
EnablePostIconOverride: false,
|
|
IncomingWebhook: model.IncomingWebhook{
|
|
DisplayName: "title",
|
|
Description: "description",
|
|
ChannelId: th.BasicChannel.Id,
|
|
Username: ":invalid and ignored:",
|
|
IconURL: "ignored",
|
|
},
|
|
|
|
ExpectedError: false,
|
|
ExpectedIncomingWebhook: &model.IncomingWebhook{
|
|
DisplayName: "title",
|
|
Description: "description",
|
|
ChannelId: th.BasicChannel.Id,
|
|
},
|
|
},
|
|
"invalid username, override enabled": {
|
|
EnableIncomingHooks: true,
|
|
EnablePostUsernameOverride: true,
|
|
EnablePostIconOverride: false,
|
|
IncomingWebhook: model.IncomingWebhook{
|
|
DisplayName: "title",
|
|
Description: "description",
|
|
ChannelId: th.BasicChannel.Id,
|
|
Username: ":invalid:",
|
|
},
|
|
|
|
ExpectedError: true,
|
|
ExpectedIncomingWebhook: nil,
|
|
},
|
|
"valid, no username or post icon url provided": {
|
|
EnableIncomingHooks: true,
|
|
EnablePostUsernameOverride: true,
|
|
EnablePostIconOverride: true,
|
|
IncomingWebhook: model.IncomingWebhook{
|
|
DisplayName: "title",
|
|
Description: "description",
|
|
ChannelId: th.BasicChannel.Id,
|
|
},
|
|
|
|
ExpectedError: false,
|
|
ExpectedIncomingWebhook: &model.IncomingWebhook{
|
|
DisplayName: "title",
|
|
Description: "description",
|
|
ChannelId: th.BasicChannel.Id,
|
|
},
|
|
},
|
|
"valid, with username and post icon": {
|
|
EnableIncomingHooks: true,
|
|
EnablePostUsernameOverride: true,
|
|
EnablePostIconOverride: true,
|
|
IncomingWebhook: model.IncomingWebhook{
|
|
DisplayName: "title",
|
|
Description: "description",
|
|
ChannelId: th.BasicChannel.Id,
|
|
Username: "valid",
|
|
IconURL: "http://example.com/icon",
|
|
},
|
|
|
|
ExpectedError: false,
|
|
ExpectedIncomingWebhook: &model.IncomingWebhook{
|
|
DisplayName: "title",
|
|
Description: "description",
|
|
ChannelId: th.BasicChannel.Id,
|
|
Username: "valid",
|
|
IconURL: "http://example.com/icon",
|
|
},
|
|
},
|
|
} {
|
|
t.Run(name, func(t *testing.T) {
|
|
th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableIncomingWebhooks = tc.EnableIncomingHooks })
|
|
th.App.UpdateConfig(func(cfg *model.Config) {
|
|
*cfg.ServiceSettings.EnablePostUsernameOverride = tc.EnablePostUsernameOverride
|
|
})
|
|
th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnablePostIconOverride = tc.EnablePostIconOverride })
|
|
|
|
createdHook, appErr := th.App.CreateIncomingWebhookForChannel(th.BasicUser.Id, th.BasicChannel, &tc.IncomingWebhook)
|
|
if tc.ExpectedError {
|
|
require.NotNil(t, appErr, "should have failed")
|
|
} else {
|
|
require.Nil(t, appErr, "should not have failed")
|
|
}
|
|
if createdHook != nil {
|
|
defer func() {
|
|
appErr := th.App.DeleteIncomingWebhook(createdHook.Id)
|
|
require.Nil(t, appErr, "Error cleaning up webhook")
|
|
}()
|
|
}
|
|
if tc.ExpectedIncomingWebhook == nil {
|
|
assert.Nil(t, createdHook, "expected nil webhook")
|
|
} else if assert.NotNil(t, createdHook, "expected non-nil webhook") {
|
|
assert.Equal(t, tc.ExpectedIncomingWebhook.DisplayName, createdHook.DisplayName)
|
|
assert.Equal(t, tc.ExpectedIncomingWebhook.Description, createdHook.Description)
|
|
assert.Equal(t, tc.ExpectedIncomingWebhook.ChannelId, createdHook.ChannelId)
|
|
assert.Equal(t, tc.ExpectedIncomingWebhook.Username, createdHook.Username)
|
|
assert.Equal(t, tc.ExpectedIncomingWebhook.IconURL, createdHook.IconURL)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestUpdateIncomingWebhook(t *testing.T) {
|
|
mainHelper.Parallel(t)
|
|
th := Setup(t).InitBasic(t)
|
|
|
|
type TestCase struct {
|
|
EnableIncomingHooks bool
|
|
EnablePostUsernameOverride bool
|
|
EnablePostIconOverride bool
|
|
IncomingWebhook model.IncomingWebhook
|
|
|
|
ExpectedError bool
|
|
ExpectedIncomingWebhook *model.IncomingWebhook
|
|
}
|
|
|
|
for name, tc := range map[string]TestCase{
|
|
"webhooks not enabled": {
|
|
EnableIncomingHooks: false,
|
|
EnablePostUsernameOverride: false,
|
|
EnablePostIconOverride: false,
|
|
IncomingWebhook: model.IncomingWebhook{
|
|
DisplayName: "title",
|
|
Description: "description",
|
|
ChannelId: th.BasicChannel.Id,
|
|
},
|
|
|
|
ExpectedError: true,
|
|
ExpectedIncomingWebhook: nil,
|
|
},
|
|
"valid: username and post icon url ignored, since override not enabled": {
|
|
EnableIncomingHooks: true,
|
|
EnablePostUsernameOverride: false,
|
|
EnablePostIconOverride: false,
|
|
IncomingWebhook: model.IncomingWebhook{
|
|
DisplayName: "title",
|
|
Description: "description",
|
|
ChannelId: th.BasicChannel.Id,
|
|
Username: ":invalid and ignored:",
|
|
IconURL: "ignored",
|
|
},
|
|
|
|
ExpectedError: false,
|
|
ExpectedIncomingWebhook: &model.IncomingWebhook{
|
|
DisplayName: "title",
|
|
Description: "description",
|
|
ChannelId: th.BasicChannel.Id,
|
|
},
|
|
},
|
|
"invalid username, override enabled": {
|
|
EnableIncomingHooks: true,
|
|
EnablePostUsernameOverride: true,
|
|
EnablePostIconOverride: false,
|
|
IncomingWebhook: model.IncomingWebhook{
|
|
DisplayName: "title",
|
|
Description: "description",
|
|
ChannelId: th.BasicChannel.Id,
|
|
Username: ":invalid:",
|
|
},
|
|
|
|
ExpectedError: true,
|
|
ExpectedIncomingWebhook: nil,
|
|
},
|
|
"valid, no username or post icon url provided": {
|
|
EnableIncomingHooks: true,
|
|
EnablePostUsernameOverride: true,
|
|
EnablePostIconOverride: true,
|
|
IncomingWebhook: model.IncomingWebhook{
|
|
DisplayName: "title",
|
|
Description: "description",
|
|
ChannelId: th.BasicChannel.Id,
|
|
},
|
|
|
|
ExpectedError: false,
|
|
ExpectedIncomingWebhook: &model.IncomingWebhook{
|
|
DisplayName: "title",
|
|
Description: "description",
|
|
ChannelId: th.BasicChannel.Id,
|
|
},
|
|
},
|
|
"valid, with username and post icon": {
|
|
EnableIncomingHooks: true,
|
|
EnablePostUsernameOverride: true,
|
|
EnablePostIconOverride: true,
|
|
IncomingWebhook: model.IncomingWebhook{
|
|
DisplayName: "title",
|
|
Description: "description",
|
|
ChannelId: th.BasicChannel.Id,
|
|
Username: "valid",
|
|
IconURL: "http://example.com/icon",
|
|
},
|
|
|
|
ExpectedError: false,
|
|
ExpectedIncomingWebhook: &model.IncomingWebhook{
|
|
DisplayName: "title",
|
|
Description: "description",
|
|
ChannelId: th.BasicChannel.Id,
|
|
Username: "valid",
|
|
IconURL: "http://example.com/icon",
|
|
},
|
|
},
|
|
} {
|
|
t.Run(name, func(t *testing.T) {
|
|
th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableIncomingWebhooks = true })
|
|
|
|
hook, appErr := th.App.CreateIncomingWebhookForChannel(th.BasicUser.Id, th.BasicChannel, &model.IncomingWebhook{
|
|
ChannelId: th.BasicChannel.Id,
|
|
})
|
|
require.Nil(t, appErr)
|
|
defer func() {
|
|
th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableIncomingWebhooks = true })
|
|
appErr = th.App.DeleteIncomingWebhook(hook.Id)
|
|
require.Nil(t, appErr, "Error cleaning up webhook")
|
|
}()
|
|
|
|
th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableIncomingWebhooks = tc.EnableIncomingHooks })
|
|
th.App.UpdateConfig(func(cfg *model.Config) {
|
|
*cfg.ServiceSettings.EnablePostUsernameOverride = tc.EnablePostUsernameOverride
|
|
})
|
|
th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnablePostIconOverride = tc.EnablePostIconOverride })
|
|
|
|
updatedHook, appErr := th.App.UpdateIncomingWebhook(hook, &tc.IncomingWebhook)
|
|
if tc.ExpectedError {
|
|
require.NotNil(t, appErr, "should have failed")
|
|
} else {
|
|
require.Nil(t, appErr, "should not have failed")
|
|
}
|
|
if tc.ExpectedIncomingWebhook == nil {
|
|
assert.Nil(t, updatedHook, "expected nil webhook")
|
|
} else if assert.NotNil(t, updatedHook, "expected non-nil webhook") {
|
|
assert.Equal(t, tc.ExpectedIncomingWebhook.DisplayName, updatedHook.DisplayName)
|
|
assert.Equal(t, tc.ExpectedIncomingWebhook.Description, updatedHook.Description)
|
|
assert.Equal(t, tc.ExpectedIncomingWebhook.ChannelId, updatedHook.ChannelId)
|
|
assert.Equal(t, tc.ExpectedIncomingWebhook.Username, updatedHook.Username)
|
|
assert.Equal(t, tc.ExpectedIncomingWebhook.IconURL, updatedHook.IconURL)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestCreateWebhookPost(t *testing.T) {
|
|
mainHelper.Parallel(t)
|
|
testCluster := &testlib.FakeClusterInterface{}
|
|
th := SetupWithClusterMock(t, testCluster).InitBasic(t)
|
|
|
|
th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableIncomingWebhooks = true })
|
|
|
|
hook, appErr := th.App.CreateIncomingWebhookForChannel(th.BasicUser.Id, th.BasicChannel, &model.IncomingWebhook{ChannelId: th.BasicChannel.Id})
|
|
require.Nil(t, appErr)
|
|
defer func() {
|
|
appErr = th.App.DeleteIncomingWebhook(hook.Id)
|
|
require.Nil(t, appErr, "Error cleaning up webhook")
|
|
}()
|
|
|
|
post, appErr := th.App.CreateWebhookPost(th.Context, hook.UserId, th.BasicChannel, "foo", "user", "http://iconurl", "",
|
|
model.StringInterface{
|
|
model.PostPropsAttachments: []*model.SlackAttachment{
|
|
{
|
|
Text: "text",
|
|
},
|
|
},
|
|
model.PostPropsWebhookDisplayName: hook.DisplayName,
|
|
},
|
|
model.PostTypeSlackAttachment,
|
|
"", nil)
|
|
require.Nil(t, appErr)
|
|
|
|
assert.Contains(t, post.GetProps(), model.PostPropsFromWebhook, "missing from_webhook prop")
|
|
assert.Contains(t, post.GetProps(), model.PostPropsAttachments, "missing attachments prop")
|
|
assert.Contains(t, post.GetProps(), model.PostPropsWebhookDisplayName, "missing webhook_display_name prop")
|
|
|
|
_, appErr = th.App.CreateWebhookPost(th.Context, hook.UserId, th.BasicChannel, "foo", "user", "http://iconurl", "", nil, model.PostTypeSystemGeneric, "", nil)
|
|
require.NotNil(t, appErr, "Should have failed - bad post type")
|
|
|
|
expectedText := "`<>|<>|`"
|
|
post, appErr = th.App.CreateWebhookPost(th.Context, hook.UserId, th.BasicChannel, expectedText, "user", "http://iconurl", "", model.StringInterface{
|
|
model.PostPropsAttachments: []*model.SlackAttachment{
|
|
{
|
|
Text: "text",
|
|
},
|
|
},
|
|
model.PostPropsWebhookDisplayName: hook.DisplayName,
|
|
}, model.PostTypeSlackAttachment, "", nil)
|
|
require.Nil(t, appErr)
|
|
assert.Equal(t, expectedText, post.Message)
|
|
|
|
expectedText = "< | \n|\n>"
|
|
post, appErr = th.App.CreateWebhookPost(th.Context, hook.UserId, th.BasicChannel, expectedText, "user", "http://iconurl", "", model.StringInterface{
|
|
model.PostPropsAttachments: []*model.SlackAttachment{
|
|
{
|
|
Text: "text",
|
|
},
|
|
},
|
|
model.PostPropsWebhookDisplayName: hook.DisplayName,
|
|
}, model.PostTypeSlackAttachment, "", nil)
|
|
require.Nil(t, appErr)
|
|
assert.Equal(t, expectedText, post.Message)
|
|
|
|
expectedText = `commit bc95839e4a430ace453e8b209a3723c000c1729a
|
|
Author: foo <foo@example.org>
|
|
Date: Thu Mar 1 19:46:54 2018 +0300
|
|
|
|
commit message 2
|
|
|
|
test | 1 +
|
|
1 file changed, 1 insertion(+)
|
|
|
|
commit 5df78b7139b543997838071cd912e375d8bd69b2
|
|
Author: foo <foo@example.org>
|
|
Date: Thu Mar 1 19:46:48 2018 +0300
|
|
|
|
commit message 1
|
|
|
|
test | 3 +++
|
|
1 file changed, 3 insertions(+)`
|
|
post, appErr = th.App.CreateWebhookPost(th.Context, hook.UserId, th.BasicChannel, expectedText, "user", "http://iconurl", "", model.StringInterface{
|
|
model.PostPropsAttachments: []*model.SlackAttachment{
|
|
{
|
|
Text: "text",
|
|
},
|
|
},
|
|
model.PostPropsWebhookDisplayName: hook.DisplayName,
|
|
}, model.PostTypeSlackAttachment, "", nil)
|
|
require.Nil(t, appErr)
|
|
assert.Equal(t, expectedText, post.Message)
|
|
|
|
t.Run("should set webhook creator status to online", func(t *testing.T) {
|
|
testCluster.ClearMessages()
|
|
_, appErr := th.App.CreateWebhookPost(th.Context, hook.UserId, th.BasicChannel, "text", "", "", "", model.StringInterface{}, model.PostTypeDefault, "", nil)
|
|
require.Nil(t, appErr)
|
|
|
|
msgs := testCluster.SelectMessages(func(msg *model.ClusterMessage) bool {
|
|
event, err := model.WebSocketEventFromJSON(bytes.NewReader(msg.Data))
|
|
return err == nil && event.EventType() == model.WebsocketEventPosted
|
|
})
|
|
require.Len(t, msgs, 1)
|
|
// We know there will be no error from the filter condition.
|
|
event, _ := model.WebSocketEventFromJSON(bytes.NewReader(msgs[0].Data))
|
|
assert.Equal(t, false, event.GetData()["set_online"])
|
|
})
|
|
}
|
|
|
|
func TestCreateWebhookPostWithOverriddenIcon(t *testing.T) {
|
|
th := Setup(t).InitBasic(t)
|
|
|
|
th.App.UpdateConfig(func(cfg *model.Config) {
|
|
*cfg.ServiceSettings.EnableIncomingWebhooks = true
|
|
*cfg.ServiceSettings.EnablePostIconOverride = true
|
|
})
|
|
|
|
hook, appErr := th.App.CreateIncomingWebhookForChannel(th.BasicUser.Id, th.BasicChannel, &model.IncomingWebhook{ChannelId: th.BasicChannel.Id})
|
|
require.Nil(t, appErr)
|
|
|
|
t.Run("should set props based on icon_url", func(t *testing.T) {
|
|
post, appErr := th.App.CreateWebhookPost(
|
|
th.Context,
|
|
hook.UserId,
|
|
th.BasicChannel,
|
|
"test post",
|
|
"",
|
|
"https://example.com/icon.png",
|
|
"",
|
|
nil,
|
|
"",
|
|
"",
|
|
nil,
|
|
)
|
|
|
|
require.Nil(t, appErr)
|
|
assert.Equal(t, "https://example.com/icon.png", post.GetProp(model.PostPropsOverrideIconURL))
|
|
|
|
clientPost := th.App.PreparePostForClient(th.Context, post, &model.PreparePostForClientOpts{IsNewPost: true})
|
|
|
|
assert.Equal(t, "https://example.com/icon.png", clientPost.GetProp(model.PostPropsOverrideIconURL))
|
|
})
|
|
|
|
t.Run("should set props based on icon_emoji", func(t *testing.T) {
|
|
post, appErr := th.App.CreateWebhookPost(
|
|
th.Context,
|
|
hook.UserId,
|
|
th.BasicChannel,
|
|
"test post",
|
|
"",
|
|
"",
|
|
"smile",
|
|
nil,
|
|
"",
|
|
"",
|
|
nil,
|
|
)
|
|
|
|
require.Nil(t, appErr)
|
|
assert.Equal(t, "smile", post.GetProp(model.PostPropsOverrideIconEmoji))
|
|
|
|
clientPost := th.App.PreparePostForClient(th.Context, post, &model.PreparePostForClientOpts{IsNewPost: true})
|
|
|
|
assert.Equal(t, "/static/emoji/1f604.png", clientPost.GetProp(model.PostPropsOverrideIconURL))
|
|
})
|
|
|
|
t.Run("should set props based on icon_emoji (using a custom emoji)", func(t *testing.T) {
|
|
emoji := th.CreateEmoji(t)
|
|
|
|
post, appErr := th.App.CreateWebhookPost(
|
|
th.Context,
|
|
hook.UserId,
|
|
th.BasicChannel,
|
|
"test post",
|
|
"",
|
|
"",
|
|
emoji.Name,
|
|
nil,
|
|
"",
|
|
"",
|
|
nil,
|
|
)
|
|
|
|
require.Nil(t, appErr)
|
|
assert.Equal(t, emoji.Name, post.GetProp(model.PostPropsOverrideIconEmoji))
|
|
|
|
clientPost := th.App.PreparePostForClient(th.Context, post, &model.PreparePostForClientOpts{IsNewPost: true})
|
|
|
|
assert.Equal(t, fmt.Sprintf("/api/v4/emoji/%s/image", emoji.Id), clientPost.GetProp(model.PostPropsOverrideIconURL))
|
|
})
|
|
|
|
t.Run("should set props based on icon_emoji (with colons around emoji name)", func(t *testing.T) {
|
|
post, appErr := th.App.CreateWebhookPost(
|
|
th.Context,
|
|
hook.UserId,
|
|
th.BasicChannel,
|
|
"test post",
|
|
"",
|
|
"",
|
|
":smile:",
|
|
nil,
|
|
"",
|
|
"",
|
|
nil,
|
|
)
|
|
|
|
require.Nil(t, appErr)
|
|
assert.Equal(t, ":smile:", post.GetProp(model.PostPropsOverrideIconEmoji))
|
|
|
|
clientPost := th.App.PreparePostForClient(th.Context, post, &model.PreparePostForClientOpts{IsNewPost: true})
|
|
|
|
assert.Equal(t, "/static/emoji/1f604.png", clientPost.GetProp(model.PostPropsOverrideIconURL))
|
|
})
|
|
}
|
|
|
|
func TestCreateWebhookPostWithPriority(t *testing.T) {
|
|
mainHelper.Parallel(t)
|
|
testCluster := &testlib.FakeClusterInterface{}
|
|
th := SetupWithClusterMock(t, testCluster).InitBasic(t)
|
|
|
|
th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableIncomingWebhooks = true })
|
|
|
|
hook, appErr := th.App.CreateIncomingWebhookForChannel(th.BasicUser.Id, th.BasicChannel, &model.IncomingWebhook{ChannelId: th.BasicChannel.Id})
|
|
require.Nil(t, appErr)
|
|
defer func() {
|
|
appErr := th.App.DeleteIncomingWebhook(hook.Id)
|
|
require.Nil(t, appErr, "Error cleaning up webhook")
|
|
}()
|
|
|
|
testConditions := []model.PostPriority{
|
|
{
|
|
Priority: model.NewPointer("high"),
|
|
RequestedAck: model.NewPointer(true),
|
|
PersistentNotifications: model.NewPointer(false),
|
|
},
|
|
{
|
|
Priority: model.NewPointer(""),
|
|
RequestedAck: model.NewPointer(true),
|
|
PersistentNotifications: model.NewPointer(false),
|
|
},
|
|
{
|
|
Priority: model.NewPointer("urgent"),
|
|
RequestedAck: model.NewPointer(false),
|
|
PersistentNotifications: model.NewPointer(true),
|
|
},
|
|
}
|
|
|
|
for _, conditions := range testConditions {
|
|
post, appErr := th.App.CreateWebhookPost(th.Context, hook.UserId, th.BasicChannel, "foo @"+th.BasicUser.Username, "user", "http://iconurl", "",
|
|
model.StringInterface{model.PostPropsWebhookDisplayName: hook.DisplayName},
|
|
model.PostTypeSlackAttachment,
|
|
"",
|
|
&conditions,
|
|
)
|
|
|
|
require.Nil(t, appErr)
|
|
|
|
assert.Equal(t, post.Message, "foo @"+th.BasicUser.Username)
|
|
assert.Contains(t, post.GetProps(), model.PostPropsFromWebhook, "missing from_webhook prop")
|
|
assert.Equal(t, *conditions.Priority, *post.GetPriority().Priority)
|
|
assert.Equal(t, *conditions.RequestedAck, *post.GetPriority().RequestedAck)
|
|
assert.Equal(t, *conditions.PersistentNotifications, *post.GetPriority().PersistentNotifications)
|
|
}
|
|
}
|
|
|
|
func TestCreateWebhookPostLinks(t *testing.T) {
|
|
mainHelper.Parallel(t)
|
|
th := Setup(t).InitBasic(t)
|
|
|
|
th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableIncomingWebhooks = true })
|
|
|
|
hook, appErr := th.App.CreateIncomingWebhookForChannel(th.BasicUser.Id, th.BasicChannel, &model.IncomingWebhook{ChannelId: th.BasicChannel.Id})
|
|
require.Nil(t, appErr)
|
|
defer func() {
|
|
appErr := th.App.DeleteIncomingWebhook(hook.Id)
|
|
require.Nil(t, appErr, "Error cleaning up webhook")
|
|
}()
|
|
|
|
for name, tc := range map[string]struct {
|
|
input string
|
|
expectedOutput string
|
|
}{
|
|
"if statement": {
|
|
input: "`if(status_int < QUERY_UNKNOWN || status_int >= QUERY_STATUS_MAX)`",
|
|
expectedOutput: "`if(status_int < QUERY_UNKNOWN || status_int >= QUERY_STATUS_MAX)`",
|
|
},
|
|
"angle bracket link": {
|
|
input: "<https://mattermost.com|Mattermost>",
|
|
expectedOutput: "[Mattermost](https://mattermost.com)",
|
|
},
|
|
} {
|
|
t.Run(name, func(t *testing.T) {
|
|
post, appErr := th.App.CreateWebhookPost(th.Context, hook.UserId, th.BasicChannel, tc.input, "", "", "", model.StringInterface{}, "", "", nil)
|
|
require.Nil(t, appErr)
|
|
require.Equal(t, tc.expectedOutput, post.Message)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestSplitWebhookPost(t *testing.T) {
|
|
mainHelper.Parallel(t)
|
|
type TestCase struct {
|
|
Post *model.Post
|
|
Expected []*model.Post
|
|
}
|
|
|
|
maxPostSize := 10000
|
|
|
|
for name, tc := range map[string]TestCase{
|
|
"LongPost": {
|
|
Post: &model.Post{
|
|
Message: strings.Repeat("本", maxPostSize*3/2),
|
|
},
|
|
Expected: []*model.Post{
|
|
{
|
|
Message: strings.Repeat("本", maxPostSize),
|
|
},
|
|
{
|
|
Message: strings.Repeat("本", maxPostSize/2),
|
|
},
|
|
},
|
|
},
|
|
"LongPostAndMultipleAttachments": {
|
|
Post: &model.Post{
|
|
Message: strings.Repeat("本", maxPostSize*3/2),
|
|
Props: map[string]any{
|
|
model.PostPropsAttachments: []*model.SlackAttachment{
|
|
{
|
|
Text: strings.Repeat("本", 1000),
|
|
},
|
|
{
|
|
Text: strings.Repeat("本", 2000),
|
|
},
|
|
{
|
|
Text: strings.Repeat("本", model.PostPropsMaxUserRunes-1000),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
Expected: []*model.Post{
|
|
{
|
|
Message: strings.Repeat("本", maxPostSize),
|
|
},
|
|
{
|
|
Message: strings.Repeat("本", maxPostSize/2),
|
|
Props: map[string]any{
|
|
model.PostPropsAttachments: []*model.SlackAttachment{
|
|
{
|
|
Text: strings.Repeat("本", 1000),
|
|
},
|
|
{
|
|
Text: strings.Repeat("本", 2000),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
Props: map[string]any{
|
|
model.PostPropsAttachments: []*model.SlackAttachment{
|
|
{
|
|
Text: strings.Repeat("本", model.PostPropsMaxUserRunes-1000),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
"UnsplittableProps": {
|
|
Post: &model.Post{
|
|
Message: "foo",
|
|
Props: map[string]any{
|
|
"foo": strings.Repeat("x", model.PostPropsMaxUserRunes*2),
|
|
},
|
|
},
|
|
},
|
|
} {
|
|
t.Run(name, func(t *testing.T) {
|
|
splits, err := splitWebhookPost(tc.Post, maxPostSize)
|
|
if tc.Expected == nil {
|
|
require.NotNil(t, err)
|
|
} else {
|
|
require.Nil(t, err)
|
|
}
|
|
assert.Equal(t, len(tc.Expected), len(splits))
|
|
for i, split := range splits {
|
|
if i < len(tc.Expected) {
|
|
assert.Equal(t, tc.Expected[i].Message, split.Message)
|
|
assert.Equal(t, tc.Expected[i].GetProp(model.PostPropsAttachments), split.GetProp(model.PostPropsAttachments))
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func makePost(message int, attachments []int) *model.Post {
|
|
var props model.StringInterface
|
|
if len(attachments) > 0 {
|
|
sa := make([]*model.SlackAttachment, 0, len(attachments))
|
|
for _, a := range attachments {
|
|
attach := &model.SlackAttachment{
|
|
Text: strings.Repeat("那", a),
|
|
}
|
|
sa = append(sa, attach)
|
|
}
|
|
props = map[string]any{model.PostPropsAttachments: sa}
|
|
}
|
|
post := &model.Post{
|
|
Message: strings.Repeat("那", message),
|
|
Props: props,
|
|
}
|
|
return post
|
|
}
|
|
|
|
func TestSplitWebhookPostAttachments(t *testing.T) {
|
|
mainHelper.Parallel(t)
|
|
maxPostSize := 10000
|
|
testCases := []struct {
|
|
name string
|
|
post *model.Post
|
|
expected []*model.Post
|
|
}{
|
|
{
|
|
// makePost(messageLength, []int{attachmentLength, ...})
|
|
name: "no split",
|
|
post: makePost(10, []int{100, 150, 200}),
|
|
expected: []*model.Post{makePost(10, []int{100, 150, 200})},
|
|
},
|
|
{
|
|
name: "split into 2",
|
|
post: makePost(maxPostSize-1, []int{model.PostPropsMaxUserRunes * 3 / 4, model.PostPropsMaxUserRunes * 1 / 4}),
|
|
expected: []*model.Post{
|
|
makePost(maxPostSize-1, []int{model.PostPropsMaxUserRunes * 3 / 4}),
|
|
makePost(0, []int{model.PostPropsMaxUserRunes * 1 / 4}),
|
|
},
|
|
},
|
|
{
|
|
name: "split into 3",
|
|
post: makePost(maxPostSize*3/2, []int{1000, 2000, model.PostPropsMaxUserRunes - 1000}),
|
|
expected: []*model.Post{
|
|
makePost(maxPostSize, nil),
|
|
makePost(maxPostSize/2, []int{1000, 2000}),
|
|
makePost(0, []int{model.PostPropsMaxUserRunes - 1000}),
|
|
},
|
|
},
|
|
{
|
|
name: "MM-24644 split into 3",
|
|
post: makePost(maxPostSize*3/2, []int{5150, 2000, model.PostPropsMaxUserRunes - 1000}),
|
|
expected: []*model.Post{
|
|
makePost(maxPostSize, nil),
|
|
makePost(maxPostSize/2, []int{5150, 2000}),
|
|
makePost(0, []int{model.PostPropsMaxUserRunes - 1000}),
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, tc := range testCases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
splits, err := splitWebhookPost(tc.post, maxPostSize)
|
|
if tc.expected == nil {
|
|
require.NotNil(t, err)
|
|
} else {
|
|
require.Nil(t, err)
|
|
}
|
|
assert.Equal(t, len(tc.expected), len(splits))
|
|
for i, split := range splits {
|
|
if i < len(tc.expected) {
|
|
assert.Equal(t, tc.expected[i].Message, split.Message, i)
|
|
assert.Equal(t, tc.expected[i].GetProp(model.PostPropsAttachments), split.GetProp(model.PostPropsAttachments), i)
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestCreateOutGoingWebhookWithUsernameAndIconURL(t *testing.T) {
|
|
mainHelper.Parallel(t)
|
|
th := Setup(t).InitBasic(t)
|
|
|
|
outgoingWebhook := model.OutgoingWebhook{
|
|
ChannelId: th.BasicChannel.Id,
|
|
TeamId: th.BasicChannel.TeamId,
|
|
CallbackURLs: []string{"http://nowhere.com"},
|
|
Username: "some-user-name",
|
|
IconURL: "http://some-icon/",
|
|
DisplayName: "some-display-name",
|
|
Description: "some-description",
|
|
CreatorId: th.BasicUser.Id,
|
|
}
|
|
|
|
th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableOutgoingWebhooks = true })
|
|
|
|
createdHook, err := th.App.CreateOutgoingWebhook(&outgoingWebhook)
|
|
require.Nil(t, err)
|
|
|
|
assert.NotNil(t, createdHook, "should not be null")
|
|
|
|
assert.Equal(t, createdHook.ChannelId, outgoingWebhook.ChannelId)
|
|
assert.Equal(t, createdHook.TeamId, outgoingWebhook.TeamId)
|
|
assert.Equal(t, createdHook.CallbackURLs, outgoingWebhook.CallbackURLs)
|
|
assert.Equal(t, createdHook.Username, outgoingWebhook.Username)
|
|
assert.Equal(t, createdHook.IconURL, outgoingWebhook.IconURL)
|
|
assert.Equal(t, createdHook.DisplayName, outgoingWebhook.DisplayName)
|
|
assert.Equal(t, createdHook.Description, outgoingWebhook.Description)
|
|
}
|
|
|
|
func TestTriggerOutGoingWebhookWithUsernameAndIconURL(t *testing.T) {
|
|
mainHelper.Parallel(t)
|
|
getPayload := func(hook *model.OutgoingWebhook, th *TestHelper, channel *model.Channel) *model.OutgoingWebhookPayload {
|
|
return &model.OutgoingWebhookPayload{
|
|
Token: hook.Token,
|
|
TeamId: hook.TeamId,
|
|
TeamDomain: th.BasicTeam.Name,
|
|
ChannelId: channel.Id,
|
|
ChannelName: channel.Name,
|
|
Timestamp: th.BasicPost.CreateAt,
|
|
UserId: th.BasicPost.UserId,
|
|
UserName: th.BasicUser.Username,
|
|
PostId: th.BasicPost.Id,
|
|
Text: th.BasicPost.Message,
|
|
TriggerWord: "Abracadabra",
|
|
FileIds: strings.Join(th.BasicPost.FileIds, ","),
|
|
}
|
|
}
|
|
|
|
waitUntilWebhookResponseIsCreatedAsPost := func(channel *model.Channel, th *TestHelper, createdPost chan *model.Post) {
|
|
go func() {
|
|
for range 5 {
|
|
time.Sleep(time.Second)
|
|
posts, _ := th.App.GetPosts(th.Context, channel.Id, 0, 5)
|
|
if len(posts.Posts) > 0 {
|
|
for _, post := range posts.Posts {
|
|
createdPost <- post
|
|
return
|
|
}
|
|
}
|
|
}
|
|
}()
|
|
}
|
|
|
|
type TestCaseOutgoing struct {
|
|
EnablePostUsernameOverride bool
|
|
EnablePostIconOverride bool
|
|
ExpectedUsername string
|
|
ExpectedIconURL string
|
|
WebhookResponse *model.OutgoingWebhookResponse
|
|
}
|
|
|
|
createOutgoingWebhook := func(channel *model.Channel, testCallBackURL string, th *TestHelper) (*model.OutgoingWebhook, *model.AppError) {
|
|
outgoingWebhook := model.OutgoingWebhook{
|
|
ChannelId: channel.Id,
|
|
TeamId: channel.TeamId,
|
|
CallbackURLs: []string{testCallBackURL},
|
|
Username: "some-user-name",
|
|
IconURL: "http://some-icon/",
|
|
DisplayName: "some-display-name",
|
|
Description: "some-description",
|
|
CreatorId: th.BasicUser.Id,
|
|
TriggerWords: []string{"Abracadabra"},
|
|
ContentType: "application/json",
|
|
}
|
|
|
|
return th.App.CreateOutgoingWebhook(&outgoingWebhook)
|
|
}
|
|
|
|
getTestCases := func() map[string]TestCaseOutgoing {
|
|
webHookResponse := "sample response text from test server"
|
|
testCasesOutgoing := map[string]TestCaseOutgoing{
|
|
"Should override username and Icon": {
|
|
EnablePostUsernameOverride: true,
|
|
EnablePostIconOverride: true,
|
|
ExpectedUsername: "some-user-name",
|
|
ExpectedIconURL: "http://some-icon/",
|
|
},
|
|
"Should not override username and Icon": {
|
|
EnablePostUsernameOverride: false,
|
|
EnablePostIconOverride: false,
|
|
},
|
|
"Should not override username and Icon if the webhook response already has it": {
|
|
EnablePostUsernameOverride: true,
|
|
EnablePostIconOverride: true,
|
|
ExpectedUsername: "webhookuser",
|
|
ExpectedIconURL: "http://webhook/icon",
|
|
WebhookResponse: &model.OutgoingWebhookResponse{Text: &webHookResponse, Username: "webhookuser", IconURL: "http://webhook/icon"},
|
|
},
|
|
}
|
|
return testCasesOutgoing
|
|
}
|
|
|
|
th := Setup(t).InitBasic(t)
|
|
|
|
th.App.UpdateConfig(func(cfg *model.Config) {
|
|
*cfg.ServiceSettings.AllowedUntrustedInternalConnections = "localhost,127.0.0.1"
|
|
})
|
|
createdPost := make(chan *model.Post)
|
|
|
|
for name, testCase := range getTestCases() {
|
|
t.Run(name, func(t *testing.T) {
|
|
th.App.UpdateConfig(func(cfg *model.Config) {
|
|
*cfg.ServiceSettings.EnableOutgoingWebhooks = true
|
|
*cfg.ServiceSettings.EnablePostUsernameOverride = testCase.EnablePostUsernameOverride
|
|
*cfg.ServiceSettings.EnablePostIconOverride = testCase.EnablePostIconOverride
|
|
})
|
|
|
|
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
if testCase.WebhookResponse != nil {
|
|
js, jsonErr := json.Marshal(testCase.WebhookResponse)
|
|
require.NoError(t, jsonErr)
|
|
_, err := w.Write(js)
|
|
require.NoError(t, err)
|
|
} else {
|
|
_, err := w.Write([]byte(`{"text": "sample response text from test server"}`))
|
|
require.NoError(t, err)
|
|
}
|
|
}))
|
|
defer ts.Close()
|
|
|
|
channel := th.CreateChannel(t, th.BasicTeam)
|
|
hook, _ := createOutgoingWebhook(channel, ts.URL, th)
|
|
payload := getPayload(hook, th, channel)
|
|
|
|
th.App.TriggerWebhook(th.Context, payload, hook, th.BasicPost, channel)
|
|
|
|
waitUntilWebhookResponseIsCreatedAsPost(channel, th, createdPost)
|
|
|
|
select {
|
|
case webhookPost := <-createdPost:
|
|
assert.Equal(t, webhookPost.Message, "sample response text from test server")
|
|
assert.Equal(t, webhookPost.GetProp(model.PostPropsFromWebhook), "true")
|
|
if testCase.ExpectedIconURL != "" {
|
|
assert.Equal(t, webhookPost.GetProp(model.PostPropsOverrideIconURL), testCase.ExpectedIconURL)
|
|
} else {
|
|
assert.Nil(t, webhookPost.GetProp(model.PostPropsOverrideIconURL))
|
|
}
|
|
|
|
if testCase.ExpectedUsername != "" {
|
|
assert.Equal(t, webhookPost.GetProp(model.PostPropsOverrideUsername), testCase.ExpectedUsername)
|
|
} else {
|
|
assert.Nil(t, webhookPost.GetProp(model.PostPropsOverrideUsername))
|
|
}
|
|
case <-time.After(5 * time.Second):
|
|
require.Fail(t, "Timeout, webhook response not created as post")
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestTriggerOutGoingWebhookWithMultipleURLs(t *testing.T) {
|
|
mainHelper.Parallel(t)
|
|
getPayload := func(hook *model.OutgoingWebhook, th *TestHelper, channel *model.Channel) *model.OutgoingWebhookPayload {
|
|
return &model.OutgoingWebhookPayload{
|
|
Token: hook.Token,
|
|
TeamId: hook.TeamId,
|
|
TeamDomain: th.BasicTeam.Name,
|
|
ChannelId: channel.Id,
|
|
ChannelName: channel.Name,
|
|
Timestamp: th.BasicPost.CreateAt,
|
|
UserId: th.BasicPost.UserId,
|
|
UserName: th.BasicUser.Username,
|
|
PostId: th.BasicPost.Id,
|
|
Text: th.BasicPost.Message,
|
|
TriggerWord: "Abracadabra",
|
|
FileIds: strings.Join(th.BasicPost.FileIds, ","),
|
|
}
|
|
}
|
|
|
|
createOutgoingWebhook := func(channel *model.Channel, testCallBackURLs []string, th *TestHelper) (*model.OutgoingWebhook, *model.AppError) {
|
|
outgoingWebhook := model.OutgoingWebhook{
|
|
ChannelId: channel.Id,
|
|
TeamId: channel.TeamId,
|
|
CallbackURLs: testCallBackURLs,
|
|
Username: "some-user-name",
|
|
IconURL: "http://some-website.com/assets/some-icon.png",
|
|
DisplayName: "some-display-name",
|
|
Description: "some-description",
|
|
CreatorId: th.BasicUser.Id,
|
|
TriggerWords: []string{"Abracadabra"},
|
|
ContentType: "application/json",
|
|
}
|
|
|
|
return th.App.CreateOutgoingWebhook(&outgoingWebhook)
|
|
}
|
|
|
|
chanTs1 := make(chan string, 1)
|
|
ts1 := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
chanTs1 <- "webhook received!"
|
|
}))
|
|
defer ts1.Close()
|
|
|
|
chanTs2 := make(chan string, 1)
|
|
ts2 := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
chanTs2 <- "webhook received!"
|
|
}))
|
|
defer ts2.Close()
|
|
|
|
th := Setup(t).InitBasic(t)
|
|
|
|
th.App.UpdateConfig(func(cfg *model.Config) {
|
|
*cfg.ServiceSettings.AllowedUntrustedInternalConnections = "localhost,127.0.0.1"
|
|
})
|
|
|
|
for name, testCase := range map[string]struct {
|
|
CallBackURLs []string
|
|
}{
|
|
"One WebhookURL": {
|
|
CallBackURLs: []string{ts1.URL},
|
|
},
|
|
"Two WebhookURLs": {
|
|
CallBackURLs: []string{ts1.URL, ts2.URL},
|
|
},
|
|
} {
|
|
t.Run(name, func(t *testing.T) {
|
|
th.App.UpdateConfig(func(cfg *model.Config) {
|
|
*cfg.ServiceSettings.EnableOutgoingWebhooks = true
|
|
})
|
|
channel := th.CreateChannel(t, th.BasicTeam)
|
|
hook, _ := createOutgoingWebhook(channel, testCase.CallBackURLs, th)
|
|
payload := getPayload(hook, th, channel)
|
|
|
|
th.App.TriggerWebhook(th.Context, payload, hook, th.BasicPost, channel)
|
|
|
|
select {
|
|
case webhookResponse := <-chanTs1:
|
|
require.Equal(t, "webhook received!", webhookResponse)
|
|
|
|
case <-time.After(5 * time.Second):
|
|
require.Fail(t, "Timeout, webhook URL 1 response not received")
|
|
}
|
|
|
|
if len(testCase.CallBackURLs) > 1 {
|
|
select {
|
|
case webhookResponse := <-chanTs2:
|
|
require.Equal(t, "webhook received!", webhookResponse)
|
|
|
|
case <-time.After(5 * time.Second):
|
|
require.Fail(t, "Timeout, webhook URL 2 response not received")
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
type InfiniteReader struct {
|
|
Prefix string
|
|
}
|
|
|
|
func (r InfiniteReader) Read(p []byte) (n int, err error) {
|
|
for i := range p {
|
|
p[i] = 'a'
|
|
}
|
|
|
|
return len(p), nil
|
|
}
|
|
|
|
func TestDoOutgoingWebhookRequest(t *testing.T) {
|
|
mainHelper.Parallel(t)
|
|
th := Setup(t)
|
|
|
|
th.App.UpdateConfig(func(cfg *model.Config) {
|
|
cfg.ServiceSettings.AllowedUntrustedInternalConnections = model.NewPointer("127.0.0.1")
|
|
*cfg.ServiceSettings.EnableOutgoingWebhooks = true
|
|
})
|
|
|
|
t.Run("with a valid response", func(t *testing.T) {
|
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
_, err := io.Copy(w, strings.NewReader(`{"text": "Hello, World!"}`))
|
|
require.NoError(t, err)
|
|
}))
|
|
defer server.Close()
|
|
|
|
resp, err := th.App.doOutgoingWebhookRequest(server.URL, strings.NewReader(""), "application/json", nil)
|
|
require.NoError(t, err)
|
|
|
|
require.NotNil(t, resp)
|
|
assert.NotNil(t, resp.Text)
|
|
assert.Equal(t, "Hello, World!", *resp.Text)
|
|
})
|
|
|
|
t.Run("with an invalid response", func(t *testing.T) {
|
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
_, err := io.Copy(w, strings.NewReader("aaaaaaaa"))
|
|
require.NoError(t, err)
|
|
}))
|
|
defer server.Close()
|
|
|
|
_, err := th.App.doOutgoingWebhookRequest(server.URL, strings.NewReader(""), "application/json", nil)
|
|
require.Error(t, err)
|
|
require.Equal(t, "api.unmarshal_error", err.(*model.AppError).Id)
|
|
})
|
|
|
|
t.Run("with a large, valid response", func(t *testing.T) {
|
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
// Don't check the error here as the client may disconnect after hitting
|
|
// the response size limit, causing a broken pipe error that we can't avoid
|
|
_, _ = io.Copy(w, io.MultiReader(strings.NewReader(`{"text": "`), InfiniteReader{}, strings.NewReader(`"}`)))
|
|
}))
|
|
defer server.Close()
|
|
|
|
_, err := th.App.doOutgoingWebhookRequest(server.URL, strings.NewReader(""), "application/json", nil)
|
|
require.Error(t, err)
|
|
require.Equal(t, "api.unmarshal_error", err.(*model.AppError).Id)
|
|
})
|
|
|
|
t.Run("with a large, invalid response", func(t *testing.T) {
|
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
// Don't check the error here as the client may disconnect after hitting
|
|
// the response size limit, causing a broken pipe error that we can't avoid
|
|
_, _ = io.Copy(w, InfiniteReader{})
|
|
}))
|
|
defer server.Close()
|
|
|
|
_, err := th.App.doOutgoingWebhookRequest(server.URL, strings.NewReader(""), "application/json", nil)
|
|
require.Error(t, err)
|
|
require.Equal(t, "api.unmarshal_error", err.(*model.AppError).Id)
|
|
})
|
|
|
|
t.Run("with a slow response", func(t *testing.T) {
|
|
releaseHandler := make(chan any)
|
|
|
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
// Don't actually handle the response, allowing the app to timeout.
|
|
<-releaseHandler
|
|
}))
|
|
defer server.Close()
|
|
defer close(releaseHandler)
|
|
|
|
th.App.UpdateConfig(func(cfg *model.Config) {
|
|
cfg.ServiceSettings.OutgoingIntegrationRequestsTimeout = model.NewPointer(int64(1))
|
|
})
|
|
|
|
_, err := th.App.doOutgoingWebhookRequest(server.URL, strings.NewReader(""), "application/json", nil)
|
|
require.Error(t, err)
|
|
require.IsType(t, &url.Error{}, err)
|
|
})
|
|
|
|
t.Run("with a slow response, long timeout configured", func(t *testing.T) {
|
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
time.Sleep(1 * time.Second)
|
|
|
|
_, err := io.Copy(w, strings.NewReader(`{"text": "Hello, World!"}`))
|
|
require.NoError(t, err)
|
|
}))
|
|
defer server.Close()
|
|
|
|
th.App.UpdateConfig(func(cfg *model.Config) {
|
|
cfg.ServiceSettings.OutgoingIntegrationRequestsTimeout = model.NewPointer(int64(2))
|
|
})
|
|
|
|
resp, err := th.App.doOutgoingWebhookRequest(server.URL, strings.NewReader(""), "application/json", nil)
|
|
require.NoError(t, err)
|
|
require.NotNil(t, resp)
|
|
assert.NotNil(t, resp.Text)
|
|
assert.Equal(t, "Hello, World!", *resp.Text)
|
|
})
|
|
|
|
t.Run("without response", func(t *testing.T) {
|
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
}))
|
|
defer server.Close()
|
|
|
|
resp, err := th.App.doOutgoingWebhookRequest(server.URL, strings.NewReader(""), "application/json", nil)
|
|
require.NoError(t, err)
|
|
require.Nil(t, resp)
|
|
})
|
|
|
|
t.Run("with auth token", func(t *testing.T) {
|
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
_, err := io.Copy(w, strings.NewReader(fmt.Sprintf(`{"text":"%s"}`, r.Header.Get("Authorization"))))
|
|
require.NoError(t, err)
|
|
}))
|
|
defer server.Close()
|
|
|
|
resp, err := th.App.doOutgoingWebhookRequest(server.URL, strings.NewReader(""), "application/json", &model.OutgoingOAuthConnectionToken{
|
|
AccessToken: "test",
|
|
TokenType: "Bearer",
|
|
})
|
|
require.NoError(t, err)
|
|
require.Equal(t, `Bearer test`, *resp.Text)
|
|
})
|
|
}
|