From 4fcae3d06d1099ade682df96e41baf4f5ac56620 Mon Sep 17 00:00:00 2001 From: KN4CK3R Date: Mon, 21 Jun 2021 04:12:19 +0200 Subject: [PATCH] Add tests for all webhooks (#16214) * Added tests for MS Teams. * Added tests for Dingtalk. * Added tests for Telegram. * Added tests for Feishu. * Added tests for Discord. * Added tests for closed issue and pullrequest comment. * Added tests for Matrix. * Trim all spaces. * Added tests for Slack. * Added JSONPayload tests. * Added general tests. * Replaced duplicated code. Co-authored-by: Lunny Xiao --- services/webhook/dingtalk.go | 123 ++------ services/webhook/dingtalk_test.go | 201 ++++++++++++- services/webhook/discord.go | 202 ++----------- services/webhook/discord_test.go | 245 +++++++++++++++ services/webhook/feishu.go | 23 +- services/webhook/feishu_test.go | 172 +++++++++++ services/webhook/general.go | 11 +- services/webhook/general_test.go | 464 +++++++++++++++++++++++++++-- services/webhook/matrix_test.go | 198 +++++++++--- services/webhook/msteams.go | 479 ++++++++---------------------- services/webhook/msteams_test.go | 374 +++++++++++++++++++++++ services/webhook/slack.go | 114 +++---- services/webhook/slack_test.go | 186 +++++++++--- services/webhook/telegram.go | 51 ++-- services/webhook/telegram_test.go | 158 +++++++++- 15 files changed, 2110 insertions(+), 891 deletions(-) create mode 100644 services/webhook/discord_test.go create mode 100644 services/webhook/feishu_test.go create mode 100644 services/webhook/msteams_test.go diff --git a/services/webhook/dingtalk.go b/services/webhook/dingtalk.go index 0401464a4..d781b8c87 100644 --- a/services/webhook/dingtalk.go +++ b/services/webhook/dingtalk.go @@ -44,16 +44,7 @@ func (d *DingtalkPayload) Create(p *api.CreatePayload) (api.Payloader, error) { refName := git.RefEndName(p.Ref) title := fmt.Sprintf("[%s] %s %s created", p.Repo.FullName, p.RefType, refName) - return &DingtalkPayload{ - MsgType: "actionCard", - ActionCard: dingtalk.ActionCard{ - Text: title, - Title: title, - HideAvatar: "0", - SingleTitle: fmt.Sprintf("view ref %s", refName), - SingleURL: p.Repo.HTMLURL + "/src/" + refName, - }, - }, nil + return createDingtalkPayload(title, title, fmt.Sprintf("view ref %s", refName), p.Repo.HTMLURL+"/src/"+refName), nil } // Delete implements PayloadConvertor Delete method @@ -62,32 +53,14 @@ func (d *DingtalkPayload) Delete(p *api.DeletePayload) (api.Payloader, error) { refName := git.RefEndName(p.Ref) title := fmt.Sprintf("[%s] %s %s deleted", p.Repo.FullName, p.RefType, refName) - return &DingtalkPayload{ - MsgType: "actionCard", - ActionCard: dingtalk.ActionCard{ - Text: title, - Title: title, - HideAvatar: "0", - SingleTitle: fmt.Sprintf("view ref %s", refName), - SingleURL: p.Repo.HTMLURL + "/src/" + refName, - }, - }, nil + return createDingtalkPayload(title, title, fmt.Sprintf("view ref %s", refName), p.Repo.HTMLURL+"/src/"+refName), nil } // Fork implements PayloadConvertor Fork method func (d *DingtalkPayload) Fork(p *api.ForkPayload) (api.Payloader, error) { title := fmt.Sprintf("%s is forked to %s", p.Forkee.FullName, p.Repo.FullName) - return &DingtalkPayload{ - MsgType: "actionCard", - ActionCard: dingtalk.ActionCard{ - Text: title, - Title: title, - HideAvatar: "0", - SingleTitle: fmt.Sprintf("view forked repo %s", p.Repo.FullName), - SingleURL: p.Repo.HTMLURL, - }, - }, nil + return createDingtalkPayload(title, title, fmt.Sprintf("view forked repo %s", p.Repo.FullName), p.Repo.HTMLURL), nil } // Push implements PayloadConvertor Push method @@ -124,70 +97,32 @@ func (d *DingtalkPayload) Push(p *api.PushPayload) (api.Payloader, error) { strings.TrimRight(commit.Message, "\r\n")) + authorName // add linebreak to each commit but the last if i < len(p.Commits)-1 { - text += "\n" + text += "\r\n" } } - return &DingtalkPayload{ - MsgType: "actionCard", - ActionCard: dingtalk.ActionCard{ - Text: text, - Title: title, - HideAvatar: "0", - SingleTitle: linkText, - SingleURL: titleLink, - }, - }, nil + return createDingtalkPayload(title, text, linkText, titleLink), nil } // Issue implements PayloadConvertor Issue method func (d *DingtalkPayload) Issue(p *api.IssuePayload) (api.Payloader, error) { text, issueTitle, attachmentText, _ := getIssuesPayloadInfo(p, noneLinkFormatter, true) - return &DingtalkPayload{ - MsgType: "actionCard", - ActionCard: dingtalk.ActionCard{ - Text: text + "\r\n\r\n" + attachmentText, - //Markdown: "# " + title + "\n" + text, - Title: issueTitle, - HideAvatar: "0", - SingleTitle: "view issue", - SingleURL: p.Issue.HTMLURL, - }, - }, nil + return createDingtalkPayload(issueTitle, text+"\r\n\r\n"+attachmentText, "view issue", p.Issue.HTMLURL), nil } // IssueComment implements PayloadConvertor IssueComment method func (d *DingtalkPayload) IssueComment(p *api.IssueCommentPayload) (api.Payloader, error) { text, issueTitle, _ := getIssueCommentPayloadInfo(p, noneLinkFormatter, true) - return &DingtalkPayload{ - MsgType: "actionCard", - ActionCard: dingtalk.ActionCard{ - Text: text + "\r\n\r\n" + p.Comment.Body, - Title: issueTitle, - HideAvatar: "0", - SingleTitle: "view issue comment", - SingleURL: p.Comment.HTMLURL, - }, - }, nil + return createDingtalkPayload(issueTitle, text+"\r\n\r\n"+p.Comment.Body, "view issue comment", p.Comment.HTMLURL), nil } // PullRequest implements PayloadConvertor PullRequest method func (d *DingtalkPayload) PullRequest(p *api.PullRequestPayload) (api.Payloader, error) { text, issueTitle, attachmentText, _ := getPullRequestPayloadInfo(p, noneLinkFormatter, true) - return &DingtalkPayload{ - MsgType: "actionCard", - ActionCard: dingtalk.ActionCard{ - Text: text + "\r\n\r\n" + attachmentText, - //Markdown: "# " + title + "\n" + text, - Title: issueTitle, - HideAvatar: "0", - SingleTitle: "view pull request", - SingleURL: p.PullRequest.HTMLURL, - }, - }, nil + return createDingtalkPayload(issueTitle, text+"\r\n\r\n"+attachmentText, "view pull request", p.PullRequest.HTMLURL), nil } // Review implements PayloadConvertor Review method @@ -205,37 +140,17 @@ func (d *DingtalkPayload) Review(p *api.PullRequestPayload, event models.HookEve } - return &DingtalkPayload{ - MsgType: "actionCard", - ActionCard: dingtalk.ActionCard{ - Text: title + "\r\n\r\n" + text, - Title: title, - HideAvatar: "0", - SingleTitle: "view pull request", - SingleURL: p.PullRequest.HTMLURL, - }, - }, nil + return createDingtalkPayload(title, title+"\r\n\r\n"+text, "view pull request", p.PullRequest.HTMLURL), nil } // Repository implements PayloadConvertor Repository method func (d *DingtalkPayload) Repository(p *api.RepositoryPayload) (api.Payloader, error) { - var title, url string switch p.Action { case api.HookRepoCreated: - title = fmt.Sprintf("[%s] Repository created", p.Repository.FullName) - url = p.Repository.HTMLURL - return &DingtalkPayload{ - MsgType: "actionCard", - ActionCard: dingtalk.ActionCard{ - Text: title, - Title: title, - HideAvatar: "0", - SingleTitle: "view repository", - SingleURL: url, - }, - }, nil + title := fmt.Sprintf("[%s] Repository created", p.Repository.FullName) + return createDingtalkPayload(title, title, "view repository", p.Repository.HTMLURL), nil case api.HookRepoDeleted: - title = fmt.Sprintf("[%s] Repository deleted", p.Repository.FullName) + title := fmt.Sprintf("[%s] Repository deleted", p.Repository.FullName) return &DingtalkPayload{ MsgType: "text", Text: struct { @@ -253,16 +168,20 @@ func (d *DingtalkPayload) Repository(p *api.RepositoryPayload) (api.Payloader, e func (d *DingtalkPayload) Release(p *api.ReleasePayload) (api.Payloader, error) { text, _ := getReleasePayloadInfo(p, noneLinkFormatter, true) + return createDingtalkPayload(text, text, "view release", p.Release.URL), nil +} + +func createDingtalkPayload(title, text, singleTitle, singleURL string) *DingtalkPayload { return &DingtalkPayload{ MsgType: "actionCard", ActionCard: dingtalk.ActionCard{ - Text: text, - Title: text, + Text: strings.TrimSpace(text), + Title: strings.TrimSpace(title), HideAvatar: "0", - SingleTitle: "view release", - SingleURL: p.Release.URL, + SingleTitle: singleTitle, + SingleURL: singleURL, }, - }, nil + } } // GetDingtalkPayload converts a ding talk webhook into a DingtalkPayload diff --git a/services/webhook/dingtalk_test.go b/services/webhook/dingtalk_test.go index e5aa0fca3..213ad1a28 100644 --- a/services/webhook/dingtalk_test.go +++ b/services/webhook/dingtalk_test.go @@ -7,25 +7,202 @@ package webhook import ( "testing" + "code.gitea.io/gitea/models" api "code.gitea.io/gitea/modules/structs" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) -func TestGetDingTalkIssuesPayload(t *testing.T) { - p := issueTestPayload() - d := new(DingtalkPayload) - p.Action = api.HookIssueOpened - pl, err := d.Issue(p) +func TestDingTalkPayload(t *testing.T) { + t.Run("Create", func(t *testing.T) { + p := createTestPayload() + + d := new(DingtalkPayload) + pl, err := d.Create(p) + require.NoError(t, err) + require.NotNil(t, pl) + require.IsType(t, &DingtalkPayload{}, pl) + + assert.Equal(t, "[test/repo] branch test created", pl.(*DingtalkPayload).ActionCard.Text) + assert.Equal(t, "[test/repo] branch test created", pl.(*DingtalkPayload).ActionCard.Title) + assert.Equal(t, "view ref test", pl.(*DingtalkPayload).ActionCard.SingleTitle) + assert.Equal(t, "http://localhost:3000/test/repo/src/test", pl.(*DingtalkPayload).ActionCard.SingleURL) + }) + + t.Run("Delete", func(t *testing.T) { + p := deleteTestPayload() + + d := new(DingtalkPayload) + pl, err := d.Delete(p) + require.NoError(t, err) + require.NotNil(t, pl) + require.IsType(t, &DingtalkPayload{}, pl) + + assert.Equal(t, "[test/repo] branch test deleted", pl.(*DingtalkPayload).ActionCard.Text) + assert.Equal(t, "[test/repo] branch test deleted", pl.(*DingtalkPayload).ActionCard.Title) + assert.Equal(t, "view ref test", pl.(*DingtalkPayload).ActionCard.SingleTitle) + assert.Equal(t, "http://localhost:3000/test/repo/src/test", pl.(*DingtalkPayload).ActionCard.SingleURL) + }) + + t.Run("Fork", func(t *testing.T) { + p := forkTestPayload() + + d := new(DingtalkPayload) + pl, err := d.Fork(p) + require.NoError(t, err) + require.NotNil(t, pl) + require.IsType(t, &DingtalkPayload{}, pl) + + assert.Equal(t, "test/repo2 is forked to test/repo", pl.(*DingtalkPayload).ActionCard.Text) + assert.Equal(t, "test/repo2 is forked to test/repo", pl.(*DingtalkPayload).ActionCard.Title) + assert.Equal(t, "view forked repo test/repo", pl.(*DingtalkPayload).ActionCard.SingleTitle) + assert.Equal(t, "http://localhost:3000/test/repo", pl.(*DingtalkPayload).ActionCard.SingleURL) + }) + + t.Run("Push", func(t *testing.T) { + p := pushTestPayload() + + d := new(DingtalkPayload) + pl, err := d.Push(p) + require.NoError(t, err) + require.NotNil(t, pl) + require.IsType(t, &DingtalkPayload{}, pl) + + assert.Equal(t, "[2020558](http://localhost:3000/test/repo/commit/2020558fe2e34debb818a514715839cabd25e778) commit message - user1\r\n[2020558](http://localhost:3000/test/repo/commit/2020558fe2e34debb818a514715839cabd25e778) commit message - user1", pl.(*DingtalkPayload).ActionCard.Text) + assert.Equal(t, "[test/repo:test] 2 new commits", pl.(*DingtalkPayload).ActionCard.Title) + assert.Equal(t, "view commit 2020558...2020558", pl.(*DingtalkPayload).ActionCard.SingleTitle) + assert.Equal(t, "http://localhost:3000/test/repo/src/test", pl.(*DingtalkPayload).ActionCard.SingleURL) + }) + + t.Run("Issue", func(t *testing.T) { + p := issueTestPayload() + + d := new(DingtalkPayload) + p.Action = api.HookIssueOpened + pl, err := d.Issue(p) + require.NoError(t, err) + require.NotNil(t, pl) + require.IsType(t, &DingtalkPayload{}, pl) + + assert.Equal(t, "[test/repo] Issue opened: #2 crash by user1\r\n\r\nissue body", pl.(*DingtalkPayload).ActionCard.Text) + assert.Equal(t, "#2 crash", pl.(*DingtalkPayload).ActionCard.Title) + assert.Equal(t, "view issue", pl.(*DingtalkPayload).ActionCard.SingleTitle) + assert.Equal(t, "http://localhost:3000/test/repo/issues/2", pl.(*DingtalkPayload).ActionCard.SingleURL) + + p.Action = api.HookIssueClosed + pl, err = d.Issue(p) + require.NoError(t, err) + require.NotNil(t, pl) + require.IsType(t, &DingtalkPayload{}, pl) + + assert.Equal(t, "[test/repo] Issue closed: #2 crash by user1", pl.(*DingtalkPayload).ActionCard.Text) + assert.Equal(t, "#2 crash", pl.(*DingtalkPayload).ActionCard.Title) + assert.Equal(t, "view issue", pl.(*DingtalkPayload).ActionCard.SingleTitle) + assert.Equal(t, "http://localhost:3000/test/repo/issues/2", pl.(*DingtalkPayload).ActionCard.SingleURL) + }) + + t.Run("IssueComment", func(t *testing.T) { + p := issueCommentTestPayload() + + d := new(DingtalkPayload) + pl, err := d.IssueComment(p) + require.NoError(t, err) + require.NotNil(t, pl) + require.IsType(t, &DingtalkPayload{}, pl) + + assert.Equal(t, "[test/repo] New comment on issue #2 crash by user1\r\n\r\nmore info needed", pl.(*DingtalkPayload).ActionCard.Text) + assert.Equal(t, "#2 crash", pl.(*DingtalkPayload).ActionCard.Title) + assert.Equal(t, "view issue comment", pl.(*DingtalkPayload).ActionCard.SingleTitle) + assert.Equal(t, "http://localhost:3000/test/repo/issues/2#issuecomment-4", pl.(*DingtalkPayload).ActionCard.SingleURL) + }) + + t.Run("PullRequest", func(t *testing.T) { + p := pullRequestTestPayload() + + d := new(DingtalkPayload) + pl, err := d.PullRequest(p) + require.NoError(t, err) + require.NotNil(t, pl) + require.IsType(t, &DingtalkPayload{}, pl) + + assert.Equal(t, "[test/repo] Pull request opened: #12 Fix bug by user1\r\n\r\nfixes bug #2", pl.(*DingtalkPayload).ActionCard.Text) + assert.Equal(t, "#12 Fix bug", pl.(*DingtalkPayload).ActionCard.Title) + assert.Equal(t, "view pull request", pl.(*DingtalkPayload).ActionCard.SingleTitle) + assert.Equal(t, "http://localhost:3000/test/repo/pulls/12", pl.(*DingtalkPayload).ActionCard.SingleURL) + }) + + t.Run("PullRequestComment", func(t *testing.T) { + p := pullRequestCommentTestPayload() + + d := new(DingtalkPayload) + pl, err := d.IssueComment(p) + require.NoError(t, err) + require.NotNil(t, pl) + require.IsType(t, &DingtalkPayload{}, pl) + + assert.Equal(t, "[test/repo] New comment on pull request #12 Fix bug by user1\r\n\r\nchanges requested", pl.(*DingtalkPayload).ActionCard.Text) + assert.Equal(t, "#12 Fix bug", pl.(*DingtalkPayload).ActionCard.Title) + assert.Equal(t, "view issue comment", pl.(*DingtalkPayload).ActionCard.SingleTitle) + assert.Equal(t, "http://localhost:3000/test/repo/pulls/12#issuecomment-4", pl.(*DingtalkPayload).ActionCard.SingleURL) + }) + + t.Run("Review", func(t *testing.T) { + p := pullRequestTestPayload() + p.Action = api.HookIssueReviewed + + d := new(DingtalkPayload) + pl, err := d.Review(p, models.HookEventPullRequestReviewApproved) + require.NoError(t, err) + require.NotNil(t, pl) + require.IsType(t, &DingtalkPayload{}, pl) + + assert.Equal(t, "[test/repo] Pull request review approved : #12 Fix bug\r\n\r\ngood job", pl.(*DingtalkPayload).ActionCard.Text) + assert.Equal(t, "[test/repo] Pull request review approved : #12 Fix bug", pl.(*DingtalkPayload).ActionCard.Title) + assert.Equal(t, "view pull request", pl.(*DingtalkPayload).ActionCard.SingleTitle) + assert.Equal(t, "http://localhost:3000/test/repo/pulls/12", pl.(*DingtalkPayload).ActionCard.SingleURL) + }) + + t.Run("Repository", func(t *testing.T) { + p := repositoryTestPayload() + + d := new(DingtalkPayload) + pl, err := d.Repository(p) + require.NoError(t, err) + require.NotNil(t, pl) + require.IsType(t, &DingtalkPayload{}, pl) + + assert.Equal(t, "[test/repo] Repository created", pl.(*DingtalkPayload).ActionCard.Text) + assert.Equal(t, "[test/repo] Repository created", pl.(*DingtalkPayload).ActionCard.Title) + assert.Equal(t, "view repository", pl.(*DingtalkPayload).ActionCard.SingleTitle) + assert.Equal(t, "http://localhost:3000/test/repo", pl.(*DingtalkPayload).ActionCard.SingleURL) + }) + + t.Run("Release", func(t *testing.T) { + p := pullReleaseTestPayload() + + d := new(DingtalkPayload) + pl, err := d.Release(p) + require.NoError(t, err) + require.NotNil(t, pl) + require.IsType(t, &DingtalkPayload{}, pl) + + assert.Equal(t, "[test/repo] Release created: v1.0 by user1", pl.(*DingtalkPayload).ActionCard.Text) + assert.Equal(t, "[test/repo] Release created: v1.0 by user1", pl.(*DingtalkPayload).ActionCard.Title) + assert.Equal(t, "view release", pl.(*DingtalkPayload).ActionCard.SingleTitle) + assert.Equal(t, "http://localhost:3000/api/v1/repos/test/repo/releases/2", pl.(*DingtalkPayload).ActionCard.SingleURL) + }) +} + +func TestDingTalkJSONPayload(t *testing.T) { + p := pushTestPayload() + + pl, err := new(DingtalkPayload).Push(p) require.NoError(t, err) require.NotNil(t, pl) - assert.Equal(t, "#2 crash", pl.(*DingtalkPayload).ActionCard.Title) - assert.Equal(t, "[test/repo] Issue opened: #2 crash by user1\r\n\r\n", pl.(*DingtalkPayload).ActionCard.Text) + require.IsType(t, &DingtalkPayload{}, pl) - p.Action = api.HookIssueClosed - pl, err = d.Issue(p) + json, err := pl.JSONPayload() require.NoError(t, err) - require.NotNil(t, pl) - assert.Equal(t, "#2 crash", pl.(*DingtalkPayload).ActionCard.Title) - assert.Equal(t, "[test/repo] Issue closed: #2 crash by user1\r\n\r\n", pl.(*DingtalkPayload).ActionCard.Text) + assert.NotEmpty(t, json) } diff --git a/services/webhook/discord.go b/services/webhook/discord.go index d28904715..378d9ff72 100644 --- a/services/webhook/discord.go +++ b/services/webhook/discord.go @@ -120,22 +120,7 @@ func (d *DiscordPayload) Create(p *api.CreatePayload) (api.Payloader, error) { refName := git.RefEndName(p.Ref) title := fmt.Sprintf("[%s] %s %s created", p.Repo.FullName, p.RefType, refName) - return &DiscordPayload{ - Username: d.Username, - AvatarURL: d.AvatarURL, - Embeds: []DiscordEmbed{ - { - Title: title, - URL: p.Repo.HTMLURL + "/src/" + refName, - Color: greenColor, - Author: DiscordEmbedAuthor{ - Name: p.Sender.UserName, - URL: setting.AppURL + p.Sender.UserName, - IconURL: p.Sender.AvatarURL, - }, - }, - }, - }, nil + return d.createPayload(p.Sender, title, "", p.Repo.HTMLURL+"/src/"+refName, greenColor), nil } // Delete implements PayloadConvertor Delete method @@ -144,44 +129,14 @@ func (d *DiscordPayload) Delete(p *api.DeletePayload) (api.Payloader, error) { refName := git.RefEndName(p.Ref) title := fmt.Sprintf("[%s] %s %s deleted", p.Repo.FullName, p.RefType, refName) - return &DiscordPayload{ - Username: d.Username, - AvatarURL: d.AvatarURL, - Embeds: []DiscordEmbed{ - { - Title: title, - URL: p.Repo.HTMLURL + "/src/" + refName, - Color: redColor, - Author: DiscordEmbedAuthor{ - Name: p.Sender.UserName, - URL: setting.AppURL + p.Sender.UserName, - IconURL: p.Sender.AvatarURL, - }, - }, - }, - }, nil + return d.createPayload(p.Sender, title, "", p.Repo.HTMLURL+"/src/"+refName, redColor), nil } // Fork implements PayloadConvertor Fork method func (d *DiscordPayload) Fork(p *api.ForkPayload) (api.Payloader, error) { title := fmt.Sprintf("%s is forked to %s", p.Forkee.FullName, p.Repo.FullName) - return &DiscordPayload{ - Username: d.Username, - AvatarURL: d.AvatarURL, - Embeds: []DiscordEmbed{ - { - Title: title, - URL: p.Repo.HTMLURL, - Color: greenColor, - Author: DiscordEmbedAuthor{ - Name: p.Sender.UserName, - URL: setting.AppURL + p.Sender.UserName, - IconURL: p.Sender.AvatarURL, - }, - }, - }, - }, nil + return d.createPayload(p.Sender, title, "", p.Repo.HTMLURL, greenColor), nil } // Push implements PayloadConvertor Push method @@ -216,92 +171,28 @@ func (d *DiscordPayload) Push(p *api.PushPayload) (api.Payloader, error) { } } - return &DiscordPayload{ - Username: d.Username, - AvatarURL: d.AvatarURL, - Embeds: []DiscordEmbed{ - { - Title: title, - Description: text, - URL: titleLink, - Color: greenColor, - Author: DiscordEmbedAuthor{ - Name: p.Sender.UserName, - URL: setting.AppURL + p.Sender.UserName, - IconURL: p.Sender.AvatarURL, - }, - }, - }, - }, nil + return d.createPayload(p.Sender, title, text, titleLink, greenColor), nil } // Issue implements PayloadConvertor Issue method func (d *DiscordPayload) Issue(p *api.IssuePayload) (api.Payloader, error) { - text, _, attachmentText, color := getIssuesPayloadInfo(p, noneLinkFormatter, false) + title, _, text, color := getIssuesPayloadInfo(p, noneLinkFormatter, false) - return &DiscordPayload{ - Username: d.Username, - AvatarURL: d.AvatarURL, - Embeds: []DiscordEmbed{ - { - Title: text, - Description: attachmentText, - URL: p.Issue.HTMLURL, - Color: color, - Author: DiscordEmbedAuthor{ - Name: p.Sender.UserName, - URL: setting.AppURL + p.Sender.UserName, - IconURL: p.Sender.AvatarURL, - }, - }, - }, - }, nil + return d.createPayload(p.Sender, title, text, p.Issue.HTMLURL, color), nil } // IssueComment implements PayloadConvertor IssueComment method func (d *DiscordPayload) IssueComment(p *api.IssueCommentPayload) (api.Payloader, error) { - text, _, color := getIssueCommentPayloadInfo(p, noneLinkFormatter, false) + title, _, color := getIssueCommentPayloadInfo(p, noneLinkFormatter, false) - return &DiscordPayload{ - Username: d.Username, - AvatarURL: d.AvatarURL, - Embeds: []DiscordEmbed{ - { - Title: text, - Description: p.Comment.Body, - URL: p.Comment.HTMLURL, - Color: color, - Author: DiscordEmbedAuthor{ - Name: p.Sender.UserName, - URL: setting.AppURL + p.Sender.UserName, - IconURL: p.Sender.AvatarURL, - }, - }, - }, - }, nil + return d.createPayload(p.Sender, title, p.Comment.Body, p.Comment.HTMLURL, color), nil } // PullRequest implements PayloadConvertor PullRequest method func (d *DiscordPayload) PullRequest(p *api.PullRequestPayload) (api.Payloader, error) { - text, _, attachmentText, color := getPullRequestPayloadInfo(p, noneLinkFormatter, false) + title, _, text, color := getPullRequestPayloadInfo(p, noneLinkFormatter, false) - return &DiscordPayload{ - Username: d.Username, - AvatarURL: d.AvatarURL, - Embeds: []DiscordEmbed{ - { - Title: text, - Description: attachmentText, - URL: p.PullRequest.HTMLURL, - Color: color, - Author: DiscordEmbedAuthor{ - Name: p.Sender.UserName, - URL: setting.AppURL + p.Sender.UserName, - IconURL: p.Sender.AvatarURL, - }, - }, - }, - }, nil + return d.createPayload(p.Sender, title, text, p.PullRequest.HTMLURL, color), nil } // Review implements PayloadConvertor Review method @@ -330,23 +221,7 @@ func (d *DiscordPayload) Review(p *api.PullRequestPayload, event models.HookEven } } - return &DiscordPayload{ - Username: d.Username, - AvatarURL: d.AvatarURL, - Embeds: []DiscordEmbed{ - { - Title: title, - Description: text, - URL: p.PullRequest.HTMLURL, - Color: color, - Author: DiscordEmbedAuthor{ - Name: p.Sender.UserName, - URL: setting.AppURL + p.Sender.UserName, - IconURL: p.Sender.AvatarURL, - }, - }, - }, - }, nil + return d.createPayload(p.Sender, title, text, p.PullRequest.HTMLURL, color), nil } // Repository implements PayloadConvertor Repository method @@ -363,45 +238,14 @@ func (d *DiscordPayload) Repository(p *api.RepositoryPayload) (api.Payloader, er color = redColor } - return &DiscordPayload{ - Username: d.Username, - AvatarURL: d.AvatarURL, - Embeds: []DiscordEmbed{ - { - Title: title, - URL: url, - Color: color, - Author: DiscordEmbedAuthor{ - Name: p.Sender.UserName, - URL: setting.AppURL + p.Sender.UserName, - IconURL: p.Sender.AvatarURL, - }, - }, - }, - }, nil + return d.createPayload(p.Sender, title, "", url, color), nil } // Release implements PayloadConvertor Release method func (d *DiscordPayload) Release(p *api.ReleasePayload) (api.Payloader, error) { text, color := getReleasePayloadInfo(p, noneLinkFormatter, false) - return &DiscordPayload{ - Username: d.Username, - AvatarURL: d.AvatarURL, - Embeds: []DiscordEmbed{ - { - Title: text, - Description: p.Release.Note, - URL: p.Release.URL, - Color: color, - Author: DiscordEmbedAuthor{ - Name: p.Sender.UserName, - URL: setting.AppURL + p.Sender.UserName, - IconURL: p.Sender.AvatarURL, - }, - }, - }, - }, nil + return d.createPayload(p.Sender, text, p.Release.Note, p.Release.URL, color), nil } // GetDiscordPayload converts a discord webhook into a DiscordPayload @@ -433,3 +277,23 @@ func parseHookPullRequestEventType(event models.HookEventType) (string, error) { return "", errors.New("unknown event type") } } + +func (d *DiscordPayload) createPayload(s *api.User, title, text, url string, color int) *DiscordPayload { + return &DiscordPayload{ + Username: d.Username, + AvatarURL: d.AvatarURL, + Embeds: []DiscordEmbed{ + { + Title: title, + Description: text, + URL: url, + Color: color, + Author: DiscordEmbedAuthor{ + Name: s.UserName, + URL: setting.AppURL + s.UserName, + IconURL: s.AvatarURL, + }, + }, + }, + } +} diff --git a/services/webhook/discord_test.go b/services/webhook/discord_test.go new file mode 100644 index 000000000..fd7d2856c --- /dev/null +++ b/services/webhook/discord_test.go @@ -0,0 +1,245 @@ +// Copyright 2021 The Gitea Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package webhook + +import ( + "testing" + + "code.gitea.io/gitea/models" + "code.gitea.io/gitea/modules/setting" + api "code.gitea.io/gitea/modules/structs" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestDiscordPayload(t *testing.T) { + t.Run("Create", func(t *testing.T) { + p := createTestPayload() + + d := new(DiscordPayload) + pl, err := d.Create(p) + require.NoError(t, err) + require.NotNil(t, pl) + require.IsType(t, &DiscordPayload{}, pl) + + assert.Len(t, pl.(*DiscordPayload).Embeds, 1) + assert.Equal(t, "[test/repo] branch test created", pl.(*DiscordPayload).Embeds[0].Title) + assert.Empty(t, pl.(*DiscordPayload).Embeds[0].Description) + assert.Equal(t, "http://localhost:3000/test/repo/src/test", pl.(*DiscordPayload).Embeds[0].URL) + assert.Equal(t, p.Sender.UserName, pl.(*DiscordPayload).Embeds[0].Author.Name) + assert.Equal(t, setting.AppURL+p.Sender.UserName, pl.(*DiscordPayload).Embeds[0].Author.URL) + assert.Equal(t, p.Sender.AvatarURL, pl.(*DiscordPayload).Embeds[0].Author.IconURL) + }) + + t.Run("Delete", func(t *testing.T) { + p := deleteTestPayload() + + d := new(DiscordPayload) + pl, err := d.Delete(p) + require.NoError(t, err) + require.NotNil(t, pl) + require.IsType(t, &DiscordPayload{}, pl) + + assert.Len(t, pl.(*DiscordPayload).Embeds, 1) + assert.Equal(t, "[test/repo] branch test deleted", pl.(*DiscordPayload).Embeds[0].Title) + assert.Empty(t, pl.(*DiscordPayload).Embeds[0].Description) + assert.Equal(t, "http://localhost:3000/test/repo/src/test", pl.(*DiscordPayload).Embeds[0].URL) + assert.Equal(t, p.Sender.UserName, pl.(*DiscordPayload).Embeds[0].Author.Name) + assert.Equal(t, setting.AppURL+p.Sender.UserName, pl.(*DiscordPayload).Embeds[0].Author.URL) + assert.Equal(t, p.Sender.AvatarURL, pl.(*DiscordPayload).Embeds[0].Author.IconURL) + }) + + t.Run("Fork", func(t *testing.T) { + p := forkTestPayload() + + d := new(DiscordPayload) + pl, err := d.Fork(p) + require.NoError(t, err) + require.NotNil(t, pl) + require.IsType(t, &DiscordPayload{}, pl) + + assert.Len(t, pl.(*DiscordPayload).Embeds, 1) + assert.Equal(t, "test/repo2 is forked to test/repo", pl.(*DiscordPayload).Embeds[0].Title) + assert.Empty(t, pl.(*DiscordPayload).Embeds[0].Description) + assert.Equal(t, "http://localhost:3000/test/repo", pl.(*DiscordPayload).Embeds[0].URL) + assert.Equal(t, p.Sender.UserName, pl.(*DiscordPayload).Embeds[0].Author.Name) + assert.Equal(t, setting.AppURL+p.Sender.UserName, pl.(*DiscordPayload).Embeds[0].Author.URL) + assert.Equal(t, p.Sender.AvatarURL, pl.(*DiscordPayload).Embeds[0].Author.IconURL) + }) + + t.Run("Push", func(t *testing.T) { + p := pushTestPayload() + + d := new(DiscordPayload) + pl, err := d.Push(p) + require.NoError(t, err) + require.NotNil(t, pl) + require.IsType(t, &DiscordPayload{}, pl) + + assert.Len(t, pl.(*DiscordPayload).Embeds, 1) + assert.Equal(t, "[test/repo:test] 2 new commits", pl.(*DiscordPayload).Embeds[0].Title) + assert.Equal(t, "[2020558](http://localhost:3000/test/repo/commit/2020558fe2e34debb818a514715839cabd25e778) commit message - user1\n[2020558](http://localhost:3000/test/repo/commit/2020558fe2e34debb818a514715839cabd25e778) commit message - user1", pl.(*DiscordPayload).Embeds[0].Description) + assert.Equal(t, "http://localhost:3000/test/repo/src/test", pl.(*DiscordPayload).Embeds[0].URL) + assert.Equal(t, p.Sender.UserName, pl.(*DiscordPayload).Embeds[0].Author.Name) + assert.Equal(t, setting.AppURL+p.Sender.UserName, pl.(*DiscordPayload).Embeds[0].Author.URL) + assert.Equal(t, p.Sender.AvatarURL, pl.(*DiscordPayload).Embeds[0].Author.IconURL) + }) + + t.Run("Issue", func(t *testing.T) { + p := issueTestPayload() + + d := new(DiscordPayload) + p.Action = api.HookIssueOpened + pl, err := d.Issue(p) + require.NoError(t, err) + require.NotNil(t, pl) + require.IsType(t, &DiscordPayload{}, pl) + + assert.Len(t, pl.(*DiscordPayload).Embeds, 1) + assert.Equal(t, "[test/repo] Issue opened: #2 crash", pl.(*DiscordPayload).Embeds[0].Title) + assert.Equal(t, "issue body", pl.(*DiscordPayload).Embeds[0].Description) + assert.Equal(t, "http://localhost:3000/test/repo/issues/2", pl.(*DiscordPayload).Embeds[0].URL) + assert.Equal(t, p.Sender.UserName, pl.(*DiscordPayload).Embeds[0].Author.Name) + assert.Equal(t, setting.AppURL+p.Sender.UserName, pl.(*DiscordPayload).Embeds[0].Author.URL) + assert.Equal(t, p.Sender.AvatarURL, pl.(*DiscordPayload).Embeds[0].Author.IconURL) + + p.Action = api.HookIssueClosed + pl, err = d.Issue(p) + require.NoError(t, err) + require.NotNil(t, pl) + require.IsType(t, &DiscordPayload{}, pl) + + assert.Len(t, pl.(*DiscordPayload).Embeds, 1) + assert.Equal(t, "[test/repo] Issue closed: #2 crash", pl.(*DiscordPayload).Embeds[0].Title) + assert.Empty(t, pl.(*DiscordPayload).Embeds[0].Description) + assert.Equal(t, "http://localhost:3000/test/repo/issues/2", pl.(*DiscordPayload).Embeds[0].URL) + assert.Equal(t, p.Sender.UserName, pl.(*DiscordPayload).Embeds[0].Author.Name) + assert.Equal(t, setting.AppURL+p.Sender.UserName, pl.(*DiscordPayload).Embeds[0].Author.URL) + assert.Equal(t, p.Sender.AvatarURL, pl.(*DiscordPayload).Embeds[0].Author.IconURL) + }) + + t.Run("IssueComment", func(t *testing.T) { + p := issueCommentTestPayload() + + d := new(DiscordPayload) + pl, err := d.IssueComment(p) + require.NoError(t, err) + require.NotNil(t, pl) + require.IsType(t, &DiscordPayload{}, pl) + + assert.Len(t, pl.(*DiscordPayload).Embeds, 1) + assert.Equal(t, "[test/repo] New comment on issue #2 crash", pl.(*DiscordPayload).Embeds[0].Title) + assert.Equal(t, "more info needed", pl.(*DiscordPayload).Embeds[0].Description) + assert.Equal(t, "http://localhost:3000/test/repo/issues/2#issuecomment-4", pl.(*DiscordPayload).Embeds[0].URL) + assert.Equal(t, p.Sender.UserName, pl.(*DiscordPayload).Embeds[0].Author.Name) + assert.Equal(t, setting.AppURL+p.Sender.UserName, pl.(*DiscordPayload).Embeds[0].Author.URL) + assert.Equal(t, p.Sender.AvatarURL, pl.(*DiscordPayload).Embeds[0].Author.IconURL) + }) + + t.Run("PullRequest", func(t *testing.T) { + p := pullRequestTestPayload() + + d := new(DiscordPayload) + pl, err := d.PullRequest(p) + require.NoError(t, err) + require.NotNil(t, pl) + require.IsType(t, &DiscordPayload{}, pl) + + assert.Len(t, pl.(*DiscordPayload).Embeds, 1) + assert.Equal(t, "[test/repo] Pull request opened: #12 Fix bug", pl.(*DiscordPayload).Embeds[0].Title) + assert.Equal(t, "fixes bug #2", pl.(*DiscordPayload).Embeds[0].Description) + assert.Equal(t, "http://localhost:3000/test/repo/pulls/12", pl.(*DiscordPayload).Embeds[0].URL) + assert.Equal(t, p.Sender.UserName, pl.(*DiscordPayload).Embeds[0].Author.Name) + assert.Equal(t, setting.AppURL+p.Sender.UserName, pl.(*DiscordPayload).Embeds[0].Author.URL) + assert.Equal(t, p.Sender.AvatarURL, pl.(*DiscordPayload).Embeds[0].Author.IconURL) + }) + + t.Run("PullRequestComment", func(t *testing.T) { + p := pullRequestCommentTestPayload() + + d := new(DiscordPayload) + pl, err := d.IssueComment(p) + require.NoError(t, err) + require.NotNil(t, pl) + require.IsType(t, &DiscordPayload{}, pl) + + assert.Len(t, pl.(*DiscordPayload).Embeds, 1) + assert.Equal(t, "[test/repo] New comment on pull request #12 Fix bug", pl.(*DiscordPayload).Embeds[0].Title) + assert.Equal(t, "changes requested", pl.(*DiscordPayload).Embeds[0].Description) + assert.Equal(t, "http://localhost:3000/test/repo/pulls/12#issuecomment-4", pl.(*DiscordPayload).Embeds[0].URL) + assert.Equal(t, p.Sender.UserName, pl.(*DiscordPayload).Embeds[0].Author.Name) + assert.Equal(t, setting.AppURL+p.Sender.UserName, pl.(*DiscordPayload).Embeds[0].Author.URL) + assert.Equal(t, p.Sender.AvatarURL, pl.(*DiscordPayload).Embeds[0].Author.IconURL) + }) + + t.Run("Review", func(t *testing.T) { + p := pullRequestTestPayload() + p.Action = api.HookIssueReviewed + + d := new(DiscordPayload) + pl, err := d.Review(p, models.HookEventPullRequestReviewApproved) + require.NoError(t, err) + require.NotNil(t, pl) + require.IsType(t, &DiscordPayload{}, pl) + + assert.Len(t, pl.(*DiscordPayload).Embeds, 1) + assert.Equal(t, "[test/repo] Pull request review approved: #12 Fix bug", pl.(*DiscordPayload).Embeds[0].Title) + assert.Equal(t, "good job", pl.(*DiscordPayload).Embeds[0].Description) + assert.Equal(t, "http://localhost:3000/test/repo/pulls/12", pl.(*DiscordPayload).Embeds[0].URL) + assert.Equal(t, p.Sender.UserName, pl.(*DiscordPayload).Embeds[0].Author.Name) + assert.Equal(t, setting.AppURL+p.Sender.UserName, pl.(*DiscordPayload).Embeds[0].Author.URL) + assert.Equal(t, p.Sender.AvatarURL, pl.(*DiscordPayload).Embeds[0].Author.IconURL) + }) + + t.Run("Repository", func(t *testing.T) { + p := repositoryTestPayload() + + d := new(DiscordPayload) + pl, err := d.Repository(p) + require.NoError(t, err) + require.NotNil(t, pl) + require.IsType(t, &DiscordPayload{}, pl) + + assert.Len(t, pl.(*DiscordPayload).Embeds, 1) + assert.Equal(t, "[test/repo] Repository created", pl.(*DiscordPayload).Embeds[0].Title) + assert.Empty(t, pl.(*DiscordPayload).Embeds[0].Description) + assert.Equal(t, "http://localhost:3000/test/repo", pl.(*DiscordPayload).Embeds[0].URL) + assert.Equal(t, p.Sender.UserName, pl.(*DiscordPayload).Embeds[0].Author.Name) + assert.Equal(t, setting.AppURL+p.Sender.UserName, pl.(*DiscordPayload).Embeds[0].Author.URL) + assert.Equal(t, p.Sender.AvatarURL, pl.(*DiscordPayload).Embeds[0].Author.IconURL) + }) + + t.Run("Release", func(t *testing.T) { + p := pullReleaseTestPayload() + + d := new(DiscordPayload) + pl, err := d.Release(p) + require.NoError(t, err) + require.NotNil(t, pl) + require.IsType(t, &DiscordPayload{}, pl) + + assert.Len(t, pl.(*DiscordPayload).Embeds, 1) + assert.Equal(t, "[test/repo] Release created: v1.0", pl.(*DiscordPayload).Embeds[0].Title) + assert.Equal(t, "Note of first stable release", pl.(*DiscordPayload).Embeds[0].Description) + assert.Equal(t, "http://localhost:3000/api/v1/repos/test/repo/releases/2", pl.(*DiscordPayload).Embeds[0].URL) + assert.Equal(t, p.Sender.UserName, pl.(*DiscordPayload).Embeds[0].Author.Name) + assert.Equal(t, setting.AppURL+p.Sender.UserName, pl.(*DiscordPayload).Embeds[0].Author.URL) + assert.Equal(t, p.Sender.AvatarURL, pl.(*DiscordPayload).Embeds[0].Author.IconURL) + }) +} + +func TestDiscordJSONPayload(t *testing.T) { + p := pushTestPayload() + + pl, err := new(DiscordPayload).Push(p) + require.NoError(t, err) + require.NotNil(t, pl) + require.IsType(t, &DiscordPayload{}, pl) + + json, err := pl.JSONPayload() + require.NoError(t, err) + assert.NotEmpty(t, json) +} diff --git a/services/webhook/feishu.go b/services/webhook/feishu.go index 847a991f3..5c80efb82 100644 --- a/services/webhook/feishu.go +++ b/services/webhook/feishu.go @@ -30,7 +30,7 @@ func newFeishuTextPayload(text string) *FeishuPayload { Content: struct { Text string `json:"text"` }{ - Text: text, + Text: strings.TrimSpace(text), }, } } @@ -84,7 +84,7 @@ func (f *FeishuPayload) Push(p *api.PushPayload) (api.Payloader, error) { commitDesc string ) - var text = fmt.Sprintf("[%s:%s] %s\n", p.Repo.FullName, branchName, commitDesc) + var text = fmt.Sprintf("[%s:%s] %s\r\n", p.Repo.FullName, branchName, commitDesc) // for each commit, generate attachment text for i, commit := range p.Commits { var authorName string @@ -95,7 +95,7 @@ func (f *FeishuPayload) Push(p *api.PushPayload) (api.Payloader, error) { strings.TrimRight(commit.Message, "\r\n")) + authorName // add linebreak to each commit but the last if i < len(p.Commits)-1 { - text += "\n" + text += "\r\n" } } @@ -125,19 +125,14 @@ func (f *FeishuPayload) PullRequest(p *api.PullRequestPayload) (api.Payloader, e // Review implements PayloadConvertor Review method func (f *FeishuPayload) Review(p *api.PullRequestPayload, event models.HookEventType) (api.Payloader, error) { - var text, title string - switch p.Action { - case api.HookIssueSynchronized: - action, err := parseHookPullRequestEventType(event) - if err != nil { - return nil, err - } - - title = fmt.Sprintf("[%s] Pull request review %s : #%d %s", p.Repository.FullName, action, p.Index, p.PullRequest.Title) - text = p.Review.Content - + action, err := parseHookPullRequestEventType(event) + if err != nil { + return nil, err } + title := fmt.Sprintf("[%s] Pull request review %s : #%d %s", p.Repository.FullName, action, p.Index, p.PullRequest.Title) + text := p.Review.Content + return newFeishuTextPayload(title + "\r\n\r\n" + text), nil } diff --git a/services/webhook/feishu_test.go b/services/webhook/feishu_test.go new file mode 100644 index 000000000..7f3508c14 --- /dev/null +++ b/services/webhook/feishu_test.go @@ -0,0 +1,172 @@ +// Copyright 2021 The Gitea Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package webhook + +import ( + "testing" + + "code.gitea.io/gitea/models" + api "code.gitea.io/gitea/modules/structs" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestFeishuPayload(t *testing.T) { + t.Run("Create", func(t *testing.T) { + p := createTestPayload() + + d := new(FeishuPayload) + pl, err := d.Create(p) + require.NoError(t, err) + require.NotNil(t, pl) + require.IsType(t, &FeishuPayload{}, pl) + + assert.Equal(t, `[test/repo] branch test created`, pl.(*FeishuPayload).Content.Text) + }) + + t.Run("Delete", func(t *testing.T) { + p := deleteTestPayload() + + d := new(FeishuPayload) + pl, err := d.Delete(p) + require.NoError(t, err) + require.NotNil(t, pl) + require.IsType(t, &FeishuPayload{}, pl) + + assert.Equal(t, `[test/repo] branch test deleted`, pl.(*FeishuPayload).Content.Text) + }) + + t.Run("Fork", func(t *testing.T) { + p := forkTestPayload() + + d := new(FeishuPayload) + pl, err := d.Fork(p) + require.NoError(t, err) + require.NotNil(t, pl) + require.IsType(t, &FeishuPayload{}, pl) + + assert.Equal(t, `test/repo2 is forked to test/repo`, pl.(*FeishuPayload).Content.Text) + }) + + t.Run("Push", func(t *testing.T) { + p := pushTestPayload() + + d := new(FeishuPayload) + pl, err := d.Push(p) + require.NoError(t, err) + require.NotNil(t, pl) + require.IsType(t, &FeishuPayload{}, pl) + + assert.Equal(t, "[test/repo:test] \r\n[2020558](http://localhost:3000/test/repo/commit/2020558fe2e34debb818a514715839cabd25e778) commit message - user1\r\n[2020558](http://localhost:3000/test/repo/commit/2020558fe2e34debb818a514715839cabd25e778) commit message - user1", pl.(*FeishuPayload).Content.Text) + }) + + t.Run("Issue", func(t *testing.T) { + p := issueTestPayload() + + d := new(FeishuPayload) + p.Action = api.HookIssueOpened + pl, err := d.Issue(p) + require.NoError(t, err) + require.NotNil(t, pl) + require.IsType(t, &FeishuPayload{}, pl) + + assert.Equal(t, "#2 crash\r\n[test/repo] Issue opened: #2 crash by user1\r\n\r\nissue body", pl.(*FeishuPayload).Content.Text) + + p.Action = api.HookIssueClosed + pl, err = d.Issue(p) + require.NoError(t, err) + require.NotNil(t, pl) + require.IsType(t, &FeishuPayload{}, pl) + + assert.Equal(t, "#2 crash\r\n[test/repo] Issue closed: #2 crash by user1", pl.(*FeishuPayload).Content.Text) + }) + + t.Run("IssueComment", func(t *testing.T) { + p := issueCommentTestPayload() + + d := new(FeishuPayload) + pl, err := d.IssueComment(p) + require.NoError(t, err) + require.NotNil(t, pl) + require.IsType(t, &FeishuPayload{}, pl) + + assert.Equal(t, "#2 crash\r\n[test/repo] New comment on issue #2 crash by user1\r\n\r\nmore info needed", pl.(*FeishuPayload).Content.Text) + }) + + t.Run("PullRequest", func(t *testing.T) { + p := pullRequestTestPayload() + + d := new(FeishuPayload) + pl, err := d.PullRequest(p) + require.NoError(t, err) + require.NotNil(t, pl) + require.IsType(t, &FeishuPayload{}, pl) + + assert.Equal(t, "#12 Fix bug\r\n[test/repo] Pull request opened: #12 Fix bug by user1\r\n\r\nfixes bug #2", pl.(*FeishuPayload).Content.Text) + }) + + t.Run("PullRequestComment", func(t *testing.T) { + p := pullRequestCommentTestPayload() + + d := new(FeishuPayload) + pl, err := d.IssueComment(p) + require.NoError(t, err) + require.NotNil(t, pl) + require.IsType(t, &FeishuPayload{}, pl) + + assert.Equal(t, "#12 Fix bug\r\n[test/repo] New comment on pull request #12 Fix bug by user1\r\n\r\nchanges requested", pl.(*FeishuPayload).Content.Text) + }) + + t.Run("Review", func(t *testing.T) { + p := pullRequestTestPayload() + p.Action = api.HookIssueReviewed + + d := new(FeishuPayload) + pl, err := d.Review(p, models.HookEventPullRequestReviewApproved) + require.NoError(t, err) + require.NotNil(t, pl) + require.IsType(t, &FeishuPayload{}, pl) + + assert.Equal(t, "[test/repo] Pull request review approved : #12 Fix bug\r\n\r\ngood job", pl.(*FeishuPayload).Content.Text) + }) + + t.Run("Repository", func(t *testing.T) { + p := repositoryTestPayload() + + d := new(FeishuPayload) + pl, err := d.Repository(p) + require.NoError(t, err) + require.NotNil(t, pl) + require.IsType(t, &FeishuPayload{}, pl) + + assert.Equal(t, "[test/repo] Repository created", pl.(*FeishuPayload).Content.Text) + }) + + t.Run("Release", func(t *testing.T) { + p := pullReleaseTestPayload() + + d := new(FeishuPayload) + pl, err := d.Release(p) + require.NoError(t, err) + require.NotNil(t, pl) + require.IsType(t, &FeishuPayload{}, pl) + + assert.Equal(t, "[test/repo] Release created: v1.0 by user1", pl.(*FeishuPayload).Content.Text) + }) +} + +func TestFeishuJSONPayload(t *testing.T) { + p := pushTestPayload() + + pl, err := new(FeishuPayload).Push(p) + require.NoError(t, err) + require.NotNil(t, pl) + require.IsType(t, &FeishuPayload{}, pl) + + json, err := pl.JSONPayload() + require.NoError(t, err) + assert.NotEmpty(t, json) +} diff --git a/services/webhook/general.go b/services/webhook/general.go index ec247a241..777ae086b 100644 --- a/services/webhook/general.go +++ b/services/webhook/general.go @@ -44,8 +44,11 @@ func getIssuesPayloadInfo(p *api.IssuePayload, linkFormatter linkFormatter, with case api.HookIssueEdited: text = fmt.Sprintf("[%s] Issue edited: %s", repoLink, titleLink) case api.HookIssueAssigned: - text = fmt.Sprintf("[%s] Issue assigned to %s: %s", repoLink, - linkFormatter(setting.AppURL+p.Issue.Assignee.UserName, p.Issue.Assignee.UserName), titleLink) + list := make([]string, len(p.Issue.Assignees)) + for i, user := range p.Issue.Assignees { + list[i] = linkFormatter(setting.AppURL+user.UserName, user.UserName) + } + text = fmt.Sprintf("[%s] Issue assigned to %s: %s", repoLink, strings.Join(list, ", "), titleLink) color = greenColor case api.HookIssueUnassigned: text = fmt.Sprintf("[%s] Issue unassigned: %s", repoLink, titleLink) @@ -102,7 +105,7 @@ func getPullRequestPayloadInfo(p *api.PullRequestPayload, linkFormatter linkForm for i, user := range p.PullRequest.Assignees { list[i] = linkFormatter(setting.AppURL+user.UserName, user.UserName) } - text = fmt.Sprintf("[%s] Pull request assigned: %s to %s", repoLink, + text = fmt.Sprintf("[%s] Pull request assigned to %s: %s", repoLink, strings.Join(list, ", "), titleLink) color = greenColor case api.HookIssueUnassigned: @@ -115,7 +118,7 @@ func getPullRequestPayloadInfo(p *api.PullRequestPayload, linkFormatter linkForm text = fmt.Sprintf("[%s] Pull request synchronized: %s", repoLink, titleLink) case api.HookIssueMilestoned: mileStoneLink := fmt.Sprintf("%s/milestone/%d", p.Repository.HTMLURL, p.PullRequest.Milestone.ID) - text = fmt.Sprintf("[%s] Pull request milestoned: %s to %s", repoLink, + text = fmt.Sprintf("[%s] Pull request milestoned to %s: %s", repoLink, linkFormatter(mileStoneLink, p.PullRequest.Milestone.Title), titleLink) case api.HookIssueDemilestoned: text = fmt.Sprintf("[%s] Pull request milestone cleared: %s", repoLink, titleLink) diff --git a/services/webhook/general_test.go b/services/webhook/general_test.go index 3033b5788..4d73afe06 100644 --- a/services/webhook/general_test.go +++ b/services/webhook/general_test.go @@ -5,14 +5,111 @@ package webhook import ( + "testing" + api "code.gitea.io/gitea/modules/structs" + + "github.com/stretchr/testify/assert" ) +func createTestPayload() *api.CreatePayload { + return &api.CreatePayload{ + Sha: "2020558fe2e34debb818a514715839cabd25e777", + Ref: "refs/heads/test", + RefType: "branch", + Repo: &api.Repository{ + HTMLURL: "http://localhost:3000/test/repo", + Name: "repo", + FullName: "test/repo", + }, + Sender: &api.User{ + UserName: "user1", + AvatarURL: "http://localhost:3000/user1/avatar", + }, + } +} + +func deleteTestPayload() *api.DeletePayload { + return &api.DeletePayload{ + Ref: "refs/heads/test", + RefType: "branch", + Repo: &api.Repository{ + HTMLURL: "http://localhost:3000/test/repo", + Name: "repo", + FullName: "test/repo", + }, + Sender: &api.User{ + UserName: "user1", + AvatarURL: "http://localhost:3000/user1/avatar", + }, + } +} + +func forkTestPayload() *api.ForkPayload { + return &api.ForkPayload{ + Forkee: &api.Repository{ + HTMLURL: "http://localhost:3000/test/repo2", + Name: "repo2", + FullName: "test/repo2", + }, + Repo: &api.Repository{ + HTMLURL: "http://localhost:3000/test/repo", + Name: "repo", + FullName: "test/repo", + }, + Sender: &api.User{ + UserName: "user1", + AvatarURL: "http://localhost:3000/user1/avatar", + }, + } +} + +func pushTestPayload() *api.PushPayload { + commit := &api.PayloadCommit{ + ID: "2020558fe2e34debb818a514715839cabd25e778", + Message: "commit message", + URL: "http://localhost:3000/test/repo/commit/2020558fe2e34debb818a514715839cabd25e778", + Author: &api.PayloadUser{ + Name: "user1", + Email: "user1@localhost", + UserName: "user1", + }, + Committer: &api.PayloadUser{ + Name: "user1", + Email: "user1@localhost", + UserName: "user1", + }, + } + + return &api.PushPayload{ + Ref: "refs/heads/test", + Before: "2020558fe2e34debb818a514715839cabd25e777", + After: "2020558fe2e34debb818a514715839cabd25e778", + CompareURL: "", + HeadCommit: commit, + Commits: []*api.PayloadCommit{commit, commit}, + Repo: &api.Repository{ + HTMLURL: "http://localhost:3000/test/repo", + Name: "repo", + FullName: "test/repo", + }, + Pusher: &api.User{ + UserName: "user1", + AvatarURL: "http://localhost:3000/user1/avatar", + }, + Sender: &api.User{ + UserName: "user1", + AvatarURL: "http://localhost:3000/user1/avatar", + }, + } +} + func issueTestPayload() *api.IssuePayload { return &api.IssuePayload{ Index: 2, Sender: &api.User{ - UserName: "user1", + UserName: "user1", + AvatarURL: "http://localhost:3000/user1/avatar", }, Repository: &api.Repository{ HTMLURL: "http://localhost:3000/test/repo", @@ -20,10 +117,23 @@ func issueTestPayload() *api.IssuePayload { FullName: "test/repo", }, Issue: &api.Issue{ - ID: 2, - Index: 2, - URL: "http://localhost:3000/api/v1/repos/test/repo/issues/2", - Title: "crash", + ID: 2, + Index: 2, + URL: "http://localhost:3000/api/v1/repos/test/repo/issues/2", + HTMLURL: "http://localhost:3000/test/repo/issues/2", + Title: "crash", + Body: "issue body", + Assignees: []*api.User{ + { + UserName: "user1", + AvatarURL: "http://localhost:3000/user1/avatar", + }, + }, + Milestone: &api.Milestone{ + ID: 1, + Title: "Milestone Title", + Description: "Milestone Description", + }, }, } } @@ -32,7 +142,8 @@ func issueCommentTestPayload() *api.IssueCommentPayload { return &api.IssueCommentPayload{ Action: api.HookIssueCommentCreated, Sender: &api.User{ - UserName: "user1", + UserName: "user1", + AvatarURL: "http://localhost:3000/user1/avatar", }, Repository: &api.Repository{ HTMLURL: "http://localhost:3000/test/repo", @@ -45,11 +156,12 @@ func issueCommentTestPayload() *api.IssueCommentPayload { Body: "more info needed", }, Issue: &api.Issue{ - ID: 2, - Index: 2, - URL: "http://localhost:3000/api/v1/repos/test/repo/issues/2", - Title: "crash", - Body: "this happened", + ID: 2, + Index: 2, + URL: "http://localhost:3000/api/v1/repos/test/repo/issues/2", + HTMLURL: "http://localhost:3000/test/repo/issues/2", + Title: "crash", + Body: "this happened", }, } } @@ -58,7 +170,8 @@ func pullRequestCommentTestPayload() *api.IssueCommentPayload { return &api.IssueCommentPayload{ Action: api.HookIssueCommentCreated, Sender: &api.User{ - UserName: "user1", + UserName: "user1", + AvatarURL: "http://localhost:3000/user1/avatar", }, Repository: &api.Repository{ HTMLURL: "http://localhost:3000/test/repo", @@ -66,16 +179,17 @@ func pullRequestCommentTestPayload() *api.IssueCommentPayload { FullName: "test/repo", }, Comment: &api.Comment{ - HTMLURL: "http://localhost:3000/test/repo/pulls/2#issuecomment-4", - PRURL: "http://localhost:3000/test/repo/pulls/2", + HTMLURL: "http://localhost:3000/test/repo/pulls/12#issuecomment-4", + PRURL: "http://localhost:3000/test/repo/pulls/12", Body: "changes requested", }, Issue: &api.Issue{ - ID: 2, - Index: 2, - URL: "http://localhost:3000/api/v1/repos/test/repo/issues/2", - Title: "Fix bug", - Body: "fixes bug #2", + ID: 12, + Index: 12, + URL: "http://localhost:3000/api/v1/repos/test/repo/pulls/12", + HTMLURL: "http://localhost:3000/test/repo/pulls/12", + Title: "Fix bug", + Body: "fixes bug #2", }, IsPull: true, } @@ -85,7 +199,8 @@ func pullReleaseTestPayload() *api.ReleasePayload { return &api.ReleasePayload{ Action: api.HookReleasePublished, Sender: &api.User{ - UserName: "user1", + UserName: "user1", + AvatarURL: "http://localhost:3000/user1/avatar", }, Repository: &api.Repository{ HTMLURL: "http://localhost:3000/test/repo", @@ -96,6 +211,7 @@ func pullReleaseTestPayload() *api.ReleasePayload { TagName: "v1.0", Target: "master", Title: "First stable release", + Note: "Note of first stable release", URL: "http://localhost:3000/api/v1/repos/test/repo/releases/2", }, } @@ -104,9 +220,10 @@ func pullReleaseTestPayload() *api.ReleasePayload { func pullRequestTestPayload() *api.PullRequestPayload { return &api.PullRequestPayload{ Action: api.HookIssueOpened, - Index: 2, + Index: 12, Sender: &api.User{ - UserName: "user1", + UserName: "user1", + AvatarURL: "http://localhost:3000/user1/avatar", }, Repository: &api.Repository{ HTMLURL: "http://localhost:3000/test/repo", @@ -114,12 +231,311 @@ func pullRequestTestPayload() *api.PullRequestPayload { FullName: "test/repo", }, PullRequest: &api.PullRequest{ - ID: 2, - Index: 2, + ID: 12, + Index: 12, URL: "http://localhost:3000/test/repo/pulls/12", + HTMLURL: "http://localhost:3000/test/repo/pulls/12", Title: "Fix bug", Body: "fixes bug #2", Mergeable: true, + Assignees: []*api.User{ + { + UserName: "user1", + AvatarURL: "http://localhost:3000/user1/avatar", + }, + }, + Milestone: &api.Milestone{ + ID: 1, + Title: "Milestone Title", + Description: "Milestone Description", + }, + }, + Review: &api.ReviewPayload{ + Content: "good job", + }, + } +} + +func repositoryTestPayload() *api.RepositoryPayload { + return &api.RepositoryPayload{ + Action: api.HookRepoCreated, + Sender: &api.User{ + UserName: "user1", + AvatarURL: "http://localhost:3000/user1/avatar", }, + Repository: &api.Repository{ + HTMLURL: "http://localhost:3000/test/repo", + Name: "repo", + FullName: "test/repo", + }, + } +} + +func TestGetIssuesPayloadInfo(t *testing.T) { + p := issueTestPayload() + + cases := []struct { + action api.HookIssueAction + text string + issueTitle string + attachmentText string + color int + }{ + { + api.HookIssueOpened, + "[test/repo] Issue opened: #2 crash by user1", + "#2 crash", + "issue body", + orangeColor, + }, + { + api.HookIssueClosed, + "[test/repo] Issue closed: #2 crash by user1", + "#2 crash", + "", + redColor, + }, + { + api.HookIssueReOpened, + "[test/repo] Issue re-opened: #2 crash by user1", + "#2 crash", + "", + yellowColor, + }, + { + api.HookIssueEdited, + "[test/repo] Issue edited: #2 crash by user1", + "#2 crash", + "issue body", + yellowColor, + }, + { + api.HookIssueAssigned, + "[test/repo] Issue assigned to user1: #2 crash by user1", + "#2 crash", + "", + greenColor, + }, + { + api.HookIssueUnassigned, + "[test/repo] Issue unassigned: #2 crash by user1", + "#2 crash", + "", + yellowColor, + }, + { + api.HookIssueLabelUpdated, + "[test/repo] Issue labels updated: #2 crash by user1", + "#2 crash", + "", + yellowColor, + }, + { + api.HookIssueLabelCleared, + "[test/repo] Issue labels cleared: #2 crash by user1", + "#2 crash", + "", + yellowColor, + }, + { + api.HookIssueSynchronized, + "[test/repo] Issue synchronized: #2 crash by user1", + "#2 crash", + "", + yellowColor, + }, + { + api.HookIssueMilestoned, + "[test/repo] Issue milestoned to Milestone Title: #2 crash by user1", + "#2 crash", + "", + yellowColor, + }, + { + api.HookIssueDemilestoned, + "[test/repo] Issue milestone cleared: #2 crash by user1", + "#2 crash", + "", + yellowColor, + }, + } + + for i, c := range cases { + p.Action = c.action + text, issueTitle, attachmentText, color := getIssuesPayloadInfo(p, noneLinkFormatter, true) + assert.Equal(t, c.text, text, "case %d", i) + assert.Equal(t, c.issueTitle, issueTitle, "case %d", i) + assert.Equal(t, c.attachmentText, attachmentText, "case %d", i) + assert.Equal(t, c.color, color, "case %d", i) + } +} + +func TestGetPullRequestPayloadInfo(t *testing.T) { + p := pullRequestTestPayload() + + cases := []struct { + action api.HookIssueAction + text string + issueTitle string + attachmentText string + color int + }{ + { + api.HookIssueOpened, + "[test/repo] Pull request opened: #12 Fix bug by user1", + "#12 Fix bug", + "fixes bug #2", + greenColor, + }, + { + api.HookIssueClosed, + "[test/repo] Pull request closed: #12 Fix bug by user1", + "#12 Fix bug", + "", + redColor, + }, + { + api.HookIssueReOpened, + "[test/repo] Pull request re-opened: #12 Fix bug by user1", + "#12 Fix bug", + "", + yellowColor, + }, + { + api.HookIssueEdited, + "[test/repo] Pull request edited: #12 Fix bug by user1", + "#12 Fix bug", + "fixes bug #2", + yellowColor, + }, + { + api.HookIssueAssigned, + "[test/repo] Pull request assigned to user1: #12 Fix bug by user1", + "#12 Fix bug", + "", + greenColor, + }, + { + api.HookIssueUnassigned, + "[test/repo] Pull request unassigned: #12 Fix bug by user1", + "#12 Fix bug", + "", + yellowColor, + }, + { + api.HookIssueLabelUpdated, + "[test/repo] Pull request labels updated: #12 Fix bug by user1", + "#12 Fix bug", + "", + yellowColor, + }, + { + api.HookIssueLabelCleared, + "[test/repo] Pull request labels cleared: #12 Fix bug by user1", + "#12 Fix bug", + "", + yellowColor, + }, + { + api.HookIssueSynchronized, + "[test/repo] Pull request synchronized: #12 Fix bug by user1", + "#12 Fix bug", + "", + yellowColor, + }, + { + api.HookIssueMilestoned, + "[test/repo] Pull request milestoned to Milestone Title: #12 Fix bug by user1", + "#12 Fix bug", + "", + yellowColor, + }, + { + api.HookIssueDemilestoned, + "[test/repo] Pull request milestone cleared: #12 Fix bug by user1", + "#12 Fix bug", + "", + yellowColor, + }, + } + + for i, c := range cases { + p.Action = c.action + text, issueTitle, attachmentText, color := getPullRequestPayloadInfo(p, noneLinkFormatter, true) + assert.Equal(t, c.text, text, "case %d", i) + assert.Equal(t, c.issueTitle, issueTitle, "case %d", i) + assert.Equal(t, c.attachmentText, attachmentText, "case %d", i) + assert.Equal(t, c.color, color, "case %d", i) + } +} + +func TestGetReleasePayloadInfo(t *testing.T) { + p := pullReleaseTestPayload() + + cases := []struct { + action api.HookReleaseAction + text string + color int + }{ + { + api.HookReleasePublished, + "[test/repo] Release created: v1.0 by user1", + greenColor, + }, + { + api.HookReleaseUpdated, + "[test/repo] Release updated: v1.0 by user1", + yellowColor, + }, + { + api.HookReleaseDeleted, + "[test/repo] Release deleted: v1.0 by user1", + redColor, + }, + } + + for i, c := range cases { + p.Action = c.action + text, color := getReleasePayloadInfo(p, noneLinkFormatter, true) + assert.Equal(t, c.text, text, "case %d", i) + assert.Equal(t, c.color, color, "case %d", i) + } +} + +func TestGetIssueCommentPayloadInfo(t *testing.T) { + p := pullRequestCommentTestPayload() + + cases := []struct { + action api.HookIssueCommentAction + text string + issueTitle string + color int + }{ + { + api.HookIssueCommentCreated, + "[test/repo] New comment on pull request #12 Fix bug by user1", + "#12 Fix bug", + greenColorLight, + }, + { + api.HookIssueCommentEdited, + "[test/repo] Comment edited on pull request #12 Fix bug by user1", + "#12 Fix bug", + yellowColor, + }, + { + api.HookIssueCommentDeleted, + "[test/repo] Comment deleted on pull request #12 Fix bug by user1", + "#12 Fix bug", + redColor, + }, + } + + for i, c := range cases { + p.Action = c.action + text, issueTitle, color := getIssueCommentPayloadInfo(p, noneLinkFormatter, true) + assert.Equal(t, c.text, text, "case %d", i) + assert.Equal(t, c.issueTitle, issueTitle, "case %d", i) + assert.Equal(t, c.color, color, "case %d", i) } } diff --git a/services/webhook/matrix_test.go b/services/webhook/matrix_test.go index 771146f2f..7b10e21cf 100644 --- a/services/webhook/matrix_test.go +++ b/services/webhook/matrix_test.go @@ -14,71 +14,173 @@ import ( "github.com/stretchr/testify/require" ) -func TestMatrixIssuesPayloadOpened(t *testing.T) { - p := issueTestPayload() - m := new(MatrixPayloadUnsafe) +func TestMatrixPayload(t *testing.T) { + t.Run("Create", func(t *testing.T) { + p := createTestPayload() - p.Action = api.HookIssueOpened - pl, err := m.Issue(p) - require.NoError(t, err) - require.NotNil(t, pl) - assert.Equal(t, "[[test/repo](http://localhost:3000/test/repo)] Issue opened: [#2 crash](http://localhost:3000/test/repo/issues/2) by [user1](https://try.gitea.io/user1)", pl.(*MatrixPayloadUnsafe).Body) - assert.Equal(t, "[test/repo] Issue opened: #2 crash by user1", pl.(*MatrixPayloadUnsafe).FormattedBody) + d := new(MatrixPayloadUnsafe) + pl, err := d.Create(p) + require.NoError(t, err) + require.NotNil(t, pl) + require.IsType(t, &MatrixPayloadUnsafe{}, pl) - p.Action = api.HookIssueClosed - pl, err = m.Issue(p) - require.NoError(t, err) - require.NotNil(t, pl) - assert.Equal(t, "[[test/repo](http://localhost:3000/test/repo)] Issue closed: [#2 crash](http://localhost:3000/test/repo/issues/2) by [user1](https://try.gitea.io/user1)", pl.(*MatrixPayloadUnsafe).Body) - assert.Equal(t, "[test/repo] Issue closed: #2 crash by user1", pl.(*MatrixPayloadUnsafe).FormattedBody) -} + assert.Equal(t, "[[test/repo](http://localhost:3000/test/repo):[test](http://localhost:3000/test/repo/src/branch/test)] branch created by user1", pl.(*MatrixPayloadUnsafe).Body) + assert.Equal(t, `[test/repo:test] branch created by user1`, pl.(*MatrixPayloadUnsafe).FormattedBody) + }) -func TestMatrixIssueCommentPayload(t *testing.T) { - p := issueCommentTestPayload() - m := new(MatrixPayloadUnsafe) + t.Run("Delete", func(t *testing.T) { + p := deleteTestPayload() - pl, err := m.IssueComment(p) - require.NoError(t, err) - require.NotNil(t, pl) + d := new(MatrixPayloadUnsafe) + pl, err := d.Delete(p) + require.NoError(t, err) + require.NotNil(t, pl) + require.IsType(t, &MatrixPayloadUnsafe{}, pl) - assert.Equal(t, "[[test/repo](http://localhost:3000/test/repo)] New comment on issue [#2 crash](http://localhost:3000/test/repo/issues/2) by [user1](https://try.gitea.io/user1)", pl.(*MatrixPayloadUnsafe).Body) - assert.Equal(t, "[test/repo] New comment on issue #2 crash by user1", pl.(*MatrixPayloadUnsafe).FormattedBody) -} + assert.Equal(t, "[[test/repo](http://localhost:3000/test/repo):test] branch deleted by user1", pl.(*MatrixPayloadUnsafe).Body) + assert.Equal(t, `[test/repo:test] branch deleted by user1`, pl.(*MatrixPayloadUnsafe).FormattedBody) + }) -func TestMatrixPullRequestCommentPayload(t *testing.T) { - p := pullRequestCommentTestPayload() - m := new(MatrixPayloadUnsafe) + t.Run("Fork", func(t *testing.T) { + p := forkTestPayload() - pl, err := m.IssueComment(p) - require.NoError(t, err) - require.NotNil(t, pl) + d := new(MatrixPayloadUnsafe) + pl, err := d.Fork(p) + require.NoError(t, err) + require.NotNil(t, pl) + require.IsType(t, &MatrixPayloadUnsafe{}, pl) - assert.Equal(t, "[[test/repo](http://localhost:3000/test/repo)] New comment on pull request [#2 Fix bug](http://localhost:3000/test/repo/pulls/2) by [user1](https://try.gitea.io/user1)", pl.(*MatrixPayloadUnsafe).Body) - assert.Equal(t, "[test/repo] New comment on pull request #2 Fix bug by user1", pl.(*MatrixPayloadUnsafe).FormattedBody) -} + assert.Equal(t, "[test/repo2](http://localhost:3000/test/repo2) is forked to [test/repo](http://localhost:3000/test/repo)", pl.(*MatrixPayloadUnsafe).Body) + assert.Equal(t, `test/repo2 is forked to test/repo`, pl.(*MatrixPayloadUnsafe).FormattedBody) + }) -func TestMatrixReleasePayload(t *testing.T) { - p := pullReleaseTestPayload() - m := new(MatrixPayloadUnsafe) + t.Run("Push", func(t *testing.T) { + p := pushTestPayload() - pl, err := m.Release(p) - require.NoError(t, err) - require.NotNil(t, pl) + d := new(MatrixPayloadUnsafe) + pl, err := d.Push(p) + require.NoError(t, err) + require.NotNil(t, pl) + require.IsType(t, &MatrixPayloadUnsafe{}, pl) + + assert.Equal(t, "[[test/repo](http://localhost:3000/test/repo)] user1 pushed 2 commits to [test](http://localhost:3000/test/repo/src/branch/test):\n[2020558](http://localhost:3000/test/repo/commit/2020558fe2e34debb818a514715839cabd25e778): commit message - user1\n[2020558](http://localhost:3000/test/repo/commit/2020558fe2e34debb818a514715839cabd25e778): commit message - user1", pl.(*MatrixPayloadUnsafe).Body) + assert.Equal(t, `[test/repo] user1 pushed 2 commits to test:
2020558: commit message - user1
2020558: commit message - user1`, pl.(*MatrixPayloadUnsafe).FormattedBody) + }) + + t.Run("Issue", func(t *testing.T) { + p := issueTestPayload() + + d := new(MatrixPayloadUnsafe) + p.Action = api.HookIssueOpened + pl, err := d.Issue(p) + require.NoError(t, err) + require.NotNil(t, pl) + require.IsType(t, &MatrixPayloadUnsafe{}, pl) + + assert.Equal(t, "[[test/repo](http://localhost:3000/test/repo)] Issue opened: [#2 crash](http://localhost:3000/test/repo/issues/2) by [user1](https://try.gitea.io/user1)", pl.(*MatrixPayloadUnsafe).Body) + assert.Equal(t, `[test/repo] Issue opened: #2 crash by user1`, pl.(*MatrixPayloadUnsafe).FormattedBody) + + p.Action = api.HookIssueClosed + pl, err = d.Issue(p) + require.NoError(t, err) + require.NotNil(t, pl) + require.IsType(t, &MatrixPayloadUnsafe{}, pl) + + assert.Equal(t, "[[test/repo](http://localhost:3000/test/repo)] Issue closed: [#2 crash](http://localhost:3000/test/repo/issues/2) by [user1](https://try.gitea.io/user1)", pl.(*MatrixPayloadUnsafe).Body) + assert.Equal(t, `[test/repo] Issue closed: #2 crash by user1`, pl.(*MatrixPayloadUnsafe).FormattedBody) + }) - assert.Equal(t, "[[test/repo](http://localhost:3000/test/repo)] Release created: [v1.0](http://localhost:3000/test/repo/src/v1.0) by [user1](https://try.gitea.io/user1)", pl.(*MatrixPayloadUnsafe).Body) - assert.Equal(t, "[test/repo] Release created: v1.0 by user1", pl.(*MatrixPayloadUnsafe).FormattedBody) + t.Run("IssueComment", func(t *testing.T) { + p := issueCommentTestPayload() + + d := new(MatrixPayloadUnsafe) + pl, err := d.IssueComment(p) + require.NoError(t, err) + require.NotNil(t, pl) + require.IsType(t, &MatrixPayloadUnsafe{}, pl) + + assert.Equal(t, "[[test/repo](http://localhost:3000/test/repo)] New comment on issue [#2 crash](http://localhost:3000/test/repo/issues/2) by [user1](https://try.gitea.io/user1)", pl.(*MatrixPayloadUnsafe).Body) + assert.Equal(t, `[test/repo] New comment on issue #2 crash by user1`, pl.(*MatrixPayloadUnsafe).FormattedBody) + }) + + t.Run("PullRequest", func(t *testing.T) { + p := pullRequestTestPayload() + + d := new(MatrixPayloadUnsafe) + pl, err := d.PullRequest(p) + require.NoError(t, err) + require.NotNil(t, pl) + require.IsType(t, &MatrixPayloadUnsafe{}, pl) + + assert.Equal(t, "[[test/repo](http://localhost:3000/test/repo)] Pull request opened: [#12 Fix bug](http://localhost:3000/test/repo/pulls/12) by [user1](https://try.gitea.io/user1)", pl.(*MatrixPayloadUnsafe).Body) + assert.Equal(t, `[test/repo] Pull request opened: #12 Fix bug by user1`, pl.(*MatrixPayloadUnsafe).FormattedBody) + }) + + t.Run("PullRequestComment", func(t *testing.T) { + p := pullRequestCommentTestPayload() + + d := new(MatrixPayloadUnsafe) + pl, err := d.IssueComment(p) + require.NoError(t, err) + require.NotNil(t, pl) + require.IsType(t, &MatrixPayloadUnsafe{}, pl) + + assert.Equal(t, "[[test/repo](http://localhost:3000/test/repo)] New comment on pull request [#12 Fix bug](http://localhost:3000/test/repo/pulls/12) by [user1](https://try.gitea.io/user1)", pl.(*MatrixPayloadUnsafe).Body) + assert.Equal(t, `[test/repo] New comment on pull request #12 Fix bug by user1`, pl.(*MatrixPayloadUnsafe).FormattedBody) + }) + + t.Run("Review", func(t *testing.T) { + p := pullRequestTestPayload() + p.Action = api.HookIssueReviewed + + d := new(MatrixPayloadUnsafe) + pl, err := d.Review(p, models.HookEventPullRequestReviewApproved) + require.NoError(t, err) + require.NotNil(t, pl) + require.IsType(t, &MatrixPayloadUnsafe{}, pl) + + assert.Equal(t, "[[test/repo](http://localhost:3000/test/repo)] Pull request review approved: [#12 Fix bug](http://localhost:3000/test/repo/pulls/12) by [user1](https://try.gitea.io/user1)", pl.(*MatrixPayloadUnsafe).Body) + assert.Equal(t, `[test/repo] Pull request review approved: [#12 Fix bug](http://localhost:3000/test/repo/pulls/12) by user1`, pl.(*MatrixPayloadUnsafe).FormattedBody) + }) + + t.Run("Repository", func(t *testing.T) { + p := repositoryTestPayload() + + d := new(MatrixPayloadUnsafe) + pl, err := d.Repository(p) + require.NoError(t, err) + require.NotNil(t, pl) + require.IsType(t, &MatrixPayloadUnsafe{}, pl) + + assert.Equal(t, `[[test/repo](http://localhost:3000/test/repo)] Repository created by [user1](https://try.gitea.io/user1)`, pl.(*MatrixPayloadUnsafe).Body) + assert.Equal(t, `[test/repo] Repository created by user1`, pl.(*MatrixPayloadUnsafe).FormattedBody) + }) + + t.Run("Release", func(t *testing.T) { + p := pullReleaseTestPayload() + + d := new(MatrixPayloadUnsafe) + pl, err := d.Release(p) + require.NoError(t, err) + require.NotNil(t, pl) + require.IsType(t, &MatrixPayloadUnsafe{}, pl) + + assert.Equal(t, "[[test/repo](http://localhost:3000/test/repo)] Release created: [v1.0](http://localhost:3000/test/repo/src/v1.0) by [user1](https://try.gitea.io/user1)", pl.(*MatrixPayloadUnsafe).Body) + assert.Equal(t, `[test/repo] Release created: v1.0 by user1`, pl.(*MatrixPayloadUnsafe).FormattedBody) + }) } -func TestMatrixPullRequestPayload(t *testing.T) { - p := pullRequestTestPayload() - m := new(MatrixPayloadUnsafe) +func TestMatrixJSONPayload(t *testing.T) { + p := pushTestPayload() - pl, err := m.PullRequest(p) + pl, err := new(MatrixPayloadUnsafe).Push(p) require.NoError(t, err) require.NotNil(t, pl) + require.IsType(t, &MatrixPayloadUnsafe{}, pl) - assert.Equal(t, "[[test/repo](http://localhost:3000/test/repo)] Pull request opened: [#2 Fix bug](http://localhost:3000/test/repo/pulls/12) by [user1](https://try.gitea.io/user1)", pl.(*MatrixPayloadUnsafe).Body) - assert.Equal(t, "[test/repo] Pull request opened: #2 Fix bug by user1", pl.(*MatrixPayloadUnsafe).FormattedBody) + json, err := pl.JSONPayload() + require.NoError(t, err) + assert.NotEmpty(t, json) } func TestMatrixHookRequest(t *testing.T) { diff --git a/services/webhook/msteams.go b/services/webhook/msteams.go index dc83a47c8..51bb28a36 100644 --- a/services/webhook/msteams.go +++ b/services/webhook/msteams.go @@ -78,42 +78,15 @@ func (m *MSTeamsPayload) Create(p *api.CreatePayload) (api.Payloader, error) { refName := git.RefEndName(p.Ref) title := fmt.Sprintf("[%s] %s %s created", p.Repo.FullName, p.RefType, refName) - return &MSTeamsPayload{ - Type: "MessageCard", - Context: "https://schema.org/extensions", - ThemeColor: fmt.Sprintf("%x", greenColor), - Title: title, - Summary: title, - Sections: []MSTeamsSection{ - { - ActivityTitle: p.Sender.FullName, - ActivitySubtitle: p.Sender.UserName, - ActivityImage: p.Sender.AvatarURL, - Facts: []MSTeamsFact{ - { - Name: "Repository:", - Value: p.Repo.FullName, - }, - { - Name: fmt.Sprintf("%s:", p.RefType), - Value: refName, - }, - }, - }, - }, - PotentialAction: []MSTeamsAction{ - { - Type: "OpenUri", - Name: "View in Gitea", - Targets: []MSTeamsActionTarget{ - { - Os: "default", - URI: p.Repo.HTMLURL + "/src/" + refName, - }, - }, - }, - }, - }, nil + return createMSTeamsPayload( + p.Repo, + p.Sender, + title, + "", + p.Repo.HTMLURL+"/src/"+refName, + greenColor, + &MSTeamsFact{fmt.Sprintf("%s:", p.RefType), refName}, + ), nil } // Delete implements PayloadConvertor Delete method @@ -122,84 +95,30 @@ func (m *MSTeamsPayload) Delete(p *api.DeletePayload) (api.Payloader, error) { refName := git.RefEndName(p.Ref) title := fmt.Sprintf("[%s] %s %s deleted", p.Repo.FullName, p.RefType, refName) - return &MSTeamsPayload{ - Type: "MessageCard", - Context: "https://schema.org/extensions", - ThemeColor: fmt.Sprintf("%x", yellowColor), - Title: title, - Summary: title, - Sections: []MSTeamsSection{ - { - ActivityTitle: p.Sender.FullName, - ActivitySubtitle: p.Sender.UserName, - ActivityImage: p.Sender.AvatarURL, - Facts: []MSTeamsFact{ - { - Name: "Repository:", - Value: p.Repo.FullName, - }, - { - Name: fmt.Sprintf("%s:", p.RefType), - Value: refName, - }, - }, - }, - }, - PotentialAction: []MSTeamsAction{ - { - Type: "OpenUri", - Name: "View in Gitea", - Targets: []MSTeamsActionTarget{ - { - Os: "default", - URI: p.Repo.HTMLURL + "/src/" + refName, - }, - }, - }, - }, - }, nil + return createMSTeamsPayload( + p.Repo, + p.Sender, + title, + "", + p.Repo.HTMLURL+"/src/"+refName, + yellowColor, + &MSTeamsFact{fmt.Sprintf("%s:", p.RefType), refName}, + ), nil } // Fork implements PayloadConvertor Fork method func (m *MSTeamsPayload) Fork(p *api.ForkPayload) (api.Payloader, error) { title := fmt.Sprintf("%s is forked to %s", p.Forkee.FullName, p.Repo.FullName) - return &MSTeamsPayload{ - Type: "MessageCard", - Context: "https://schema.org/extensions", - ThemeColor: fmt.Sprintf("%x", greenColor), - Title: title, - Summary: title, - Sections: []MSTeamsSection{ - { - ActivityTitle: p.Sender.FullName, - ActivitySubtitle: p.Sender.UserName, - ActivityImage: p.Sender.AvatarURL, - Facts: []MSTeamsFact{ - { - Name: "Forkee:", - Value: p.Forkee.FullName, - }, - { - Name: "Repository:", - Value: p.Repo.FullName, - }, - }, - }, - }, - PotentialAction: []MSTeamsAction{ - { - Type: "OpenUri", - Name: "View in Gitea", - Targets: []MSTeamsActionTarget{ - { - Os: "default", - URI: p.Repo.HTMLURL, - }, - }, - }, - }, - }, nil + return createMSTeamsPayload( + p.Repo, + p.Sender, + title, + "", + p.Repo.HTMLURL, + greenColor, + &MSTeamsFact{"Forkee:", p.Forkee.FullName}, + ), nil } // Push implements PayloadConvertor Push method @@ -234,172 +153,60 @@ func (m *MSTeamsPayload) Push(p *api.PushPayload) (api.Payloader, error) { } } - return &MSTeamsPayload{ - Type: "MessageCard", - Context: "https://schema.org/extensions", - ThemeColor: fmt.Sprintf("%x", greenColor), - Title: title, - Summary: title, - Sections: []MSTeamsSection{ - { - ActivityTitle: p.Sender.FullName, - ActivitySubtitle: p.Sender.UserName, - ActivityImage: p.Sender.AvatarURL, - Text: text, - Facts: []MSTeamsFact{ - { - Name: "Repository:", - Value: p.Repo.FullName, - }, - { - Name: "Commit count:", - Value: fmt.Sprintf("%d", len(p.Commits)), - }, - }, - }, - }, - PotentialAction: []MSTeamsAction{ - { - Type: "OpenUri", - Name: "View in Gitea", - Targets: []MSTeamsActionTarget{ - { - Os: "default", - URI: titleLink, - }, - }, - }, - }, - }, nil + return createMSTeamsPayload( + p.Repo, + p.Sender, + title, + text, + titleLink, + greenColor, + &MSTeamsFact{"Commit count:", fmt.Sprintf("%d", len(p.Commits))}, + ), nil } // Issue implements PayloadConvertor Issue method func (m *MSTeamsPayload) Issue(p *api.IssuePayload) (api.Payloader, error) { - text, _, attachmentText, color := getIssuesPayloadInfo(p, noneLinkFormatter, false) - - return &MSTeamsPayload{ - Type: "MessageCard", - Context: "https://schema.org/extensions", - ThemeColor: fmt.Sprintf("%x", color), - Title: text, - Summary: text, - Sections: []MSTeamsSection{ - { - ActivityTitle: p.Sender.FullName, - ActivitySubtitle: p.Sender.UserName, - ActivityImage: p.Sender.AvatarURL, - Text: attachmentText, - Facts: []MSTeamsFact{ - { - Name: "Repository:", - Value: p.Repository.FullName, - }, - { - Name: "Issue #:", - Value: fmt.Sprintf("%d", p.Issue.ID), - }, - }, - }, - }, - PotentialAction: []MSTeamsAction{ - { - Type: "OpenUri", - Name: "View in Gitea", - Targets: []MSTeamsActionTarget{ - { - Os: "default", - URI: p.Issue.HTMLURL, - }, - }, - }, - }, - }, nil + title, _, attachmentText, color := getIssuesPayloadInfo(p, noneLinkFormatter, false) + + return createMSTeamsPayload( + p.Repository, + p.Sender, + title, + attachmentText, + p.Issue.HTMLURL, + color, + &MSTeamsFact{"Issue #:", fmt.Sprintf("%d", p.Issue.ID)}, + ), nil } // IssueComment implements PayloadConvertor IssueComment method func (m *MSTeamsPayload) IssueComment(p *api.IssueCommentPayload) (api.Payloader, error) { - text, _, color := getIssueCommentPayloadInfo(p, noneLinkFormatter, false) - - return &MSTeamsPayload{ - Type: "MessageCard", - Context: "https://schema.org/extensions", - ThemeColor: fmt.Sprintf("%x", color), - Title: text, - Summary: text, - Sections: []MSTeamsSection{ - { - ActivityTitle: p.Sender.FullName, - ActivitySubtitle: p.Sender.UserName, - ActivityImage: p.Sender.AvatarURL, - Text: p.Comment.Body, - Facts: []MSTeamsFact{ - { - Name: "Repository:", - Value: p.Repository.FullName, - }, - { - Name: "Issue #:", - Value: fmt.Sprintf("%d", p.Issue.ID), - }, - }, - }, - }, - PotentialAction: []MSTeamsAction{ - { - Type: "OpenUri", - Name: "View in Gitea", - Targets: []MSTeamsActionTarget{ - { - Os: "default", - URI: p.Comment.HTMLURL, - }, - }, - }, - }, - }, nil + title, _, color := getIssueCommentPayloadInfo(p, noneLinkFormatter, false) + + return createMSTeamsPayload( + p.Repository, + p.Sender, + title, + p.Comment.Body, + p.Comment.HTMLURL, + color, + &MSTeamsFact{"Issue #:", fmt.Sprintf("%d", p.Issue.ID)}, + ), nil } // PullRequest implements PayloadConvertor PullRequest method func (m *MSTeamsPayload) PullRequest(p *api.PullRequestPayload) (api.Payloader, error) { - text, _, attachmentText, color := getPullRequestPayloadInfo(p, noneLinkFormatter, false) - - return &MSTeamsPayload{ - Type: "MessageCard", - Context: "https://schema.org/extensions", - ThemeColor: fmt.Sprintf("%x", color), - Title: text, - Summary: text, - Sections: []MSTeamsSection{ - { - ActivityTitle: p.Sender.FullName, - ActivitySubtitle: p.Sender.UserName, - ActivityImage: p.Sender.AvatarURL, - Text: attachmentText, - Facts: []MSTeamsFact{ - { - Name: "Repository:", - Value: p.Repository.FullName, - }, - { - Name: "Pull request #:", - Value: fmt.Sprintf("%d", p.PullRequest.ID), - }, - }, - }, - }, - PotentialAction: []MSTeamsAction{ - { - Type: "OpenUri", - Name: "View in Gitea", - Targets: []MSTeamsActionTarget{ - { - Os: "default", - URI: p.PullRequest.HTMLURL, - }, - }, - }, - }, - }, nil + title, _, attachmentText, color := getPullRequestPayloadInfo(p, noneLinkFormatter, false) + + return createMSTeamsPayload( + p.Repository, + p.Sender, + title, + attachmentText, + p.PullRequest.HTMLURL, + color, + &MSTeamsFact{"Pull request #:", fmt.Sprintf("%d", p.PullRequest.ID)}, + ), nil } // Review implements PayloadConvertor Review method @@ -428,43 +235,15 @@ func (m *MSTeamsPayload) Review(p *api.PullRequestPayload, event models.HookEven } } - return &MSTeamsPayload{ - Type: "MessageCard", - Context: "https://schema.org/extensions", - ThemeColor: fmt.Sprintf("%x", color), - Title: title, - Summary: title, - Sections: []MSTeamsSection{ - { - ActivityTitle: p.Sender.FullName, - ActivitySubtitle: p.Sender.UserName, - ActivityImage: p.Sender.AvatarURL, - Text: text, - Facts: []MSTeamsFact{ - { - Name: "Repository:", - Value: p.Repository.FullName, - }, - { - Name: "Pull request #:", - Value: fmt.Sprintf("%d", p.PullRequest.ID), - }, - }, - }, - }, - PotentialAction: []MSTeamsAction{ - { - Type: "OpenUri", - Name: "View in Gitea", - Targets: []MSTeamsActionTarget{ - { - Os: "default", - URI: p.PullRequest.HTMLURL, - }, - }, - }, - }, - }, nil + return createMSTeamsPayload( + p.Repository, + p.Sender, + title, + text, + p.PullRequest.HTMLURL, + color, + &MSTeamsFact{"Pull request #:", fmt.Sprintf("%d", p.PullRequest.ID)}, + ), nil } // Repository implements PayloadConvertor Repository method @@ -481,66 +260,61 @@ func (m *MSTeamsPayload) Repository(p *api.RepositoryPayload) (api.Payloader, er color = yellowColor } - return &MSTeamsPayload{ - Type: "MessageCard", - Context: "https://schema.org/extensions", - ThemeColor: fmt.Sprintf("%x", color), - Title: title, - Summary: title, - Sections: []MSTeamsSection{ - { - ActivityTitle: p.Sender.FullName, - ActivitySubtitle: p.Sender.UserName, - ActivityImage: p.Sender.AvatarURL, - Facts: []MSTeamsFact{ - { - Name: "Repository:", - Value: p.Repository.FullName, - }, - }, - }, - }, - PotentialAction: []MSTeamsAction{ - { - Type: "OpenUri", - Name: "View in Gitea", - Targets: []MSTeamsActionTarget{ - { - Os: "default", - URI: url, - }, - }, - }, - }, - }, nil + return createMSTeamsPayload( + p.Repository, + p.Sender, + title, + "", + url, + color, + nil, + ), nil } // Release implements PayloadConvertor Release method func (m *MSTeamsPayload) Release(p *api.ReleasePayload) (api.Payloader, error) { - text, color := getReleasePayloadInfo(p, noneLinkFormatter, false) + title, color := getReleasePayloadInfo(p, noneLinkFormatter, false) + + return createMSTeamsPayload( + p.Repository, + p.Sender, + title, + "", + p.Release.URL, + color, + &MSTeamsFact{"Tag:", p.Release.TagName}, + ), nil +} + +// GetMSTeamsPayload converts a MSTeams webhook into a MSTeamsPayload +func GetMSTeamsPayload(p api.Payloader, event models.HookEventType, meta string) (api.Payloader, error) { + return convertPayloader(new(MSTeamsPayload), p, event) +} + +func createMSTeamsPayload(r *api.Repository, s *api.User, title, text, actionTarget string, color int, fact *MSTeamsFact) *MSTeamsPayload { + facts := []MSTeamsFact{ + { + Name: "Repository:", + Value: r.FullName, + }, + } + if fact != nil { + facts = append(facts, *fact) + } return &MSTeamsPayload{ Type: "MessageCard", Context: "https://schema.org/extensions", ThemeColor: fmt.Sprintf("%x", color), - Title: text, - Summary: text, + Title: title, + Summary: title, Sections: []MSTeamsSection{ { - ActivityTitle: p.Sender.FullName, - ActivitySubtitle: p.Sender.UserName, - ActivityImage: p.Sender.AvatarURL, - Text: p.Release.Note, - Facts: []MSTeamsFact{ - { - Name: "Repository:", - Value: p.Repository.FullName, - }, - { - Name: "Tag:", - Value: p.Release.TagName, - }, - }, + ActivityTitle: s.FullName, + ActivitySubtitle: s.UserName, + ActivityImage: s.AvatarURL, + Text: text, + Facts: facts, }, }, PotentialAction: []MSTeamsAction{ @@ -550,15 +324,10 @@ func (m *MSTeamsPayload) Release(p *api.ReleasePayload) (api.Payloader, error) { Targets: []MSTeamsActionTarget{ { Os: "default", - URI: p.Release.URL, + URI: actionTarget, }, }, }, }, - }, nil -} - -// GetMSTeamsPayload converts a MSTeams webhook into a MSTeamsPayload -func GetMSTeamsPayload(p api.Payloader, event models.HookEventType, meta string) (api.Payloader, error) { - return convertPayloader(new(MSTeamsPayload), p, event) + } } diff --git a/services/webhook/msteams_test.go b/services/webhook/msteams_test.go new file mode 100644 index 000000000..2f54c39d3 --- /dev/null +++ b/services/webhook/msteams_test.go @@ -0,0 +1,374 @@ +// Copyright 2021 The Gitea Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package webhook + +import ( + "testing" + + "code.gitea.io/gitea/models" + api "code.gitea.io/gitea/modules/structs" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestMSTeamsPayload(t *testing.T) { + t.Run("Create", func(t *testing.T) { + p := createTestPayload() + + d := new(MSTeamsPayload) + pl, err := d.Create(p) + require.NoError(t, err) + require.NotNil(t, pl) + require.IsType(t, &MSTeamsPayload{}, pl) + + assert.Equal(t, "[test/repo] branch test created", pl.(*MSTeamsPayload).Title) + assert.Equal(t, "[test/repo] branch test created", pl.(*MSTeamsPayload).Summary) + assert.Len(t, pl.(*MSTeamsPayload).Sections, 1) + assert.Equal(t, "user1", pl.(*MSTeamsPayload).Sections[0].ActivitySubtitle) + assert.Empty(t, pl.(*MSTeamsPayload).Sections[0].Text) + assert.Len(t, pl.(*MSTeamsPayload).Sections[0].Facts, 2) + for _, fact := range pl.(*MSTeamsPayload).Sections[0].Facts { + if fact.Name == "Repository:" { + assert.Equal(t, p.Repo.FullName, fact.Value) + } else if fact.Name == "branch:" { + assert.Equal(t, "test", fact.Value) + } else { + t.Fail() + } + } + assert.Len(t, pl.(*MSTeamsPayload).PotentialAction, 1) + assert.Len(t, pl.(*MSTeamsPayload).PotentialAction[0].Targets, 1) + assert.Equal(t, "http://localhost:3000/test/repo/src/test", pl.(*MSTeamsPayload).PotentialAction[0].Targets[0].URI) + }) + + t.Run("Delete", func(t *testing.T) { + p := deleteTestPayload() + + d := new(MSTeamsPayload) + pl, err := d.Delete(p) + require.NoError(t, err) + require.NotNil(t, pl) + require.IsType(t, &MSTeamsPayload{}, pl) + + assert.Equal(t, "[test/repo] branch test deleted", pl.(*MSTeamsPayload).Title) + assert.Equal(t, "[test/repo] branch test deleted", pl.(*MSTeamsPayload).Summary) + assert.Len(t, pl.(*MSTeamsPayload).Sections, 1) + assert.Equal(t, "user1", pl.(*MSTeamsPayload).Sections[0].ActivitySubtitle) + assert.Empty(t, pl.(*MSTeamsPayload).Sections[0].Text) + assert.Len(t, pl.(*MSTeamsPayload).Sections[0].Facts, 2) + for _, fact := range pl.(*MSTeamsPayload).Sections[0].Facts { + if fact.Name == "Repository:" { + assert.Equal(t, p.Repo.FullName, fact.Value) + } else if fact.Name == "branch:" { + assert.Equal(t, "test", fact.Value) + } else { + t.Fail() + } + } + assert.Len(t, pl.(*MSTeamsPayload).PotentialAction, 1) + assert.Len(t, pl.(*MSTeamsPayload).PotentialAction[0].Targets, 1) + assert.Equal(t, "http://localhost:3000/test/repo/src/test", pl.(*MSTeamsPayload).PotentialAction[0].Targets[0].URI) + }) + + t.Run("Fork", func(t *testing.T) { + p := forkTestPayload() + + d := new(MSTeamsPayload) + pl, err := d.Fork(p) + require.NoError(t, err) + require.NotNil(t, pl) + require.IsType(t, &MSTeamsPayload{}, pl) + + assert.Equal(t, "test/repo2 is forked to test/repo", pl.(*MSTeamsPayload).Title) + assert.Equal(t, "test/repo2 is forked to test/repo", pl.(*MSTeamsPayload).Summary) + assert.Len(t, pl.(*MSTeamsPayload).Sections, 1) + assert.Equal(t, "user1", pl.(*MSTeamsPayload).Sections[0].ActivitySubtitle) + assert.Empty(t, pl.(*MSTeamsPayload).Sections[0].Text) + assert.Len(t, pl.(*MSTeamsPayload).Sections[0].Facts, 2) + for _, fact := range pl.(*MSTeamsPayload).Sections[0].Facts { + if fact.Name == "Repository:" { + assert.Equal(t, p.Repo.FullName, fact.Value) + } else if fact.Name == "Forkee:" { + assert.Equal(t, p.Forkee.FullName, fact.Value) + } else { + t.Fail() + } + } + assert.Len(t, pl.(*MSTeamsPayload).PotentialAction, 1) + assert.Len(t, pl.(*MSTeamsPayload).PotentialAction[0].Targets, 1) + assert.Equal(t, "http://localhost:3000/test/repo", pl.(*MSTeamsPayload).PotentialAction[0].Targets[0].URI) + }) + + t.Run("Push", func(t *testing.T) { + p := pushTestPayload() + + d := new(MSTeamsPayload) + pl, err := d.Push(p) + require.NoError(t, err) + require.NotNil(t, pl) + require.IsType(t, &MSTeamsPayload{}, pl) + + assert.Equal(t, "[test/repo:test] 2 new commits", pl.(*MSTeamsPayload).Title) + assert.Equal(t, "[test/repo:test] 2 new commits", pl.(*MSTeamsPayload).Summary) + assert.Len(t, pl.(*MSTeamsPayload).Sections, 1) + assert.Equal(t, "user1", pl.(*MSTeamsPayload).Sections[0].ActivitySubtitle) + assert.Equal(t, "[2020558](http://localhost:3000/test/repo/commit/2020558fe2e34debb818a514715839cabd25e778) commit message - user1\n\n[2020558](http://localhost:3000/test/repo/commit/2020558fe2e34debb818a514715839cabd25e778) commit message - user1", pl.(*MSTeamsPayload).Sections[0].Text) + assert.Len(t, pl.(*MSTeamsPayload).Sections[0].Facts, 2) + for _, fact := range pl.(*MSTeamsPayload).Sections[0].Facts { + if fact.Name == "Repository:" { + assert.Equal(t, p.Repo.FullName, fact.Value) + } else if fact.Name == "Commit count:" { + assert.Equal(t, "2", fact.Value) + } else { + t.Fail() + } + } + assert.Len(t, pl.(*MSTeamsPayload).PotentialAction, 1) + assert.Len(t, pl.(*MSTeamsPayload).PotentialAction[0].Targets, 1) + assert.Equal(t, "http://localhost:3000/test/repo/src/test", pl.(*MSTeamsPayload).PotentialAction[0].Targets[0].URI) + }) + + t.Run("Issue", func(t *testing.T) { + p := issueTestPayload() + + d := new(MSTeamsPayload) + p.Action = api.HookIssueOpened + pl, err := d.Issue(p) + require.NoError(t, err) + require.NotNil(t, pl) + require.IsType(t, &MSTeamsPayload{}, pl) + + assert.Equal(t, "[test/repo] Issue opened: #2 crash", pl.(*MSTeamsPayload).Title) + assert.Equal(t, "[test/repo] Issue opened: #2 crash", pl.(*MSTeamsPayload).Summary) + assert.Len(t, pl.(*MSTeamsPayload).Sections, 1) + assert.Equal(t, "user1", pl.(*MSTeamsPayload).Sections[0].ActivitySubtitle) + assert.Equal(t, "issue body", pl.(*MSTeamsPayload).Sections[0].Text) + assert.Len(t, pl.(*MSTeamsPayload).Sections[0].Facts, 2) + for _, fact := range pl.(*MSTeamsPayload).Sections[0].Facts { + if fact.Name == "Repository:" { + assert.Equal(t, p.Repository.FullName, fact.Value) + } else if fact.Name == "Issue #:" { + assert.Equal(t, "2", fact.Value) + } else { + t.Fail() + } + } + assert.Len(t, pl.(*MSTeamsPayload).PotentialAction, 1) + assert.Len(t, pl.(*MSTeamsPayload).PotentialAction[0].Targets, 1) + assert.Equal(t, "http://localhost:3000/test/repo/issues/2", pl.(*MSTeamsPayload).PotentialAction[0].Targets[0].URI) + + p.Action = api.HookIssueClosed + pl, err = d.Issue(p) + require.NoError(t, err) + require.NotNil(t, pl) + require.IsType(t, &MSTeamsPayload{}, pl) + + assert.Equal(t, "[test/repo] Issue closed: #2 crash", pl.(*MSTeamsPayload).Title) + assert.Equal(t, "[test/repo] Issue closed: #2 crash", pl.(*MSTeamsPayload).Summary) + assert.Len(t, pl.(*MSTeamsPayload).Sections, 1) + assert.Equal(t, "user1", pl.(*MSTeamsPayload).Sections[0].ActivitySubtitle) + assert.Empty(t, pl.(*MSTeamsPayload).Sections[0].Text) + assert.Len(t, pl.(*MSTeamsPayload).Sections[0].Facts, 2) + for _, fact := range pl.(*MSTeamsPayload).Sections[0].Facts { + if fact.Name == "Repository:" { + assert.Equal(t, p.Repository.FullName, fact.Value) + } else if fact.Name == "Issue #:" { + assert.Equal(t, "2", fact.Value) + } else { + t.Fail() + } + } + assert.Len(t, pl.(*MSTeamsPayload).PotentialAction, 1) + assert.Len(t, pl.(*MSTeamsPayload).PotentialAction[0].Targets, 1) + assert.Equal(t, "http://localhost:3000/test/repo/issues/2", pl.(*MSTeamsPayload).PotentialAction[0].Targets[0].URI) + }) + + t.Run("IssueComment", func(t *testing.T) { + p := issueCommentTestPayload() + + d := new(MSTeamsPayload) + pl, err := d.IssueComment(p) + require.NoError(t, err) + require.NotNil(t, pl) + require.IsType(t, &MSTeamsPayload{}, pl) + + assert.Equal(t, "[test/repo] New comment on issue #2 crash", pl.(*MSTeamsPayload).Title) + assert.Equal(t, "[test/repo] New comment on issue #2 crash", pl.(*MSTeamsPayload).Summary) + assert.Len(t, pl.(*MSTeamsPayload).Sections, 1) + assert.Equal(t, "user1", pl.(*MSTeamsPayload).Sections[0].ActivitySubtitle) + assert.Equal(t, "more info needed", pl.(*MSTeamsPayload).Sections[0].Text) + assert.Len(t, pl.(*MSTeamsPayload).Sections[0].Facts, 2) + for _, fact := range pl.(*MSTeamsPayload).Sections[0].Facts { + if fact.Name == "Repository:" { + assert.Equal(t, p.Repository.FullName, fact.Value) + } else if fact.Name == "Issue #:" { + assert.Equal(t, "2", fact.Value) + } else { + t.Fail() + } + } + assert.Len(t, pl.(*MSTeamsPayload).PotentialAction, 1) + assert.Len(t, pl.(*MSTeamsPayload).PotentialAction[0].Targets, 1) + assert.Equal(t, "http://localhost:3000/test/repo/issues/2#issuecomment-4", pl.(*MSTeamsPayload).PotentialAction[0].Targets[0].URI) + }) + + t.Run("PullRequest", func(t *testing.T) { + p := pullRequestTestPayload() + + d := new(MSTeamsPayload) + pl, err := d.PullRequest(p) + require.NoError(t, err) + require.NotNil(t, pl) + require.IsType(t, &MSTeamsPayload{}, pl) + + assert.Equal(t, "[test/repo] Pull request opened: #12 Fix bug", pl.(*MSTeamsPayload).Title) + assert.Equal(t, "[test/repo] Pull request opened: #12 Fix bug", pl.(*MSTeamsPayload).Summary) + assert.Len(t, pl.(*MSTeamsPayload).Sections, 1) + assert.Equal(t, "user1", pl.(*MSTeamsPayload).Sections[0].ActivitySubtitle) + assert.Equal(t, "fixes bug #2", pl.(*MSTeamsPayload).Sections[0].Text) + assert.Len(t, pl.(*MSTeamsPayload).Sections[0].Facts, 2) + for _, fact := range pl.(*MSTeamsPayload).Sections[0].Facts { + if fact.Name == "Repository:" { + assert.Equal(t, p.Repository.FullName, fact.Value) + } else if fact.Name == "Pull request #:" { + assert.Equal(t, "12", fact.Value) + } else { + t.Fail() + } + } + assert.Len(t, pl.(*MSTeamsPayload).PotentialAction, 1) + assert.Len(t, pl.(*MSTeamsPayload).PotentialAction[0].Targets, 1) + assert.Equal(t, "http://localhost:3000/test/repo/pulls/12", pl.(*MSTeamsPayload).PotentialAction[0].Targets[0].URI) + }) + + t.Run("PullRequestComment", func(t *testing.T) { + p := pullRequestCommentTestPayload() + + d := new(MSTeamsPayload) + pl, err := d.IssueComment(p) + require.NoError(t, err) + require.NotNil(t, pl) + require.IsType(t, &MSTeamsPayload{}, pl) + + assert.Equal(t, "[test/repo] New comment on pull request #12 Fix bug", pl.(*MSTeamsPayload).Title) + assert.Equal(t, "[test/repo] New comment on pull request #12 Fix bug", pl.(*MSTeamsPayload).Summary) + assert.Len(t, pl.(*MSTeamsPayload).Sections, 1) + assert.Equal(t, "user1", pl.(*MSTeamsPayload).Sections[0].ActivitySubtitle) + assert.Equal(t, "changes requested", pl.(*MSTeamsPayload).Sections[0].Text) + assert.Len(t, pl.(*MSTeamsPayload).Sections[0].Facts, 2) + for _, fact := range pl.(*MSTeamsPayload).Sections[0].Facts { + if fact.Name == "Repository:" { + assert.Equal(t, p.Repository.FullName, fact.Value) + } else if fact.Name == "Issue #:" { + assert.Equal(t, "12", fact.Value) + } else { + t.Fail() + } + } + assert.Len(t, pl.(*MSTeamsPayload).PotentialAction, 1) + assert.Len(t, pl.(*MSTeamsPayload).PotentialAction[0].Targets, 1) + assert.Equal(t, "http://localhost:3000/test/repo/pulls/12#issuecomment-4", pl.(*MSTeamsPayload).PotentialAction[0].Targets[0].URI) + }) + + t.Run("Review", func(t *testing.T) { + p := pullRequestTestPayload() + p.Action = api.HookIssueReviewed + + d := new(MSTeamsPayload) + pl, err := d.Review(p, models.HookEventPullRequestReviewApproved) + require.NoError(t, err) + require.NotNil(t, pl) + require.IsType(t, &MSTeamsPayload{}, pl) + + assert.Equal(t, "[test/repo] Pull request review approved: #12 Fix bug", pl.(*MSTeamsPayload).Title) + assert.Equal(t, "[test/repo] Pull request review approved: #12 Fix bug", pl.(*MSTeamsPayload).Summary) + assert.Len(t, pl.(*MSTeamsPayload).Sections, 1) + assert.Equal(t, "user1", pl.(*MSTeamsPayload).Sections[0].ActivitySubtitle) + assert.Equal(t, "good job", pl.(*MSTeamsPayload).Sections[0].Text) + assert.Len(t, pl.(*MSTeamsPayload).Sections[0].Facts, 2) + for _, fact := range pl.(*MSTeamsPayload).Sections[0].Facts { + if fact.Name == "Repository:" { + assert.Equal(t, p.Repository.FullName, fact.Value) + } else if fact.Name == "Pull request #:" { + assert.Equal(t, "12", fact.Value) + } else { + t.Fail() + } + } + assert.Len(t, pl.(*MSTeamsPayload).PotentialAction, 1) + assert.Len(t, pl.(*MSTeamsPayload).PotentialAction[0].Targets, 1) + assert.Equal(t, "http://localhost:3000/test/repo/pulls/12", pl.(*MSTeamsPayload).PotentialAction[0].Targets[0].URI) + }) + + t.Run("Repository", func(t *testing.T) { + p := repositoryTestPayload() + + d := new(MSTeamsPayload) + pl, err := d.Repository(p) + require.NoError(t, err) + require.NotNil(t, pl) + require.IsType(t, &MSTeamsPayload{}, pl) + + assert.Equal(t, "[test/repo] Repository created", pl.(*MSTeamsPayload).Title) + assert.Equal(t, "[test/repo] Repository created", pl.(*MSTeamsPayload).Summary) + assert.Len(t, pl.(*MSTeamsPayload).Sections, 1) + assert.Equal(t, "user1", pl.(*MSTeamsPayload).Sections[0].ActivitySubtitle) + assert.Empty(t, pl.(*MSTeamsPayload).Sections[0].Text) + assert.Len(t, pl.(*MSTeamsPayload).Sections[0].Facts, 1) + for _, fact := range pl.(*MSTeamsPayload).Sections[0].Facts { + if fact.Name == "Repository:" { + assert.Equal(t, p.Repository.FullName, fact.Value) + } else { + t.Fail() + } + } + assert.Len(t, pl.(*MSTeamsPayload).PotentialAction, 1) + assert.Len(t, pl.(*MSTeamsPayload).PotentialAction[0].Targets, 1) + assert.Equal(t, "http://localhost:3000/test/repo", pl.(*MSTeamsPayload).PotentialAction[0].Targets[0].URI) + }) + + t.Run("Release", func(t *testing.T) { + p := pullReleaseTestPayload() + + d := new(MSTeamsPayload) + pl, err := d.Release(p) + require.NoError(t, err) + require.NotNil(t, pl) + require.IsType(t, &MSTeamsPayload{}, pl) + + assert.Equal(t, "[test/repo] Release created: v1.0", pl.(*MSTeamsPayload).Title) + assert.Equal(t, "[test/repo] Release created: v1.0", pl.(*MSTeamsPayload).Summary) + assert.Len(t, pl.(*MSTeamsPayload).Sections, 1) + assert.Equal(t, "user1", pl.(*MSTeamsPayload).Sections[0].ActivitySubtitle) + assert.Empty(t, pl.(*MSTeamsPayload).Sections[0].Text) + assert.Len(t, pl.(*MSTeamsPayload).Sections[0].Facts, 2) + for _, fact := range pl.(*MSTeamsPayload).Sections[0].Facts { + if fact.Name == "Repository:" { + assert.Equal(t, p.Repository.FullName, fact.Value) + } else if fact.Name == "Tag:" { + assert.Equal(t, "v1.0", fact.Value) + } else { + t.Fail() + } + } + assert.Len(t, pl.(*MSTeamsPayload).PotentialAction, 1) + assert.Len(t, pl.(*MSTeamsPayload).PotentialAction[0].Targets, 1) + assert.Equal(t, "http://localhost:3000/api/v1/repos/test/repo/releases/2", pl.(*MSTeamsPayload).PotentialAction[0].Targets[0].URI) + }) +} + +func TestMSTeamsJSONPayload(t *testing.T) { + p := pushTestPayload() + + pl, err := new(MSTeamsPayload).Push(p) + require.NoError(t, err) + require.NotNil(t, pl) + require.IsType(t, &MSTeamsPayload{}, pl) + + json, err := pl.JSONPayload() + require.NoError(t, err) + assert.NotEmpty(t, json) +} diff --git a/services/webhook/slack.go b/services/webhook/slack.go index f5c857f2a..1de9c9c37 100644 --- a/services/webhook/slack.go +++ b/services/webhook/slack.go @@ -111,12 +111,7 @@ func (s *SlackPayload) Create(p *api.CreatePayload) (api.Payloader, error) { refLink := SlackLinkToRef(p.Repo.HTMLURL, p.Ref) text := fmt.Sprintf("[%s:%s] %s created by %s", repoLink, refLink, p.RefType, p.Sender.UserName) - return &SlackPayload{ - Channel: s.Channel, - Text: text, - Username: s.Username, - IconURL: s.IconURL, - }, nil + return s.createPayload(text, nil), nil } // Delete composes Slack payload for delete a branch or tag. @@ -124,12 +119,8 @@ func (s *SlackPayload) Delete(p *api.DeletePayload) (api.Payloader, error) { refName := git.RefEndName(p.Ref) repoLink := SlackLinkFormatter(p.Repo.HTMLURL, p.Repo.FullName) text := fmt.Sprintf("[%s:%s] %s deleted by %s", repoLink, refName, p.RefType, p.Sender.UserName) - return &SlackPayload{ - Channel: s.Channel, - Text: text, - Username: s.Username, - IconURL: s.IconURL, - }, nil + + return s.createPayload(text, nil), nil } // Fork composes Slack payload for forked by a repository. @@ -137,66 +128,46 @@ func (s *SlackPayload) Fork(p *api.ForkPayload) (api.Payloader, error) { baseLink := SlackLinkFormatter(p.Forkee.HTMLURL, p.Forkee.FullName) forkLink := SlackLinkFormatter(p.Repo.HTMLURL, p.Repo.FullName) text := fmt.Sprintf("%s is forked to %s", baseLink, forkLink) - return &SlackPayload{ - Channel: s.Channel, - Text: text, - Username: s.Username, - IconURL: s.IconURL, - }, nil + + return s.createPayload(text, nil), nil } // Issue implements PayloadConvertor Issue method func (s *SlackPayload) Issue(p *api.IssuePayload) (api.Payloader, error) { text, issueTitle, attachmentText, color := getIssuesPayloadInfo(p, SlackLinkFormatter, true) - pl := &SlackPayload{ - Channel: s.Channel, - Text: text, - Username: s.Username, - IconURL: s.IconURL, - } + var attachments []SlackAttachment if attachmentText != "" { attachmentText = SlackTextFormatter(attachmentText) issueTitle = SlackTextFormatter(issueTitle) - pl.Attachments = []SlackAttachment{{ + attachments = append(attachments, SlackAttachment{ Color: fmt.Sprintf("%x", color), Title: issueTitle, TitleLink: p.Issue.HTMLURL, Text: attachmentText, - }} + }) } - return pl, nil + return s.createPayload(text, attachments), nil } // IssueComment implements PayloadConvertor IssueComment method func (s *SlackPayload) IssueComment(p *api.IssueCommentPayload) (api.Payloader, error) { text, issueTitle, color := getIssueCommentPayloadInfo(p, SlackLinkFormatter, true) - return &SlackPayload{ - Channel: s.Channel, - Text: text, - Username: s.Username, - IconURL: s.IconURL, - Attachments: []SlackAttachment{{ - Color: fmt.Sprintf("%x", color), - Title: issueTitle, - TitleLink: p.Comment.HTMLURL, - Text: SlackTextFormatter(p.Comment.Body), - }}, - }, nil + return s.createPayload(text, []SlackAttachment{{ + Color: fmt.Sprintf("%x", color), + Title: issueTitle, + TitleLink: p.Comment.HTMLURL, + Text: SlackTextFormatter(p.Comment.Body), + }}), nil } // Release implements PayloadConvertor Release method func (s *SlackPayload) Release(p *api.ReleasePayload) (api.Payloader, error) { text, _ := getReleasePayloadInfo(p, SlackLinkFormatter, true) - return &SlackPayload{ - Channel: s.Channel, - Text: text, - Username: s.Username, - IconURL: s.IconURL, - }, nil + return s.createPayload(text, nil), nil } // Push implements PayloadConvertor Push method @@ -232,42 +203,31 @@ func (s *SlackPayload) Push(p *api.PushPayload) (api.Payloader, error) { } } - return &SlackPayload{ - Channel: s.Channel, - Text: text, - Username: s.Username, - IconURL: s.IconURL, - Attachments: []SlackAttachment{{ - Color: s.Color, - Title: p.Repo.HTMLURL, - TitleLink: p.Repo.HTMLURL, - Text: attachmentText, - }}, - }, nil + return s.createPayload(text, []SlackAttachment{{ + Color: s.Color, + Title: p.Repo.HTMLURL, + TitleLink: p.Repo.HTMLURL, + Text: attachmentText, + }}), nil } // PullRequest implements PayloadConvertor PullRequest method func (s *SlackPayload) PullRequest(p *api.PullRequestPayload) (api.Payloader, error) { text, issueTitle, attachmentText, color := getPullRequestPayloadInfo(p, SlackLinkFormatter, true) - pl := &SlackPayload{ - Channel: s.Channel, - Text: text, - Username: s.Username, - IconURL: s.IconURL, - } + var attachments []SlackAttachment if attachmentText != "" { attachmentText = SlackTextFormatter(p.PullRequest.Body) issueTitle = SlackTextFormatter(issueTitle) - pl.Attachments = []SlackAttachment{{ + attachments = append(attachments, SlackAttachment{ Color: fmt.Sprintf("%x", color), Title: issueTitle, TitleLink: p.PullRequest.URL, Text: attachmentText, - }} + }) } - return pl, nil + return s.createPayload(text, attachments), nil } // Review implements PayloadConvertor Review method @@ -288,12 +248,7 @@ func (s *SlackPayload) Review(p *api.PullRequestPayload, event models.HookEventT text = fmt.Sprintf("[%s] Pull request review %s: [%s](%s) by %s", repoLink, action, title, titleLink, senderLink) } - return &SlackPayload{ - Channel: s.Channel, - Text: text, - Username: s.Username, - IconURL: s.IconURL, - }, nil + return s.createPayload(text, nil), nil } // Repository implements PayloadConvertor Repository method @@ -309,12 +264,17 @@ func (s *SlackPayload) Repository(p *api.RepositoryPayload) (api.Payloader, erro text = fmt.Sprintf("[%s] Repository deleted by %s", repoLink, senderLink) } + return s.createPayload(text, nil), nil +} + +func (s *SlackPayload) createPayload(text string, attachments []SlackAttachment) *SlackPayload { return &SlackPayload{ - Channel: s.Channel, - Text: text, - Username: s.Username, - IconURL: s.IconURL, - }, nil + Channel: s.Channel, + Text: text, + Username: s.Username, + IconURL: s.IconURL, + Attachments: attachments, + } } // GetSlackPayload converts a slack webhook into a SlackPayload diff --git a/services/webhook/slack_test.go b/services/webhook/slack_test.go index 20de80bd6..3f279810c 100644 --- a/services/webhook/slack_test.go +++ b/services/webhook/slack_test.go @@ -7,74 +7,166 @@ package webhook import ( "testing" + "code.gitea.io/gitea/models" api "code.gitea.io/gitea/modules/structs" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) -func TestSlackIssuesPayloadOpened(t *testing.T) { - p := issueTestPayload() - p.Action = api.HookIssueOpened +func TestSlackPayload(t *testing.T) { + t.Run("Create", func(t *testing.T) { + p := createTestPayload() - s := new(SlackPayload) - s.Username = p.Sender.UserName + d := new(SlackPayload) + pl, err := d.Create(p) + require.NoError(t, err) + require.NotNil(t, pl) + require.IsType(t, &SlackPayload{}, pl) - pl, err := s.Issue(p) - require.NoError(t, err) - require.NotNil(t, pl) - assert.Equal(t, "[] Issue opened: by ", pl.(*SlackPayload).Text) + assert.Equal(t, "[:] branch created by user1", pl.(*SlackPayload).Text) + }) - p.Action = api.HookIssueClosed - pl, err = s.Issue(p) - require.NoError(t, err) - require.NotNil(t, pl) - assert.Equal(t, "[] Issue closed: by ", pl.(*SlackPayload).Text) -} + t.Run("Delete", func(t *testing.T) { + p := deleteTestPayload() -func TestSlackIssueCommentPayload(t *testing.T) { - p := issueCommentTestPayload() - s := new(SlackPayload) - s.Username = p.Sender.UserName + d := new(SlackPayload) + pl, err := d.Delete(p) + require.NoError(t, err) + require.NotNil(t, pl) + require.IsType(t, &SlackPayload{}, pl) - pl, err := s.IssueComment(p) - require.NoError(t, err) - require.NotNil(t, pl) + assert.Equal(t, "[:test] branch deleted by user1", pl.(*SlackPayload).Text) + }) - assert.Equal(t, "[] New comment on issue by ", pl.(*SlackPayload).Text) -} + t.Run("Fork", func(t *testing.T) { + p := forkTestPayload() -func TestSlackPullRequestCommentPayload(t *testing.T) { - p := pullRequestCommentTestPayload() - s := new(SlackPayload) - s.Username = p.Sender.UserName + d := new(SlackPayload) + pl, err := d.Fork(p) + require.NoError(t, err) + require.NotNil(t, pl) + require.IsType(t, &SlackPayload{}, pl) - pl, err := s.IssueComment(p) - require.NoError(t, err) - require.NotNil(t, pl) + assert.Equal(t, " is forked to ", pl.(*SlackPayload).Text) + }) - assert.Equal(t, "[] New comment on pull request by ", pl.(*SlackPayload).Text) -} + t.Run("Push", func(t *testing.T) { + p := pushTestPayload() -func TestSlackReleasePayload(t *testing.T) { - p := pullReleaseTestPayload() - s := new(SlackPayload) - s.Username = p.Sender.UserName + d := new(SlackPayload) + pl, err := d.Push(p) + require.NoError(t, err) + require.NotNil(t, pl) + require.IsType(t, &SlackPayload{}, pl) - pl, err := s.Release(p) - require.NoError(t, err) - require.NotNil(t, pl) + assert.Equal(t, "[:] 2 new commits pushed by user1", pl.(*SlackPayload).Text) + }) + + t.Run("Issue", func(t *testing.T) { + p := issueTestPayload() + + d := new(SlackPayload) + p.Action = api.HookIssueOpened + pl, err := d.Issue(p) + require.NoError(t, err) + require.NotNil(t, pl) + require.IsType(t, &SlackPayload{}, pl) + + assert.Equal(t, "[] Issue opened: by ", pl.(*SlackPayload).Text) - assert.Equal(t, "[] Release created: by ", pl.(*SlackPayload).Text) + p.Action = api.HookIssueClosed + pl, err = d.Issue(p) + require.NoError(t, err) + require.NotNil(t, pl) + require.IsType(t, &SlackPayload{}, pl) + + assert.Equal(t, "[] Issue closed: by ", pl.(*SlackPayload).Text) + }) + + t.Run("IssueComment", func(t *testing.T) { + p := issueCommentTestPayload() + + d := new(SlackPayload) + pl, err := d.IssueComment(p) + require.NoError(t, err) + require.NotNil(t, pl) + require.IsType(t, &SlackPayload{}, pl) + + assert.Equal(t, "[] New comment on issue by ", pl.(*SlackPayload).Text) + }) + + t.Run("PullRequest", func(t *testing.T) { + p := pullRequestTestPayload() + + d := new(SlackPayload) + pl, err := d.PullRequest(p) + require.NoError(t, err) + require.NotNil(t, pl) + require.IsType(t, &SlackPayload{}, pl) + + assert.Equal(t, "[] Pull request opened: by ", pl.(*SlackPayload).Text) + }) + + t.Run("PullRequestComment", func(t *testing.T) { + p := pullRequestCommentTestPayload() + + d := new(SlackPayload) + pl, err := d.IssueComment(p) + require.NoError(t, err) + require.NotNil(t, pl) + require.IsType(t, &SlackPayload{}, pl) + + assert.Equal(t, "[] New comment on pull request by ", pl.(*SlackPayload).Text) + }) + + t.Run("Review", func(t *testing.T) { + p := pullRequestTestPayload() + p.Action = api.HookIssueReviewed + + d := new(SlackPayload) + pl, err := d.Review(p, models.HookEventPullRequestReviewApproved) + require.NoError(t, err) + require.NotNil(t, pl) + require.IsType(t, &SlackPayload{}, pl) + + assert.Equal(t, "[] Pull request review approved: [#12 Fix bug](http://localhost:3000/test/repo/pulls/12) by ", pl.(*SlackPayload).Text) + }) + + t.Run("Repository", func(t *testing.T) { + p := repositoryTestPayload() + + d := new(SlackPayload) + pl, err := d.Repository(p) + require.NoError(t, err) + require.NotNil(t, pl) + require.IsType(t, &SlackPayload{}, pl) + + assert.Equal(t, "[] Repository created by ", pl.(*SlackPayload).Text) + }) + + t.Run("Release", func(t *testing.T) { + p := pullReleaseTestPayload() + + d := new(SlackPayload) + pl, err := d.Release(p) + require.NoError(t, err) + require.NotNil(t, pl) + require.IsType(t, &SlackPayload{}, pl) + + assert.Equal(t, "[] Release created: by ", pl.(*SlackPayload).Text) + }) } -func TestSlackPullRequestPayload(t *testing.T) { - p := pullRequestTestPayload() - s := new(SlackPayload) - s.Username = p.Sender.UserName +func TestSlackJSONPayload(t *testing.T) { + p := pushTestPayload() - pl, err := s.PullRequest(p) + pl, err := new(SlackPayload).Push(p) require.NoError(t, err) require.NotNil(t, pl) + require.IsType(t, &SlackPayload{}, pl) - assert.Equal(t, "[] Pull request opened: by ", pl.(*SlackPayload).Text) + json, err := pl.JSONPayload() + require.NoError(t, err) + assert.NotEmpty(t, json) } diff --git a/services/webhook/telegram.go b/services/webhook/telegram.go index 5b78b46f8..f71352141 100644 --- a/services/webhook/telegram.go +++ b/services/webhook/telegram.go @@ -68,9 +68,7 @@ func (t *TelegramPayload) Create(p *api.CreatePayload) (api.Payloader, error) { title := fmt.Sprintf(`[%s] %s %s created`, p.Repo.HTMLURL, p.Repo.FullName, p.RefType, p.Repo.HTMLURL+"/src/"+refName, refName) - return &TelegramPayload{ - Message: title, - }, nil + return createTelegramPayload(title), nil } // Delete implements PayloadConvertor Delete method @@ -80,18 +78,14 @@ func (t *TelegramPayload) Delete(p *api.DeletePayload) (api.Payloader, error) { title := fmt.Sprintf(`[%s] %s %s deleted`, p.Repo.HTMLURL, p.Repo.FullName, p.RefType, p.Repo.HTMLURL+"/src/"+refName, refName) - return &TelegramPayload{ - Message: title, - }, nil + return createTelegramPayload(title), nil } // Fork implements PayloadConvertor Fork method func (t *TelegramPayload) Fork(p *api.ForkPayload) (api.Payloader, error) { title := fmt.Sprintf(`%s is forked to %s`, p.Forkee.FullName, p.Repo.HTMLURL, p.Repo.FullName) - return &TelegramPayload{ - Message: title, - }, nil + return createTelegramPayload(title), nil } // Push implements PayloadConvertor Push method @@ -129,36 +123,28 @@ func (t *TelegramPayload) Push(p *api.PushPayload) (api.Payloader, error) { } } - return &TelegramPayload{ - Message: title + "\n" + text, - }, nil + return createTelegramPayload(title + "\n" + text), nil } // Issue implements PayloadConvertor Issue method func (t *TelegramPayload) Issue(p *api.IssuePayload) (api.Payloader, error) { text, _, attachmentText, _ := getIssuesPayloadInfo(p, htmlLinkFormatter, true) - return &TelegramPayload{ - Message: text + "\n\n" + attachmentText, - }, nil + return createTelegramPayload(text + "\n\n" + attachmentText), nil } // IssueComment implements PayloadConvertor IssueComment method func (t *TelegramPayload) IssueComment(p *api.IssueCommentPayload) (api.Payloader, error) { text, _, _ := getIssueCommentPayloadInfo(p, htmlLinkFormatter, true) - return &TelegramPayload{ - Message: text + "\n" + p.Comment.Body, - }, nil + return createTelegramPayload(text + "\n" + p.Comment.Body), nil } // PullRequest implements PayloadConvertor PullRequest method func (t *TelegramPayload) PullRequest(p *api.PullRequestPayload) (api.Payloader, error) { text, _, attachmentText, _ := getPullRequestPayloadInfo(p, htmlLinkFormatter, true) - return &TelegramPayload{ - Message: text + "\n" + attachmentText, - }, nil + return createTelegramPayload(text + "\n" + attachmentText), nil } // Review implements PayloadConvertor Review method @@ -173,12 +159,9 @@ func (t *TelegramPayload) Review(p *api.PullRequestPayload, event models.HookEve text = fmt.Sprintf("[%s] Pull request review %s: #%d %s", p.Repository.FullName, action, p.Index, p.PullRequest.Title) attachmentText = p.Review.Content - } - return &TelegramPayload{ - Message: text + "\n" + attachmentText, - }, nil + return createTelegramPayload(text + "\n" + attachmentText), nil } // Repository implements PayloadConvertor Repository method @@ -187,14 +170,10 @@ func (t *TelegramPayload) Repository(p *api.RepositoryPayload) (api.Payloader, e switch p.Action { case api.HookRepoCreated: title = fmt.Sprintf(`[%s] Repository created`, p.Repository.HTMLURL, p.Repository.FullName) - return &TelegramPayload{ - Message: title, - }, nil + return createTelegramPayload(title), nil case api.HookRepoDeleted: title = fmt.Sprintf("[%s] Repository deleted", p.Repository.FullName) - return &TelegramPayload{ - Message: title, - }, nil + return createTelegramPayload(title), nil } return nil, nil } @@ -203,12 +182,16 @@ func (t *TelegramPayload) Repository(p *api.RepositoryPayload) (api.Payloader, e func (t *TelegramPayload) Release(p *api.ReleasePayload) (api.Payloader, error) { text, _ := getReleasePayloadInfo(p, htmlLinkFormatter, true) - return &TelegramPayload{ - Message: text + "\n", - }, nil + return createTelegramPayload(text), nil } // GetTelegramPayload converts a telegram webhook into a TelegramPayload func GetTelegramPayload(p api.Payloader, event models.HookEventType, meta string) (api.Payloader, error) { return convertPayloader(new(TelegramPayload), p, event) } + +func createTelegramPayload(message string) *TelegramPayload { + return &TelegramPayload{ + Message: strings.TrimSpace(message), + } +} diff --git a/services/webhook/telegram_test.go b/services/webhook/telegram_test.go index 0e909343a..037a2481d 100644 --- a/services/webhook/telegram_test.go +++ b/services/webhook/telegram_test.go @@ -7,18 +7,166 @@ package webhook import ( "testing" + "code.gitea.io/gitea/models" api "code.gitea.io/gitea/modules/structs" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) -func TestGetTelegramIssuesPayload(t *testing.T) { - p := issueTestPayload() - p.Action = api.HookIssueClosed +func TestTelegramPayload(t *testing.T) { + t.Run("Create", func(t *testing.T) { + p := createTestPayload() + + d := new(TelegramPayload) + pl, err := d.Create(p) + require.NoError(t, err) + require.NotNil(t, pl) + require.IsType(t, &TelegramPayload{}, pl) + + assert.Equal(t, `[test/repo] branch test created`, pl.(*TelegramPayload).Message) + }) + + t.Run("Delete", func(t *testing.T) { + p := deleteTestPayload() + + d := new(TelegramPayload) + pl, err := d.Delete(p) + require.NoError(t, err) + require.NotNil(t, pl) + require.IsType(t, &TelegramPayload{}, pl) + + assert.Equal(t, `[test/repo] branch test deleted`, pl.(*TelegramPayload).Message) + }) + + t.Run("Fork", func(t *testing.T) { + p := forkTestPayload() + + d := new(TelegramPayload) + pl, err := d.Fork(p) + require.NoError(t, err) + require.NotNil(t, pl) + require.IsType(t, &TelegramPayload{}, pl) + + assert.Equal(t, `test/repo2 is forked to test/repo`, pl.(*TelegramPayload).Message) + }) + + t.Run("Push", func(t *testing.T) { + p := pushTestPayload() + + d := new(TelegramPayload) + pl, err := d.Push(p) + require.NoError(t, err) + require.NotNil(t, pl) + require.IsType(t, &TelegramPayload{}, pl) + + assert.Equal(t, "[test/repo:test] 2 new commits\n[2020558] commit message - user1\n[2020558] commit message - user1", pl.(*TelegramPayload).Message) + }) + + t.Run("Issue", func(t *testing.T) { + p := issueTestPayload() + + d := new(TelegramPayload) + p.Action = api.HookIssueOpened + pl, err := d.Issue(p) + require.NoError(t, err) + require.NotNil(t, pl) + require.IsType(t, &TelegramPayload{}, pl) + + assert.Equal(t, "[test/repo] Issue opened: #2 crash by user1\n\nissue body", pl.(*TelegramPayload).Message) + + p.Action = api.HookIssueClosed + pl, err = d.Issue(p) + require.NoError(t, err) + require.NotNil(t, pl) + require.IsType(t, &TelegramPayload{}, pl) + + assert.Equal(t, `[test/repo] Issue closed: #2 crash by user1`, pl.(*TelegramPayload).Message) + }) + + t.Run("IssueComment", func(t *testing.T) { + p := issueCommentTestPayload() + + d := new(TelegramPayload) + pl, err := d.IssueComment(p) + require.NoError(t, err) + require.NotNil(t, pl) + require.IsType(t, &TelegramPayload{}, pl) - pl, err := new(TelegramPayload).Issue(p) + assert.Equal(t, "[test/repo] New comment on issue #2 crash by user1\nmore info needed", pl.(*TelegramPayload).Message) + }) + + t.Run("PullRequest", func(t *testing.T) { + p := pullRequestTestPayload() + + d := new(TelegramPayload) + pl, err := d.PullRequest(p) + require.NoError(t, err) + require.NotNil(t, pl) + require.IsType(t, &TelegramPayload{}, pl) + + assert.Equal(t, "[test/repo] Pull request opened: #12 Fix bug by user1\nfixes bug #2", pl.(*TelegramPayload).Message) + }) + + t.Run("PullRequestComment", func(t *testing.T) { + p := pullRequestCommentTestPayload() + + d := new(TelegramPayload) + pl, err := d.IssueComment(p) + require.NoError(t, err) + require.NotNil(t, pl) + require.IsType(t, &TelegramPayload{}, pl) + + assert.Equal(t, "[test/repo] New comment on pull request #12 Fix bug by user1\nchanges requested", pl.(*TelegramPayload).Message) + }) + + t.Run("Review", func(t *testing.T) { + p := pullRequestTestPayload() + p.Action = api.HookIssueReviewed + + d := new(TelegramPayload) + pl, err := d.Review(p, models.HookEventPullRequestReviewApproved) + require.NoError(t, err) + require.NotNil(t, pl) + require.IsType(t, &TelegramPayload{}, pl) + + assert.Equal(t, "[test/repo] Pull request review approved: #12 Fix bug\ngood job", pl.(*TelegramPayload).Message) + }) + + t.Run("Repository", func(t *testing.T) { + p := repositoryTestPayload() + + d := new(TelegramPayload) + pl, err := d.Repository(p) + require.NoError(t, err) + require.NotNil(t, pl) + require.IsType(t, &TelegramPayload{}, pl) + + assert.Equal(t, `[test/repo] Repository created`, pl.(*TelegramPayload).Message) + }) + + t.Run("Release", func(t *testing.T) { + p := pullReleaseTestPayload() + + d := new(TelegramPayload) + pl, err := d.Release(p) + require.NoError(t, err) + require.NotNil(t, pl) + require.IsType(t, &TelegramPayload{}, pl) + + assert.Equal(t, `[test/repo] Release created: v1.0 by user1`, pl.(*TelegramPayload).Message) + }) +} + +func TestTelegramJSONPayload(t *testing.T) { + p := pushTestPayload() + + pl, err := new(TelegramPayload).Push(p) require.NoError(t, err) require.NotNil(t, pl) + require.IsType(t, &TelegramPayload{}, pl) - assert.Equal(t, "[test/repo] Issue closed: #2 crash by user1\n\n", pl.(*TelegramPayload).Message) + json, err := pl.JSONPayload() + require.NoError(t, err) + assert.NotEmpty(t, json) }