feat: Update Microsoft Teams webhook to use AdaptiveCard (#11704)

## Checklist

The [contributor guide](https://forgejo.org/docs/next/contributor/) contains information that will be helpful to first time contributors. There also are a few [conditions for merging Pull Requests in Forgejo repositories](https://codeberg.org/forgejo/governance/src/branch/main/PullRequestsAgreement.md). You are also welcome to join the [Forgejo development chatroom](https://matrix.to/#/#forgejo-development:matrix.org).

### Tests for Go changes

(can be removed for JavaScript changes)

- I added test coverage for Go changes...
  - [x] in their respective `*_test.go` for unit tests.
  - [ ] in the `tests/integration` directory if it involves interactions with a live Forgejo server.
- I ran...
  - [ ] `make pr-go` before pushing

### Documentation

- [ ] I created a pull request [to the documentation](https://codeberg.org/forgejo/docs) to explain to Forgejo users how to use this change.
- [x] I did not document these changes and I do not expect someone else to do it.

### Release notes

- [x] This change will be noticed by a Forgejo user or admin (feature, bug fix, performance, etc.). I suggest to include a release note for this change.
- [ ] This change is not visible to a Forgejo user or admin (refactor, dependency upgrade, etc.). I think there is no need to add a release note for this change.

Closes #4517

<!--start release-notes-assistant-->

## Release notes
<!--URL:https://codeberg.org/forgejo/forgejo-->
- Features
  - [PR](https://codeberg.org/forgejo/forgejo/pulls/11704): <!--number 11704 --><!--line 0 --><!--description VXBkYXRlIE1pY3Jvc29mdCBUZWFtcyB3ZWJob29rIHRvIHVzZSBBZGFwdGl2ZUNhcmQ=-->Update Microsoft Teams webhook to use AdaptiveCard<!--description-->
<!--end release-notes-assistant-->

Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/11704
Reviewed-by: oliverpool <oliverpool@noreply.codeberg.org>
Reviewed-by: Mathieu Fenniak <mfenniak@noreply.codeberg.org>
This commit is contained in:
nightfurysl2001
2026-05-26 16:10:55 +02:00
committed by Mathieu Fenniak
parent 0c418d2c0e
commit b58d8e20fd
17 changed files with 1408 additions and 602 deletions
+14 -7
View File
@@ -62,6 +62,13 @@ type (
}
)
var dingtalkPayloadFormatter = webhookPayloadFormatter{
linkFormatter: noneLinkFormatter,
nameFormatter: noneNameFormatter,
withSender: true,
withRepoName: true,
}
// Create implements PayloadConvertor Create method
func (dc dingtalkConvertor) Create(p *api.CreatePayload) (DingtalkPayload, error) {
// created tag/branch
@@ -130,14 +137,14 @@ func (dc dingtalkConvertor) Push(p *api.PushPayload) (DingtalkPayload, error) {
// Issue implements PayloadConvertor Issue method
func (dc dingtalkConvertor) Issue(p *api.IssuePayload) (DingtalkPayload, error) {
text, issueTitle, attachmentText, _ := getIssuesPayloadInfo(p, noneLinkFormatter, noneNameFormatter, true)
text, issueTitle, attachmentText, _ := dingtalkPayloadFormatter.getIssuesPayloadInfo(p)
return createDingtalkPayload(issueTitle, text+"\r\n\r\n"+attachmentText, "view issue", p.Issue.HTMLURL), nil
}
// Wiki implements PayloadConvertor Wiki method
func (dc dingtalkConvertor) Wiki(p *api.WikiPayload) (DingtalkPayload, error) {
text, _, _ := getWikiPayloadInfo(p, noneLinkFormatter, noneNameFormatter, true)
text, _, _ := dingtalkPayloadFormatter.getWikiPayloadInfo(p, true)
url := p.Repository.HTMLURL + "/wiki/" + url.PathEscape(p.Page)
return createDingtalkPayload(text, text, "view wiki", url), nil
@@ -145,14 +152,14 @@ func (dc dingtalkConvertor) Wiki(p *api.WikiPayload) (DingtalkPayload, error) {
// IssueComment implements PayloadConvertor IssueComment method
func (dc dingtalkConvertor) IssueComment(p *api.IssueCommentPayload) (DingtalkPayload, error) {
text, issueTitle, _ := getIssueCommentPayloadInfo(p, noneLinkFormatter, noneNameFormatter, true)
text, issueTitle, _ := dingtalkPayloadFormatter.getIssueCommentPayloadInfo(p)
return createDingtalkPayload(issueTitle, text+"\r\n\r\n"+p.Comment.Body, "view issue comment", p.Comment.HTMLURL), nil
}
// PullRequest implements PayloadConvertor PullRequest method
func (dc dingtalkConvertor) PullRequest(p *api.PullRequestPayload) (DingtalkPayload, error) {
text, issueTitle, attachmentText, _ := getPullRequestPayloadInfo(p, noneLinkFormatter, noneNameFormatter, true)
text, issueTitle, attachmentText, _ := dingtalkPayloadFormatter.getPullRequestPayloadInfo(p)
return createDingtalkPayload(issueTitle, text+"\r\n\r\n"+attachmentText, "view pull request", p.PullRequest.HTMLURL), nil
}
@@ -196,19 +203,19 @@ func (dc dingtalkConvertor) Repository(p *api.RepositoryPayload) (DingtalkPayloa
// Release implements PayloadConvertor Release method
func (dc dingtalkConvertor) Release(p *api.ReleasePayload) (DingtalkPayload, error) {
text, _ := getReleasePayloadInfo(p, noneLinkFormatter, noneNameFormatter, true)
text, _ := dingtalkPayloadFormatter.getReleasePayloadInfo(p)
return createDingtalkPayload(text, text, "view release", p.Release.HTMLURL), nil
}
func (dc dingtalkConvertor) Package(p *api.PackagePayload) (DingtalkPayload, error) {
text, _ := getPackagePayloadInfo(p, noneLinkFormatter, noneNameFormatter, true)
text, _ := dingtalkPayloadFormatter.getPackagePayloadInfo(p)
return createDingtalkPayload(text, text, "view package", p.Package.HTMLURL), nil
}
func (dc dingtalkConvertor) Action(p *api.ActionPayload) (DingtalkPayload, error) {
text, _ := getActionPayloadInfo(p, noneLinkFormatter)
text, _ := dingtalkPayloadFormatter.getActionPayloadInfo(p)
return createDingtalkPayload(text, text, "view action", p.Run.HTMLURL), nil
}
+6 -6
View File
@@ -178,8 +178,8 @@ func TestDingTalkPayload(t *testing.T) {
pl, err := dc.Wiki(p)
require.NoError(t, err)
assert.Equal(t, "[test/repo] New wiki page 'index' (Wiki change comment) by user1", pl.ActionCard.Text)
assert.Equal(t, "[test/repo] New wiki page 'index' (Wiki change comment) by user1", pl.ActionCard.Title)
assert.Equal(t, "[test/repo] New wiki page \"index\" (Wiki change comment) by user1", pl.ActionCard.Text)
assert.Equal(t, "[test/repo] New wiki page \"index\" (Wiki change comment) by user1", pl.ActionCard.Title)
assert.Equal(t, "view wiki", pl.ActionCard.SingleTitle)
assert.Equal(t, "http://localhost:3000/test/repo/wiki/index", parseRealSingleURL(pl.ActionCard.SingleURL))
@@ -187,8 +187,8 @@ func TestDingTalkPayload(t *testing.T) {
pl, err = dc.Wiki(p)
require.NoError(t, err)
assert.Equal(t, "[test/repo] Wiki page 'index' edited (Wiki change comment) by user1", pl.ActionCard.Text)
assert.Equal(t, "[test/repo] Wiki page 'index' edited (Wiki change comment) by user1", pl.ActionCard.Title)
assert.Equal(t, "[test/repo] Wiki page \"index\" edited (Wiki change comment) by user1", pl.ActionCard.Text)
assert.Equal(t, "[test/repo] Wiki page \"index\" edited (Wiki change comment) by user1", pl.ActionCard.Title)
assert.Equal(t, "view wiki", pl.ActionCard.SingleTitle)
assert.Equal(t, "http://localhost:3000/test/repo/wiki/index", parseRealSingleURL(pl.ActionCard.SingleURL))
@@ -196,8 +196,8 @@ func TestDingTalkPayload(t *testing.T) {
pl, err = dc.Wiki(p)
require.NoError(t, err)
assert.Equal(t, "[test/repo] Wiki page 'index' deleted by user1", pl.ActionCard.Text)
assert.Equal(t, "[test/repo] Wiki page 'index' deleted by user1", pl.ActionCard.Title)
assert.Equal(t, "[test/repo] Wiki page \"index\" deleted by user1", pl.ActionCard.Text)
assert.Equal(t, "[test/repo] Wiki page \"index\" deleted by user1", pl.ActionCard.Title)
assert.Equal(t, "view wiki", pl.ActionCard.SingleTitle)
assert.Equal(t, "http://localhost:3000/test/repo/wiki/index", parseRealSingleURL(pl.ActionCard.SingleURL))
})
+13 -7
View File
@@ -45,6 +45,12 @@ type discordForm struct {
var _ binding.Validator = &discordForm{}
var discordPayloadFormatter = webhookPayloadFormatter{
linkFormatter: noneLinkFormatter,
nameFormatter: noneNameFormatter,
withRepoName: true,
}
// Validate implements binding.Validator.
func (d *discordForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
ctx := app_context.GetWebContext(req)
@@ -235,21 +241,21 @@ func (d discordConvertor) Push(p *api.PushPayload) (DiscordPayload, error) {
// Issue implements PayloadConvertor Issue method
func (d discordConvertor) Issue(p *api.IssuePayload) (DiscordPayload, error) {
title, _, text, color := getIssuesPayloadInfo(p, noneLinkFormatter, noneNameFormatter, false)
title, _, text, color := discordPayloadFormatter.getIssuesPayloadInfo(p)
return d.createPayload(p.Sender, title, text, p.Issue.HTMLURL, color), nil
}
// IssueComment implements PayloadConvertor IssueComment method
func (d discordConvertor) IssueComment(p *api.IssueCommentPayload) (DiscordPayload, error) {
title, _, color := getIssueCommentPayloadInfo(p, noneLinkFormatter, noneNameFormatter, false)
title, _, color := discordPayloadFormatter.getIssueCommentPayloadInfo(p)
return d.createPayload(p.Sender, title, p.Comment.Body, p.Comment.HTMLURL, color), nil
}
// PullRequest implements PayloadConvertor PullRequest method
func (d discordConvertor) PullRequest(p *api.PullRequestPayload) (DiscordPayload, error) {
title, _, text, color := getPullRequestPayloadInfo(p, noneLinkFormatter, noneNameFormatter, false)
title, _, text, color := discordPayloadFormatter.getPullRequestPayloadInfo(p)
return d.createPayload(p.Sender, title, text, p.PullRequest.HTMLURL, color), nil
}
@@ -301,7 +307,7 @@ func (d discordConvertor) Repository(p *api.RepositoryPayload) (DiscordPayload,
// Wiki implements PayloadConvertor Wiki method
func (d discordConvertor) Wiki(p *api.WikiPayload) (DiscordPayload, error) {
text, color, _ := getWikiPayloadInfo(p, noneLinkFormatter, noneNameFormatter, false)
text, color, _ := discordPayloadFormatter.getWikiPayloadInfo(p, true)
htmlLink := p.Repository.HTMLURL + "/wiki/" + url.PathEscape(p.Page)
var description string
@@ -314,19 +320,19 @@ func (d discordConvertor) Wiki(p *api.WikiPayload) (DiscordPayload, error) {
// Release implements PayloadConvertor Release method
func (d discordConvertor) Release(p *api.ReleasePayload) (DiscordPayload, error) {
text, color := getReleasePayloadInfo(p, noneLinkFormatter, noneNameFormatter, false)
text, color := discordPayloadFormatter.getReleasePayloadInfo(p)
return d.createPayload(p.Sender, text, p.Release.Note, p.Release.HTMLURL, color), nil
}
func (d discordConvertor) Package(p *api.PackagePayload) (DiscordPayload, error) {
text, color := getPackagePayloadInfo(p, noneLinkFormatter, noneNameFormatter, false)
text, color := discordPayloadFormatter.getPackagePayloadInfo(p)
return d.createPayload(p.Sender, text, "", p.Package.HTMLURL, color), nil
}
func (d discordConvertor) Action(p *api.ActionPayload) (DiscordPayload, error) {
text, color := getActionPayloadInfo(p, noneLinkFormatter)
text, color := discordPayloadFormatter.getActionPayloadInfo(p)
return d.createPayload(p.Run.TriggerUser, text, "", p.Run.HTMLURL, color), nil
}
+3 -3
View File
@@ -279,7 +279,7 @@ func TestDiscordPayload(t *testing.T) {
require.NoError(t, err)
assert.Len(t, pl.Embeds, 1)
assert.Equal(t, "[test/repo] New wiki page 'index' (Wiki change comment)", pl.Embeds[0].Title)
assert.Equal(t, "[test/repo] New wiki page \"index\" (Wiki change comment)", pl.Embeds[0].Title)
assert.Equal(t, "Wiki change comment", pl.Embeds[0].Description)
assert.Equal(t, "http://localhost:3000/test/repo/wiki/index", pl.Embeds[0].URL)
assert.Equal(t, p.Sender.UserName, pl.Embeds[0].Author.Name)
@@ -291,7 +291,7 @@ func TestDiscordPayload(t *testing.T) {
require.NoError(t, err)
assert.Len(t, pl.Embeds, 1)
assert.Equal(t, "[test/repo] Wiki page 'index' edited (Wiki change comment)", pl.Embeds[0].Title)
assert.Equal(t, "[test/repo] Wiki page \"index\" edited (Wiki change comment)", pl.Embeds[0].Title)
assert.Equal(t, "Wiki change comment", pl.Embeds[0].Description)
assert.Equal(t, "http://localhost:3000/test/repo/wiki/index", pl.Embeds[0].URL)
assert.Equal(t, p.Sender.UserName, pl.Embeds[0].Author.Name)
@@ -303,7 +303,7 @@ func TestDiscordPayload(t *testing.T) {
require.NoError(t, err)
assert.Len(t, pl.Embeds, 1)
assert.Equal(t, "[test/repo] Wiki page 'index' deleted", pl.Embeds[0].Title)
assert.Equal(t, "[test/repo] Wiki page \"index\" deleted", pl.Embeds[0].Title)
assert.Empty(t, pl.Embeds[0].Description)
assert.Equal(t, "http://localhost:3000/test/repo/wiki/index", pl.Embeds[0].URL)
assert.Equal(t, p.Sender.UserName, pl.Embeds[0].Author.Name)
+11 -4
View File
@@ -63,6 +63,13 @@ func newFeishuTextPayload(text string) FeishuPayload {
}
}
var feishuPayloadFormatter = webhookPayloadFormatter{
linkFormatter: noneLinkFormatter,
nameFormatter: noneNameFormatter,
withSender: true,
withRepoName: true,
}
// Create implements PayloadConvertor Create method
func (fc feishuConvertor) Create(p *api.CreatePayload) (FeishuPayload, error) {
// created tag/branch
@@ -174,26 +181,26 @@ func (fc feishuConvertor) Repository(p *api.RepositoryPayload) (FeishuPayload, e
// Wiki implements PayloadConvertor Wiki method
func (fc feishuConvertor) Wiki(p *api.WikiPayload) (FeishuPayload, error) {
text, _, _ := getWikiPayloadInfo(p, noneLinkFormatter, noneNameFormatter, true)
text, _, _ := feishuPayloadFormatter.getWikiPayloadInfo(p, true)
return newFeishuTextPayload(text), nil
}
// Release implements PayloadConvertor Release method
func (fc feishuConvertor) Release(p *api.ReleasePayload) (FeishuPayload, error) {
text, _ := getReleasePayloadInfo(p, noneLinkFormatter, noneNameFormatter, true)
text, _ := feishuPayloadFormatter.getReleasePayloadInfo(p)
return newFeishuTextPayload(text), nil
}
func (fc feishuConvertor) Package(p *api.PackagePayload) (FeishuPayload, error) {
text, _ := getPackagePayloadInfo(p, noneLinkFormatter, noneNameFormatter, true)
text, _ := feishuPayloadFormatter.getPackagePayloadInfo(p)
return newFeishuTextPayload(text), nil
}
func (fc feishuConvertor) Action(p *api.ActionPayload) (FeishuPayload, error) {
text, _ := getActionPayloadInfo(p, noneLinkFormatter)
text, _ := feishuPayloadFormatter.getActionPayloadInfo(p)
return newFeishuTextPayload(text), nil
}
+3 -3
View File
@@ -131,19 +131,19 @@ func TestFeishuPayload(t *testing.T) {
pl, err := fc.Wiki(p)
require.NoError(t, err)
assert.Equal(t, "[test/repo] New wiki page 'index' (Wiki change comment) by user1", pl.Content.Text)
assert.Equal(t, "[test/repo] New wiki page \"index\" (Wiki change comment) by user1", pl.Content.Text)
p.Action = api.HookWikiEdited
pl, err = fc.Wiki(p)
require.NoError(t, err)
assert.Equal(t, "[test/repo] Wiki page 'index' edited (Wiki change comment) by user1", pl.Content.Text)
assert.Equal(t, "[test/repo] Wiki page \"index\" edited (Wiki change comment) by user1", pl.Content.Text)
p.Action = api.HookWikiDeleted
pl, err = fc.Wiki(p)
require.NoError(t, err)
assert.Equal(t, "[test/repo] Wiki page 'index' deleted by user1", pl.Content.Text)
assert.Equal(t, "[test/repo] Wiki page \"index\" deleted by user1", pl.Content.Text)
})
t.Run("Release", func(t *testing.T) {
+99 -83
View File
@@ -17,8 +17,8 @@ import (
)
type (
linkFormatter = func(string, string) string
nameFormatter = func(string) string
linkFormatter = func(url, text string) string
nameFormatter = func(name string) string
)
// noneLinkFormatter does not create a link but just returns the text
@@ -101,48 +101,56 @@ func getIssuesCommentInfo(p *api.IssueCommentPayload, nameFormatter nameFormatte
return title, link, by, operator
}
func getIssuesPayloadInfo(p *api.IssuePayload, linkFormatter linkFormatter, nameFormatter nameFormatter, withSender bool) (string, string, string, int) {
issueTitle := fmt.Sprintf("#%d %s", p.Index, p.Issue.Title)
titleLink := linkFormatter(fmt.Sprintf("%s/issues/%d", p.Repository.HTMLURL, p.Index), issueTitle)
var text string
color := yellowColor
type webhookPayloadFormatter struct {
linkFormatter linkFormatter
nameFormatter nameFormatter
withSender, withRepoName bool
}
func (wpf webhookPayloadFormatter) getIssuesPayloadInfo(p *api.IssuePayload) (text, issueTitle, attachmentText string, color int) {
issueTitle = fmt.Sprintf("#%d %s", p.Index, p.Issue.Title)
titleLink := wpf.linkFormatter(fmt.Sprintf("%s/issues/%d", p.Repository.HTMLURL, p.Index), issueTitle)
color = yellowColor
switch p.Action {
case api.HookIssueOpened:
text = fmt.Sprintf("[%s] Issue opened: %s", p.Repository.FullName, titleLink)
text = fmt.Sprintf("Issue opened: %s", titleLink)
color = orangeColor
case api.HookIssueClosed:
text = fmt.Sprintf("[%s] Issue closed: %s", p.Repository.FullName, titleLink)
text = fmt.Sprintf("Issue closed: %s", titleLink)
color = redColor
case api.HookIssueReOpened:
text = fmt.Sprintf("[%s] Issue re-opened: %s", p.Repository.FullName, titleLink)
text = fmt.Sprintf("Issue re-opened: %s", titleLink)
case api.HookIssueEdited:
text = fmt.Sprintf("[%s] Issue edited: %s", p.Repository.FullName, titleLink)
text = fmt.Sprintf("Issue edited: %s", titleLink)
case api.HookIssueAssigned:
list := make([]string, len(p.Issue.Assignees))
for i, user := range p.Issue.Assignees {
list[i] = linkFormatter(setting.AppURL+url.PathEscape(user.UserName), user.UserName)
list[i] = wpf.linkFormatter(setting.AppURL+url.PathEscape(user.UserName), user.UserName)
}
text = fmt.Sprintf("[%s] Issue assigned to %s: %s", p.Repository.FullName, strings.Join(list, ", "), titleLink)
text = fmt.Sprintf("Issue assigned to %s: %s", strings.Join(list, ", "), titleLink)
color = greenColor
case api.HookIssueUnassigned:
text = fmt.Sprintf("[%s] Issue unassigned: %s", p.Repository.FullName, titleLink)
text = fmt.Sprintf("Issue unassigned: %s", titleLink)
case api.HookIssueLabelUpdated:
text = fmt.Sprintf("[%s] Issue labels updated: %s", p.Repository.FullName, titleLink)
text = fmt.Sprintf("Issue labels updated: %s", titleLink)
case api.HookIssueLabelCleared:
text = fmt.Sprintf("[%s] Issue labels cleared: %s", p.Repository.FullName, titleLink)
text = fmt.Sprintf("Issue labels cleared: %s", titleLink)
case api.HookIssueSynchronized:
text = fmt.Sprintf("[%s] Issue synchronized: %s", p.Repository.FullName, titleLink)
text = fmt.Sprintf("Issue synchronized: %s", titleLink)
case api.HookIssueMilestoned:
text = fmt.Sprintf("[%s] Issue milestoned to %s: %s", p.Repository.FullName, p.Issue.Milestone.Title, titleLink)
text = fmt.Sprintf("Issue milestoned to %s: %s", p.Issue.Milestone.Title, titleLink)
case api.HookIssueDemilestoned:
text = fmt.Sprintf("[%s] Issue milestone cleared: %s", p.Repository.FullName, titleLink)
}
if withSender {
text += fmt.Sprintf(" by %s", nameFormatter(p.Sender.UserName))
text = fmt.Sprintf("Issue milestone cleared: %s", titleLink)
}
if wpf.withRepoName {
text = fmt.Sprintf("[%s] %s", p.Repository.FullName, text)
}
if wpf.withSender {
text += fmt.Sprintf(" by %s", wpf.nameFormatter(p.Sender.UserName))
}
var attachmentText string
if p.Action == api.HookIssueOpened || p.Action == api.HookIssueEdited {
attachmentText = p.Issue.Body
}
@@ -150,152 +158,160 @@ func getIssuesPayloadInfo(p *api.IssuePayload, linkFormatter linkFormatter, name
return text, issueTitle, attachmentText, color
}
func getPullRequestPayloadInfo(p *api.PullRequestPayload, linkFormatter linkFormatter, nameFormatter nameFormatter, withSender bool) (string, string, string, int) {
issueTitle := fmt.Sprintf("#%d %s", p.Index, p.PullRequest.Title)
titleLink := linkFormatter(p.PullRequest.URL, issueTitle)
var text string
var attachmentText string
color := yellowColor
func (wpf webhookPayloadFormatter) getPullRequestPayloadInfo(p *api.PullRequestPayload) (text, issueTitle, attachmentText string, color int) {
issueTitle = fmt.Sprintf("#%d %s", p.Index, p.PullRequest.Title)
titleLink := wpf.linkFormatter(p.PullRequest.URL, issueTitle)
color = yellowColor
switch p.Action {
case api.HookIssueOpened:
text = fmt.Sprintf("[%s] Pull request opened: %s", p.Repository.FullName, titleLink)
text = fmt.Sprintf("Pull request opened: %s", titleLink)
attachmentText = p.PullRequest.Body
color = greenColor
case api.HookIssueClosed:
if p.PullRequest.HasMerged {
text = fmt.Sprintf("[%s] Pull request merged: %s", p.Repository.FullName, titleLink)
text = fmt.Sprintf("Pull request merged: %s", titleLink)
color = purpleColor
} else {
text = fmt.Sprintf("[%s] Pull request closed: %s", p.Repository.FullName, titleLink)
text = fmt.Sprintf("Pull request closed: %s", titleLink)
color = redColor
}
case api.HookIssueReOpened:
text = fmt.Sprintf("[%s] Pull request re-opened: %s", p.Repository.FullName, titleLink)
text = fmt.Sprintf("Pull request re-opened: %s", titleLink)
case api.HookIssueEdited:
text = fmt.Sprintf("[%s] Pull request edited: %s", p.Repository.FullName, titleLink)
text = fmt.Sprintf("Pull request edited: %s", titleLink)
attachmentText = p.PullRequest.Body
case api.HookIssueAssigned:
list := make([]string, len(p.PullRequest.Assignees))
for i, user := range p.PullRequest.Assignees {
list[i] = linkFormatter(setting.AppURL+user.UserName, user.UserName)
list[i] = wpf.linkFormatter(setting.AppURL+user.UserName, user.UserName)
}
text = fmt.Sprintf("[%s] Pull request assigned to %s: %s", p.Repository.FullName,
strings.Join(list, ", "), titleLink)
text = fmt.Sprintf("Pull request assigned to %s: %s", strings.Join(list, ", "), titleLink)
color = greenColor
case api.HookIssueUnassigned:
text = fmt.Sprintf("[%s] Pull request unassigned: %s", p.Repository.FullName, titleLink)
text = fmt.Sprintf("Pull request unassigned: %s", titleLink)
case api.HookIssueLabelUpdated:
text = fmt.Sprintf("[%s] Pull request labels updated: %s", p.Repository.FullName, titleLink)
text = fmt.Sprintf("Pull request labels updated: %s", titleLink)
case api.HookIssueLabelCleared:
text = fmt.Sprintf("[%s] Pull request labels cleared: %s", p.Repository.FullName, titleLink)
text = fmt.Sprintf("Pull request labels cleared: %s", titleLink)
case api.HookIssueSynchronized:
text = fmt.Sprintf("[%s] Pull request synchronized: %s", p.Repository.FullName, titleLink)
text = fmt.Sprintf("Pull request synchronized: %s", titleLink)
case api.HookIssueMilestoned:
text = fmt.Sprintf("[%s] Pull request milestoned to %s: %s", p.Repository.FullName, p.PullRequest.Milestone.Title, titleLink)
text = fmt.Sprintf("Pull request milestoned to %s: %s", p.PullRequest.Milestone.Title, titleLink)
case api.HookIssueDemilestoned:
text = fmt.Sprintf("[%s] Pull request milestone cleared: %s", p.Repository.FullName, titleLink)
text = fmt.Sprintf("Pull request milestone cleared: %s", titleLink)
case api.HookIssueReviewed:
text = fmt.Sprintf("[%s] Pull request reviewed: %s", p.Repository.FullName, titleLink)
text = fmt.Sprintf("Pull request reviewed: %s", titleLink)
attachmentText = p.Review.Content
case api.HookIssueReviewRequested:
text = fmt.Sprintf("[%s] Pull request review requested: %s", p.Repository.FullName, titleLink)
text = fmt.Sprintf("Pull request review requested: %s", titleLink)
case api.HookIssueReviewRequestRemoved:
text = fmt.Sprintf("[%s] Pull request review request removed: %s", p.Repository.FullName, titleLink)
text = fmt.Sprintf("Pull request review request removed: %s", titleLink)
}
if withSender {
text += fmt.Sprintf(" by %s", nameFormatter(p.Sender.UserName))
if wpf.withRepoName {
text = fmt.Sprintf("[%s] %s", p.Repository.FullName, text)
}
if wpf.withSender {
text += fmt.Sprintf(" by %s", wpf.nameFormatter(p.Sender.UserName))
}
return text, issueTitle, attachmentText, color
}
func getReleasePayloadInfo(p *api.ReleasePayload, linkFormatter linkFormatter, nameFormatter nameFormatter, withSender bool) (text string, color int) {
refLink := linkFormatter(p.Repository.HTMLURL+"/releases/tag/"+util.PathEscapeSegments(p.Release.TagName), p.Release.TagName)
func (wpf webhookPayloadFormatter) getReleasePayloadInfo(p *api.ReleasePayload) (text string, color int) {
refLink := wpf.linkFormatter(p.Repository.HTMLURL+"/releases/tag/"+util.PathEscapeSegments(p.Release.TagName), p.Release.TagName)
switch p.Action {
case api.HookReleasePublished:
text = fmt.Sprintf("[%s] Release created: %s", p.Repository.FullName, refLink)
text = fmt.Sprintf("Release created: %s", refLink)
color = greenColor
case api.HookReleaseUpdated:
text = fmt.Sprintf("[%s] Release updated: %s", p.Repository.FullName, refLink)
text = fmt.Sprintf("Release updated: %s", refLink)
color = yellowColor
case api.HookReleaseDeleted:
text = fmt.Sprintf("[%s] Release deleted: %s", p.Repository.FullName, refLink)
text = fmt.Sprintf("Release deleted: %s", refLink)
color = redColor
}
if withSender {
text += fmt.Sprintf(" by %s", nameFormatter(p.Sender.UserName))
if wpf.withRepoName {
text = fmt.Sprintf("[%s] %s", p.Repository.FullName, text)
}
if wpf.withSender {
text += fmt.Sprintf(" by %s", wpf.nameFormatter(p.Sender.UserName))
}
return text, color
}
func getWikiPayloadInfo(p *api.WikiPayload, linkFormatter linkFormatter, nameFormatter nameFormatter, withSender bool) (string, int, string) {
pageLink := linkFormatter(p.Repository.HTMLURL+"/wiki/"+url.PathEscape(p.Page), p.Page)
func (wpf webhookPayloadFormatter) getWikiPayloadInfo(p *api.WikiPayload, withCommitMessage bool) (text string, color int, pageLink string) {
pageLink = wpf.linkFormatter(p.Repository.HTMLURL+"/wiki/"+url.PathEscape(p.Page), p.Page)
var text string
color := greenColor
color = greenColor
switch p.Action {
case api.HookWikiCreated:
text = fmt.Sprintf("[%s] New wiki page '%s'", p.Repository.FullName, pageLink)
text = fmt.Sprintf("New wiki page \"%s\"", pageLink)
case api.HookWikiEdited:
text = fmt.Sprintf("[%s] Wiki page '%s' edited", p.Repository.FullName, pageLink)
text = fmt.Sprintf("Wiki page \"%s\" edited", pageLink)
color = yellowColor
case api.HookWikiDeleted:
text = fmt.Sprintf("[%s] Wiki page '%s' deleted", p.Repository.FullName, pageLink)
text = fmt.Sprintf("Wiki page \"%s\" deleted", pageLink)
color = redColor
}
if p.Action != api.HookWikiDeleted && p.Comment != "" {
if p.Action != api.HookWikiDeleted && p.Comment != "" && withCommitMessage {
text += fmt.Sprintf(" (%s)", p.Comment)
}
if withSender {
text += fmt.Sprintf(" by %s", nameFormatter(p.Sender.UserName))
if wpf.withRepoName {
text = fmt.Sprintf("[%s] %s", p.Repository.FullName, text)
}
if wpf.withSender {
text += fmt.Sprintf(" by %s", wpf.nameFormatter(p.Sender.UserName))
}
return text, color, pageLink
}
func getIssueCommentPayloadInfo(p *api.IssueCommentPayload, linkFormatter linkFormatter, nameFormatter nameFormatter, withSender bool) (string, string, int) {
issueTitle := fmt.Sprintf("#%d %s", p.Issue.Index, p.Issue.Title)
func (wpf webhookPayloadFormatter) getIssueCommentPayloadInfo(p *api.IssueCommentPayload) (text, issueTitle string, color int) {
issueTitle = fmt.Sprintf("#%d %s", p.Issue.Index, p.Issue.Title)
var text, typ, titleLink string
color := yellowColor
var typ, titleLink string
color = yellowColor
if p.IsPull {
typ = "pull request"
titleLink = linkFormatter(p.Comment.PRURL, issueTitle)
titleLink = wpf.linkFormatter(p.Comment.PRURL, issueTitle)
} else {
typ = "issue"
titleLink = linkFormatter(p.Comment.IssueURL, issueTitle)
titleLink = wpf.linkFormatter(p.Comment.IssueURL, issueTitle)
}
switch p.Action {
case api.HookIssueCommentCreated:
text = fmt.Sprintf("[%s] New comment on %s %s", p.Repository.FullName, typ, titleLink)
text = fmt.Sprintf("New comment on %s %s", typ, titleLink)
if p.IsPull {
color = greenColorLight
} else {
color = orangeColorLight
}
case api.HookIssueCommentEdited:
text = fmt.Sprintf("[%s] Comment edited on %s %s", p.Repository.FullName, typ, titleLink)
text = fmt.Sprintf("Comment edited on %s %s", typ, titleLink)
case api.HookIssueCommentDeleted:
text = fmt.Sprintf("[%s] Comment deleted on %s %s", p.Repository.FullName, typ, titleLink)
text = fmt.Sprintf("Comment deleted on %s %s", typ, titleLink)
color = redColor
}
if withSender {
text += fmt.Sprintf(" by %s", nameFormatter(p.Sender.UserName))
if wpf.withRepoName {
text = fmt.Sprintf("[%s] %s", p.Repository.FullName, text)
}
if wpf.withSender {
text += fmt.Sprintf(" by %s", wpf.nameFormatter(p.Sender.UserName))
}
return text, issueTitle, color
}
func getPackagePayloadInfo(p *api.PackagePayload, linkFormatter linkFormatter, nameFormatter nameFormatter, withSender bool) (text string, color int) {
refLink := linkFormatter(p.Package.HTMLURL, p.Package.Name+":"+p.Package.Version)
func (wpf webhookPayloadFormatter) getPackagePayloadInfo(p *api.PackagePayload) (text string, color int) {
refLink := wpf.linkFormatter(p.Package.HTMLURL, p.Package.Name+":"+p.Package.Version)
switch p.Action {
case api.HookPackageCreated:
@@ -305,16 +321,16 @@ func getPackagePayloadInfo(p *api.PackagePayload, linkFormatter linkFormatter, n
text = fmt.Sprintf("Package deleted: %s", refLink)
color = redColor
}
if withSender {
text += fmt.Sprintf(" by %s", nameFormatter(p.Sender.UserName))
if wpf.withSender {
text += fmt.Sprintf(" by %s", wpf.nameFormatter(p.Sender.UserName))
}
return text, color
}
func getActionPayloadInfo(p *api.ActionPayload, linkFormatter linkFormatter) (text string, color int) {
runLink := linkFormatter(p.Run.HTMLURL, p.Run.Title)
repoLink := linkFormatter(p.Run.Repo.HTMLURL, p.Run.Repo.FullName)
func (wpf webhookPayloadFormatter) getActionPayloadInfo(p *api.ActionPayload) (text string, color int) {
runLink := wpf.linkFormatter(p.Run.HTMLURL, p.Run.Title)
repoLink := wpf.linkFormatter(p.Run.Repo.HTMLURL, p.Run.Repo.FullName)
switch p.Action {
case api.HookActionFailure:
+91 -9
View File
@@ -4,6 +4,7 @@
package webhook
import (
"fmt"
"strings"
"testing"
@@ -12,6 +13,13 @@ import (
"github.com/stretchr/testify/assert"
)
var generalTestPayloadFormatter = webhookPayloadFormatter{
linkFormatter: noneLinkFormatter,
nameFormatter: noneNameFormatter,
withSender: true,
withRepoName: true,
}
func createTestPayload() *api.CreatePayload {
return &api.CreatePayload{
Sha: "2020558fe2e34debb818a514715839cabd25e777",
@@ -476,7 +484,7 @@ func TestGetIssuesPayloadInfo(t *testing.T) {
for i, c := range cases {
p.Action = c.action
text, issueTitle, attachmentText, color := getIssuesPayloadInfo(p, noneLinkFormatter, noneNameFormatter, true)
text, issueTitle, attachmentText, color := generalTestPayloadFormatter.getIssuesPayloadInfo(p)
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)
@@ -575,7 +583,7 @@ func TestGetPullRequestPayloadInfo(t *testing.T) {
for i, c := range cases {
p.Action = c.action
text, issueTitle, attachmentText, color := getPullRequestPayloadInfo(p, noneLinkFormatter, noneNameFormatter, true)
text, issueTitle, attachmentText, color := generalTestPayloadFormatter.getPullRequestPayloadInfo(p)
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)
@@ -594,19 +602,19 @@ func TestGetWikiPayloadInfo(t *testing.T) {
}{
{
api.HookWikiCreated,
"[test/repo] New wiki page 'index' (Wiki change comment) by user1",
"[test/repo] New wiki page \"index\" (Wiki change comment) by user1",
greenColor,
"index",
},
{
api.HookWikiEdited,
"[test/repo] Wiki page 'index' edited (Wiki change comment) by user1",
"[test/repo] Wiki page \"index\" edited (Wiki change comment) by user1",
yellowColor,
"index",
},
{
api.HookWikiDeleted,
"[test/repo] Wiki page 'index' deleted by user1",
"[test/repo] Wiki page \"index\" deleted by user1",
redColor,
"index",
},
@@ -614,7 +622,7 @@ func TestGetWikiPayloadInfo(t *testing.T) {
for i, c := range cases {
p.Action = c.action
text, color, link := getWikiPayloadInfo(p, noneLinkFormatter, noneNameFormatter, true)
text, color, link := generalTestPayloadFormatter.getWikiPayloadInfo(p, true)
assert.Equal(t, c.text, text, "case %d", i)
assert.Equal(t, c.color, color, "case %d", i)
assert.Equal(t, c.link, link, "case %d", i)
@@ -648,7 +656,7 @@ func TestGetReleasePayloadInfo(t *testing.T) {
for i, c := range cases {
p.Action = c.action
text, color := getReleasePayloadInfo(p, noneLinkFormatter, noneNameFormatter, true)
text, color := generalTestPayloadFormatter.getReleasePayloadInfo(p)
assert.Equal(t, c.text, text, "case %d", i)
assert.Equal(t, c.color, color, "case %d", i)
}
@@ -685,7 +693,7 @@ func TestGetIssueCommentPayloadInfo(t *testing.T) {
for i, c := range cases {
p.Action = c.action
text, issueTitle, color := getIssueCommentPayloadInfo(p, noneLinkFormatter, noneNameFormatter, true)
text, issueTitle, color := generalTestPayloadFormatter.getIssueCommentPayloadInfo(p)
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)
@@ -719,8 +727,82 @@ func TestGetActionPayloadInfo(t *testing.T) {
for i, c := range cases {
p.Action = c.action
text, color := getActionPayloadInfo(p, noneLinkFormatter)
text, color := generalTestPayloadFormatter.getActionPayloadInfo(p)
assert.Equal(t, c.text, text, "case %d", i)
assert.Equal(t, c.color, color, "case %d", i)
}
}
func TestWebhookPayloadTextFormatter(t *testing.T) {
p := issueTestPayload()
p.Action = api.HookIssueOpened
formatterCases := []struct {
formatter webhookPayloadFormatter
expectedText string
}{
{
formatter: webhookPayloadFormatter{
linkFormatter: noneLinkFormatter,
nameFormatter: noneNameFormatter,
withSender: false,
withRepoName: false,
},
expectedText: "Issue opened: #2 crash",
},
{
formatter: webhookPayloadFormatter{
linkFormatter: noneLinkFormatter,
nameFormatter: noneNameFormatter,
withSender: true,
withRepoName: false,
},
expectedText: "Issue opened: #2 crash by user1",
},
{
formatter: webhookPayloadFormatter{
linkFormatter: noneLinkFormatter,
nameFormatter: noneNameFormatter,
withSender: false,
withRepoName: true,
},
expectedText: "[test/repo] Issue opened: #2 crash",
},
{
formatter: webhookPayloadFormatter{
linkFormatter: noneLinkFormatter,
nameFormatter: noneNameFormatter,
withSender: true,
withRepoName: true,
},
expectedText: "[test/repo] Issue opened: #2 crash by user1",
},
{
formatter: webhookPayloadFormatter{
linkFormatter: func(url, text string) string {
return fmt.Sprintf("%s @ %s", text, url)
},
nameFormatter: noneNameFormatter,
withSender: true,
withRepoName: true,
},
expectedText: "[test/repo] Issue opened: #2 crash @ http://localhost:3000/test/repo/issues/2 by user1",
},
{
formatter: webhookPayloadFormatter{
linkFormatter: noneLinkFormatter,
nameFormatter: func(name string) string {
return fmt.Sprintf("!!! @%s !!!", name)
},
withSender: true,
withRepoName: false,
},
expectedText: "Issue opened: #2 crash by !!! @user1 !!!",
},
}
for i, c := range formatterCases {
text, _, _, _ := c.formatter.getIssuesPayloadInfo(p)
assert.Equal(t, c.expectedText, text, "case %d", i)
}
}
+13 -6
View File
@@ -127,6 +127,13 @@ type MatrixPayload struct {
var _ shared.PayloadConvertor[MatrixPayload] = matrixConvertor{}
var matrixPayloadFormatter = webhookPayloadFormatter{
linkFormatter: htmlLinkFormatter,
nameFormatter: noneNameFormatter,
withSender: true,
withRepoName: true,
}
type matrixConvertor struct {
MsgType string
}
@@ -169,28 +176,28 @@ func (m matrixConvertor) Fork(p *api.ForkPayload) (MatrixPayload, error) {
// Issue implements payloadConvertor Issue method
func (m matrixConvertor) Issue(p *api.IssuePayload) (MatrixPayload, error) {
text, _, _, _ := getIssuesPayloadInfo(p, htmlLinkFormatter, noneNameFormatter, true)
text, _, _, _ := matrixPayloadFormatter.getIssuesPayloadInfo(p)
return m.newPayload(text)
}
// IssueComment implements payloadConvertor IssueComment method
func (m matrixConvertor) IssueComment(p *api.IssueCommentPayload) (MatrixPayload, error) {
text, _, _ := getIssueCommentPayloadInfo(p, htmlLinkFormatter, noneNameFormatter, true)
text, _, _ := matrixPayloadFormatter.getIssueCommentPayloadInfo(p)
return m.newPayload(text)
}
// Wiki implements payloadConvertor Wiki method
func (m matrixConvertor) Wiki(p *api.WikiPayload) (MatrixPayload, error) {
text, _, _ := getWikiPayloadInfo(p, htmlLinkFormatter, noneNameFormatter, true)
text, _, _ := matrixPayloadFormatter.getWikiPayloadInfo(p, true)
return m.newPayload(text)
}
// Release implements payloadConvertor Release method
func (m matrixConvertor) Release(p *api.ReleasePayload) (MatrixPayload, error) {
text, _ := getReleasePayloadInfo(p, htmlLinkFormatter, noneNameFormatter, true)
text, _ := matrixPayloadFormatter.getReleasePayloadInfo(p)
return m.newPayload(text)
}
@@ -223,7 +230,7 @@ func (m matrixConvertor) Push(p *api.PushPayload) (MatrixPayload, error) {
// PullRequest implements payloadConvertor PullRequest method
func (m matrixConvertor) PullRequest(p *api.PullRequestPayload) (MatrixPayload, error) {
text, _, _, _ := getPullRequestPayloadInfo(p, htmlLinkFormatter, noneNameFormatter, true)
text, _, _, _ := matrixPayloadFormatter.getPullRequestPayloadInfo(p)
return m.newPayload(text)
}
@@ -275,7 +282,7 @@ func (m matrixConvertor) Package(p *api.PackagePayload) (MatrixPayload, error) {
}
func (m matrixConvertor) Action(p *api.ActionPayload) (MatrixPayload, error) {
text, _ := getActionPayloadInfo(p, htmlLinkFormatter)
text, _ := matrixPayloadFormatter.getActionPayloadInfo(p)
return m.newPayload(text)
}
+6 -6
View File
@@ -159,24 +159,24 @@ func TestMatrixPayload(t *testing.T) {
require.NoError(t, err)
require.NotNil(t, pl)
assert.Equal(t, "[test/repo] New wiki page '[index](http://localhost:3000/test/repo/wiki/index)' (Wiki change comment) by user1", pl.Body)
assert.Equal(t, `[test/repo] New wiki page '<a href="http://localhost:3000/test/repo/wiki/index">index</a>' (Wiki change comment) by user1`, pl.FormattedBody)
assert.Equal(t, "[test/repo] New wiki page \"[index](http://localhost:3000/test/repo/wiki/index)\" (Wiki change comment) by user1", pl.Body)
assert.Equal(t, `[test/repo] New wiki page "<a href="http://localhost:3000/test/repo/wiki/index">index</a>" (Wiki change comment) by user1`, pl.FormattedBody)
p.Action = api.HookWikiEdited
pl, err = mc.Wiki(p)
require.NoError(t, err)
require.NotNil(t, pl)
assert.Equal(t, "[test/repo] Wiki page '[index](http://localhost:3000/test/repo/wiki/index)' edited (Wiki change comment) by user1", pl.Body)
assert.Equal(t, `[test/repo] Wiki page '<a href="http://localhost:3000/test/repo/wiki/index">index</a>' edited (Wiki change comment) by user1`, pl.FormattedBody)
assert.Equal(t, "[test/repo] Wiki page \"[index](http://localhost:3000/test/repo/wiki/index)\" edited (Wiki change comment) by user1", pl.Body)
assert.Equal(t, `[test/repo] Wiki page "<a href="http://localhost:3000/test/repo/wiki/index">index</a>" edited (Wiki change comment) by user1`, pl.FormattedBody)
p.Action = api.HookWikiDeleted
pl, err = mc.Wiki(p)
require.NoError(t, err)
require.NotNil(t, pl)
assert.Equal(t, "[test/repo] Wiki page '[index](http://localhost:3000/test/repo/wiki/index)' deleted by user1", pl.Body)
assert.Equal(t, `[test/repo] Wiki page '<a href="http://localhost:3000/test/repo/wiki/index">index</a>' deleted by user1`, pl.FormattedBody)
assert.Equal(t, "[test/repo] Wiki page \"[index](http://localhost:3000/test/repo/wiki/index)\" deleted by user1", pl.Body)
assert.Equal(t, `[test/repo] Wiki page "<a href="http://localhost:3000/test/repo/wiki/index">index</a>" deleted by user1`, pl.FormattedBody)
})
t.Run("Release", func(t *testing.T) {
File diff suppressed because it is too large Load Diff
+246 -284
View File
@@ -4,6 +4,9 @@
package webhook
import (
"bytes"
"context"
"strings"
"testing"
webhook_model "forgejo.org/models/webhook"
@@ -15,32 +18,74 @@ import (
"github.com/stretchr/testify/require"
)
// findTextInContainer recursively searches for text within an MSTeamsContainer
func findTextInContainer(c MSTeamsContainer, substr string) bool {
for _, it := range c.Items {
switch v := it.(type) {
case MSTeamsTextBlock:
if strings.Contains(v.Text, substr) {
return true
}
case MSTeamsColumnSet:
for _, col := range v.Columns {
for _, it2 := range col.Items {
if tb, ok := it2.(MSTeamsTextBlock); ok && strings.Contains(tb.Text, substr) {
return true
}
}
}
case MSTeamsContainer:
if findTextInContainer(v, substr) {
return true
}
case MSTeamsFactSet:
for _, fact := range v.Facts {
if strings.Contains(fact.Value, substr) || strings.Contains(fact.Title, substr) {
return true
}
}
}
}
return false
}
func TestMSTeamsPayload(t *testing.T) {
mc := msteamsConvertor{}
// helper to find text within the adaptive card body
findTextInBody := func(pl MSTeamsPayload, substr string) bool {
for _, container := range pl.Body {
if findTextInContainer(container, substr) {
return true
}
}
return false
}
t.Run("Create", func(t *testing.T) {
p := createTestPayload()
pl, err := mc.Create(p)
require.NoError(t, err)
require.NotNil(t, pl)
assert.Equal(t, "[test/repo] branch test created", pl.Title)
assert.Equal(t, "[test/repo] branch test created", pl.Summary)
assert.Len(t, pl.Sections, 1)
assert.Equal(t, "user1", pl.Sections[0].ActivitySubtitle)
assert.Empty(t, pl.Sections[0].Text)
assert.Len(t, pl.Sections[0].Facts, 2)
for _, fact := range pl.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.PotentialAction, 1)
assert.Len(t, pl.PotentialAction[0].Targets, 1)
assert.Equal(t, "http://localhost:3000/test/repo/src/test", pl.PotentialAction[0].Targets[0].URI)
// Check payload structure
require.Equal(t, "AdaptiveCard", pl.Type)
require.Equal(t, "1.5", pl.Version)
// Check body structure: header + title + badge sections
require.GreaterOrEqual(t, len(pl.Body), 2)
// Header should contain repo info
assert.True(t, findTextInBody(pl, "test/repo"))
// Title should contain action by user
assert.True(t, findTextInBody(pl, "Branch created: test"))
// action button should point to branch
require.Len(t, pl.Actions, 1)
assert.Equal(t, "View in Forgejo", pl.Actions[0].Title)
assert.Equal(t, "http://localhost:3000/test/repo/src/test", pl.Actions[0].URL)
})
t.Run("Delete", func(t *testing.T) {
@@ -48,25 +93,20 @@ func TestMSTeamsPayload(t *testing.T) {
pl, err := mc.Delete(p)
require.NoError(t, err)
require.NotNil(t, pl)
assert.Equal(t, "[test/repo] branch test deleted", pl.Title)
assert.Equal(t, "[test/repo] branch test deleted", pl.Summary)
assert.Len(t, pl.Sections, 1)
assert.Equal(t, "user1", pl.Sections[0].ActivitySubtitle)
assert.Empty(t, pl.Sections[0].Text)
assert.Len(t, pl.Sections[0].Facts, 2)
for _, fact := range pl.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.PotentialAction, 1)
assert.Len(t, pl.PotentialAction[0].Targets, 1)
assert.Equal(t, "http://localhost:3000/test/repo/src/test", pl.PotentialAction[0].Targets[0].URI)
// Check basic structure
require.Equal(t, "AdaptiveCard", pl.Type)
require.GreaterOrEqual(t, len(pl.Body), 2)
// Verify content
assert.True(t, findTextInBody(pl, "test/repo"))
assert.True(t, findTextInBody(pl, "Branch deleted: test"))
// action button should point to branch
require.Len(t, pl.Actions, 1)
assert.Equal(t, "View in Forgejo", pl.Actions[0].Title)
assert.Equal(t, "http://localhost:3000/test/repo", pl.Actions[0].URL)
})
t.Run("Fork", func(t *testing.T) {
@@ -74,25 +114,19 @@ func TestMSTeamsPayload(t *testing.T) {
pl, err := mc.Fork(p)
require.NoError(t, err)
require.NotNil(t, pl)
assert.Equal(t, "test/repo2 is forked to test/repo", pl.Title)
assert.Equal(t, "test/repo2 is forked to test/repo", pl.Summary)
assert.Len(t, pl.Sections, 1)
assert.Equal(t, "user1", pl.Sections[0].ActivitySubtitle)
assert.Empty(t, pl.Sections[0].Text)
assert.Len(t, pl.Sections[0].Facts, 2)
for _, fact := range pl.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.PotentialAction, 1)
assert.Len(t, pl.PotentialAction[0].Targets, 1)
assert.Equal(t, "http://localhost:3000/test/repo", pl.PotentialAction[0].Targets[0].URI)
// Check basic structure
require.Equal(t, "AdaptiveCard", pl.Type)
require.GreaterOrEqual(t, len(pl.Body), 2)
// Verify content
assert.True(t, findTextInBody(pl, "[test/repo2](http://localhost:3000/test/repo2) is forked to test/repo"))
// action button should point to repo
require.Len(t, pl.Actions, 1)
assert.Equal(t, "View in Forgejo", pl.Actions[0].Title)
assert.Equal(t, "http://localhost:3000/test/repo", pl.Actions[0].URL)
})
t.Run("Push", func(t *testing.T) {
@@ -100,25 +134,23 @@ func TestMSTeamsPayload(t *testing.T) {
pl, err := mc.Push(p)
require.NoError(t, err)
require.NotNil(t, pl)
assert.Equal(t, "[test/repo:test] 2 new commits", pl.Title)
assert.Equal(t, "[test/repo:test] 2 new commits", pl.Summary)
assert.Len(t, pl.Sections, 1)
assert.Equal(t, "user1", pl.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.Sections[0].Text)
assert.Len(t, pl.Sections[0].Facts, 2)
for _, fact := range pl.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.PotentialAction, 1)
assert.Len(t, pl.PotentialAction[0].Targets, 1)
assert.Equal(t, "http://localhost:3000/test/repo/src/test", pl.PotentialAction[0].Targets[0].URI)
// Check basic structure
require.Equal(t, "AdaptiveCard", pl.Type)
require.GreaterOrEqual(t, len(pl.Body), 2)
// Verify repo and basic content
assert.True(t, findTextInBody(pl, "[test] 2 new commits"))
// commit details should be present in body
assert.True(t, findTextInBody(pl, "2020558"))
assert.True(t, findTextInBody(pl, "commit message"))
// action button should point to compare
require.Len(t, pl.Actions, 1)
assert.Equal(t, "View in Forgejo", pl.Actions[0].Title)
assert.Equal(t, "http://localhost:3000/test/repo/src/test", pl.Actions[0].URL)
})
t.Run("Issue", func(t *testing.T) {
@@ -127,48 +159,28 @@ func TestMSTeamsPayload(t *testing.T) {
p.Action = api.HookIssueOpened
pl, err := mc.Issue(p)
require.NoError(t, err)
require.NotNil(t, pl)
assert.Equal(t, "[test/repo] Issue opened: #2 crash", pl.Title)
assert.Equal(t, "[test/repo] Issue opened: #2 crash", pl.Summary)
assert.Len(t, pl.Sections, 1)
assert.Equal(t, "user1", pl.Sections[0].ActivitySubtitle)
assert.Equal(t, "issue body", pl.Sections[0].Text)
assert.Len(t, pl.Sections[0].Facts, 2)
for _, fact := range pl.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.PotentialAction, 1)
assert.Len(t, pl.PotentialAction[0].Targets, 1)
assert.Equal(t, "http://localhost:3000/test/repo/issues/2", pl.PotentialAction[0].Targets[0].URI)
// Check basic structure
require.Equal(t, "AdaptiveCard", pl.Type)
require.GreaterOrEqual(t, len(pl.Body), 2)
// Verify content
assert.True(t, findTextInBody(pl, "test/repo"))
assert.True(t, findTextInBody(pl, "Issue opened: #2 crash"))
assert.True(t, findTextInBody(pl, "issue body"))
require.Len(t, pl.Actions, 1)
assert.Equal(t, "View in Forgejo", pl.Actions[0].Title)
assert.Equal(t, "http://localhost:3000/test/repo/issues/2", pl.Actions[0].URL)
p.Action = api.HookIssueClosed
pl, err = mc.Issue(p)
require.NoError(t, err)
assert.Equal(t, "[test/repo] Issue closed: #2 crash", pl.Title)
assert.Equal(t, "[test/repo] Issue closed: #2 crash", pl.Summary)
assert.Len(t, pl.Sections, 1)
assert.Equal(t, "user1", pl.Sections[0].ActivitySubtitle)
assert.Empty(t, pl.Sections[0].Text)
assert.Len(t, pl.Sections[0].Facts, 2)
for _, fact := range pl.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.PotentialAction, 1)
assert.Len(t, pl.PotentialAction[0].Targets, 1)
assert.Equal(t, "http://localhost:3000/test/repo/issues/2", pl.PotentialAction[0].Targets[0].URI)
require.NotNil(t, pl)
assert.True(t, findTextInBody(pl, "Issue closed: #2 crash"))
require.Len(t, pl.Actions, 1)
assert.Equal(t, "http://localhost:3000/test/repo/issues/2", pl.Actions[0].URL)
})
t.Run("IssueComment", func(t *testing.T) {
@@ -176,51 +188,53 @@ func TestMSTeamsPayload(t *testing.T) {
pl, err := mc.IssueComment(p)
require.NoError(t, err)
require.NotNil(t, pl)
assert.Equal(t, "[test/repo] New comment on issue #2 crash", pl.Title)
assert.Equal(t, "[test/repo] New comment on issue #2 crash", pl.Summary)
assert.Len(t, pl.Sections, 1)
assert.Equal(t, "user1", pl.Sections[0].ActivitySubtitle)
assert.Equal(t, "more info needed", pl.Sections[0].Text)
assert.Len(t, pl.Sections[0].Facts, 2)
for _, fact := range pl.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.PotentialAction, 1)
assert.Len(t, pl.PotentialAction[0].Targets, 1)
assert.Equal(t, "http://localhost:3000/test/repo/issues/2#issuecomment-4", pl.PotentialAction[0].Targets[0].URI)
// Check basic structure
require.Equal(t, "AdaptiveCard", pl.Type)
require.GreaterOrEqual(t, len(pl.Body), 2)
// Verify content
assert.True(t, findTextInBody(pl, "test/repo"))
assert.True(t, findTextInBody(pl, "New comment on issue #2 crash"))
assert.True(t, findTextInBody(pl, "more info needed"))
require.Len(t, pl.Actions, 1)
assert.Equal(t, "View in Forgejo", pl.Actions[0].Title)
assert.Equal(t, "http://localhost:3000/test/repo/issues/2#issuecomment-4", pl.Actions[0].URL)
})
t.Run("PullRequest", func(t *testing.T) {
p := pullRequestTestPayload()
p.PullRequest.Head = &api.PRBranchInfo{
Name: "feature/test",
Ref: "feature/test",
Sha: "b1eb92dc659513b7b4eb57d7ee7f9c6f92e714b5",
RepoID: 1,
Repository: &api.Repository{
HTMLURL: "http://localhost:3000/test/repo",
Name: "repo",
FullName: "test/repo",
},
}
pl, err := mc.PullRequest(p)
require.NoError(t, err)
require.NotNil(t, pl)
assert.Equal(t, "[test/repo] Pull request opened: #12 Fix bug", pl.Title)
assert.Equal(t, "[test/repo] Pull request opened: #12 Fix bug", pl.Summary)
assert.Len(t, pl.Sections, 1)
assert.Equal(t, "user1", pl.Sections[0].ActivitySubtitle)
assert.Equal(t, "fixes bug #2", pl.Sections[0].Text)
assert.Len(t, pl.Sections[0].Facts, 2)
for _, fact := range pl.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.PotentialAction, 1)
assert.Len(t, pl.PotentialAction[0].Targets, 1)
assert.Equal(t, "http://localhost:3000/test/repo/pulls/12", pl.PotentialAction[0].Targets[0].URI)
// Check basic structure
require.Equal(t, "AdaptiveCard", pl.Type)
require.GreaterOrEqual(t, len(pl.Body), 2)
// Verify content
assert.True(t, findTextInBody(pl, "test/repo"))
assert.True(t, findTextInBody(pl, "Pull request opened: #12 Fix bug"))
assert.True(t, findTextInBody(pl, "fixes bug #2"))
assert.True(t, findTextInBody(pl, "feature/test → refs/pull/2/head"))
require.Len(t, pl.Actions, 1)
assert.Equal(t, "View in Forgejo", pl.Actions[0].Title)
assert.Equal(t, "http://localhost:3000/test/repo/pulls/12", pl.Actions[0].URL)
})
t.Run("PullRequestComment", func(t *testing.T) {
@@ -228,25 +242,20 @@ func TestMSTeamsPayload(t *testing.T) {
pl, err := mc.IssueComment(p)
require.NoError(t, err)
require.NotNil(t, pl)
assert.Equal(t, "[test/repo] New comment on pull request #12 Fix bug", pl.Title)
assert.Equal(t, "[test/repo] New comment on pull request #12 Fix bug", pl.Summary)
assert.Len(t, pl.Sections, 1)
assert.Equal(t, "user1", pl.Sections[0].ActivitySubtitle)
assert.Equal(t, "changes requested", pl.Sections[0].Text)
assert.Len(t, pl.Sections[0].Facts, 2)
for _, fact := range pl.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.PotentialAction, 1)
assert.Len(t, pl.PotentialAction[0].Targets, 1)
assert.Equal(t, "http://localhost:3000/test/repo/pulls/12#issuecomment-4", pl.PotentialAction[0].Targets[0].URI)
// Check basic structure
require.Equal(t, "AdaptiveCard", pl.Type)
require.GreaterOrEqual(t, len(pl.Body), 2)
// Verify content
assert.True(t, findTextInBody(pl, "test/repo"))
assert.True(t, findTextInBody(pl, "New comment on pull request #12 Fix bug"))
assert.True(t, findTextInBody(pl, "changes requested"))
require.Len(t, pl.Actions, 1)
assert.Equal(t, "View in Forgejo", pl.Actions[0].Title)
assert.Equal(t, "http://localhost:3000/test/repo/pulls/12#issuecomment-4", pl.Actions[0].URL)
})
t.Run("Review", func(t *testing.T) {
@@ -255,25 +264,19 @@ func TestMSTeamsPayload(t *testing.T) {
pl, err := mc.Review(p, webhook_module.HookEventPullRequestReviewApproved)
require.NoError(t, err)
require.NotNil(t, pl)
assert.Equal(t, "[test/repo] Pull request review approved: #12 Fix bug", pl.Title)
assert.Equal(t, "[test/repo] Pull request review approved: #12 Fix bug", pl.Summary)
assert.Len(t, pl.Sections, 1)
assert.Equal(t, "user1", pl.Sections[0].ActivitySubtitle)
assert.Equal(t, "good job", pl.Sections[0].Text)
assert.Len(t, pl.Sections[0].Facts, 2)
for _, fact := range pl.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.PotentialAction, 1)
assert.Len(t, pl.PotentialAction[0].Targets, 1)
assert.Equal(t, "http://localhost:3000/test/repo/pulls/12", pl.PotentialAction[0].Targets[0].URI)
// Check basic structure
require.Equal(t, "AdaptiveCard", pl.Type)
require.GreaterOrEqual(t, len(pl.Body), 2)
// review content should be present
assert.True(t, findTextInBody(pl, "Pull request review approved: #12 Fix bug"))
assert.True(t, findTextInBody(pl, "good job"))
require.Len(t, pl.Actions, 1)
assert.Equal(t, "View in Forgejo", pl.Actions[0].Title)
assert.Equal(t, "http://localhost:3000/test/repo/pulls/12", pl.Actions[0].URL)
})
t.Run("Repository", func(t *testing.T) {
@@ -281,23 +284,18 @@ func TestMSTeamsPayload(t *testing.T) {
pl, err := mc.Repository(p)
require.NoError(t, err)
require.NotNil(t, pl)
assert.Equal(t, "[test/repo] Repository created", pl.Title)
assert.Equal(t, "[test/repo] Repository created", pl.Summary)
assert.Len(t, pl.Sections, 1)
assert.Equal(t, "user1", pl.Sections[0].ActivitySubtitle)
assert.Empty(t, pl.Sections[0].Text)
assert.Len(t, pl.Sections[0].Facts, 1)
for _, fact := range pl.Sections[0].Facts {
if fact.Name == "Repository:" {
assert.Equal(t, p.Repository.FullName, fact.Value)
} else {
t.Fail()
}
}
assert.Len(t, pl.PotentialAction, 1)
assert.Len(t, pl.PotentialAction[0].Targets, 1)
assert.Equal(t, "http://localhost:3000/test/repo", pl.PotentialAction[0].Targets[0].URI)
// Check basic structure
require.Equal(t, "AdaptiveCard", pl.Type)
require.GreaterOrEqual(t, len(pl.Body), 2)
// Verify content
assert.True(t, findTextInBody(pl, "Repository created: test/repo"))
require.Len(t, pl.Actions, 1)
assert.Equal(t, "View in Forgejo", pl.Actions[0].Title)
assert.Equal(t, "http://localhost:3000/test/repo", pl.Actions[0].URL)
})
t.Run("Package", func(t *testing.T) {
@@ -305,23 +303,20 @@ func TestMSTeamsPayload(t *testing.T) {
pl, err := mc.Package(p)
require.NoError(t, err)
require.NotNil(t, pl)
assert.Equal(t, "Package created: GiteaContainer:latest", pl.Title)
assert.Equal(t, "Package created: GiteaContainer:latest", pl.Summary)
assert.Len(t, pl.Sections, 1)
assert.Equal(t, "user1", pl.Sections[0].ActivitySubtitle)
assert.Empty(t, pl.Sections[0].Text)
assert.Len(t, pl.Sections[0].Facts, 1)
for _, fact := range pl.Sections[0].Facts {
if fact.Name == "Package:" {
assert.Equal(t, p.Package.Name, fact.Value)
} else {
t.Fail()
}
}
assert.Len(t, pl.PotentialAction, 1)
assert.Len(t, pl.PotentialAction[0].Targets, 1)
assert.Equal(t, "http://localhost:3000/user1/-/packages/container/GiteaContainer/latest", pl.PotentialAction[0].Targets[0].URI)
// Check basic structure
require.Equal(t, "AdaptiveCard", pl.Type)
require.GreaterOrEqual(t, len(pl.Body), 2)
// no repo is associated
assert.False(t, findTextInBody(pl, "test/repo"))
// Verify content
assert.True(t, findTextInBody(pl, "Package created: GiteaContainer:latest"))
require.Len(t, pl.Actions, 1)
assert.Equal(t, "View in Forgejo", pl.Actions[0].Title)
assert.Equal(t, "http://localhost:3000/user1/-/packages/container/GiteaContainer/latest", pl.Actions[0].URL)
})
t.Run("Wiki", func(t *testing.T) {
@@ -330,65 +325,35 @@ func TestMSTeamsPayload(t *testing.T) {
p.Action = api.HookWikiCreated
pl, err := mc.Wiki(p)
require.NoError(t, err)
require.NotNil(t, pl)
assert.Equal(t, "[test/repo] New wiki page 'index' (Wiki change comment)", pl.Title)
assert.Equal(t, "[test/repo] New wiki page 'index' (Wiki change comment)", pl.Summary)
assert.Len(t, pl.Sections, 1)
assert.Equal(t, "user1", pl.Sections[0].ActivitySubtitle)
assert.Empty(t, pl.Sections[0].Text)
assert.Len(t, pl.Sections[0].Facts, 2)
for _, fact := range pl.Sections[0].Facts {
if fact.Name == "Repository:" {
assert.Equal(t, p.Repository.FullName, fact.Value)
} else {
t.Fail()
}
}
assert.Len(t, pl.PotentialAction, 1)
assert.Len(t, pl.PotentialAction[0].Targets, 1)
assert.Equal(t, "http://localhost:3000/test/repo/wiki/index", pl.PotentialAction[0].Targets[0].URI)
// Check basic structure
require.Equal(t, "AdaptiveCard", pl.Type)
require.GreaterOrEqual(t, len(pl.Body), 2)
// Verify content for create
assert.True(t, findTextInBody(pl, "New wiki page \"index\""))
assert.True(t, findTextInBody(pl, "Wiki change comment"))
require.Len(t, pl.Actions, 1)
assert.Equal(t, "View in Forgejo", pl.Actions[0].Title)
assert.Equal(t, "http://localhost:3000/test/repo/wiki/index", pl.Actions[0].URL)
p.Action = api.HookWikiEdited
pl, err = mc.Wiki(p)
require.NoError(t, err)
assert.Equal(t, "[test/repo] Wiki page 'index' edited (Wiki change comment)", pl.Title)
assert.Equal(t, "[test/repo] Wiki page 'index' edited (Wiki change comment)", pl.Summary)
assert.Len(t, pl.Sections, 1)
assert.Equal(t, "user1", pl.Sections[0].ActivitySubtitle)
assert.Empty(t, pl.Sections[0].Text)
assert.Len(t, pl.Sections[0].Facts, 2)
for _, fact := range pl.Sections[0].Facts {
if fact.Name == "Repository:" {
assert.Equal(t, p.Repository.FullName, fact.Value)
} else {
t.Fail()
}
}
assert.Len(t, pl.PotentialAction, 1)
assert.Len(t, pl.PotentialAction[0].Targets, 1)
assert.Equal(t, "http://localhost:3000/test/repo/wiki/index", pl.PotentialAction[0].Targets[0].URI)
require.NotNil(t, pl)
assert.True(t, findTextInBody(pl, "Wiki page \"index\" edited"))
assert.True(t, findTextInBody(pl, "Wiki change comment"))
require.Len(t, pl.Actions, 1)
assert.Equal(t, "http://localhost:3000/test/repo/wiki/index", pl.Actions[0].URL)
p.Action = api.HookWikiDeleted
pl, err = mc.Wiki(p)
require.NoError(t, err)
assert.Equal(t, "[test/repo] Wiki page 'index' deleted", pl.Title)
assert.Equal(t, "[test/repo] Wiki page 'index' deleted", pl.Summary)
assert.Len(t, pl.Sections, 1)
assert.Equal(t, "user1", pl.Sections[0].ActivitySubtitle)
assert.Empty(t, pl.Sections[0].Text)
assert.Len(t, pl.Sections[0].Facts, 2)
for _, fact := range pl.Sections[0].Facts {
if fact.Name == "Repository:" {
assert.Equal(t, p.Repository.FullName, fact.Value)
} else {
t.Fail()
}
}
assert.Len(t, pl.PotentialAction, 1)
assert.Len(t, pl.PotentialAction[0].Targets, 1)
assert.Equal(t, "http://localhost:3000/test/repo/wiki/index", pl.PotentialAction[0].Targets[0].URI)
require.NotNil(t, pl)
assert.True(t, findTextInBody(pl, "Wiki page \"index\" deleted"))
require.Len(t, pl.Actions, 1)
assert.Equal(t, "http://localhost:3000/test/repo/wiki/index", pl.Actions[0].URL)
})
t.Run("Release", func(t *testing.T) {
@@ -396,25 +361,18 @@ func TestMSTeamsPayload(t *testing.T) {
pl, err := mc.Release(p)
require.NoError(t, err)
require.NotNil(t, pl)
assert.Equal(t, "[test/repo] Release created: v1.0", pl.Title)
assert.Equal(t, "[test/repo] Release created: v1.0", pl.Summary)
assert.Len(t, pl.Sections, 1)
assert.Equal(t, "user1", pl.Sections[0].ActivitySubtitle)
assert.Empty(t, pl.Sections[0].Text)
assert.Len(t, pl.Sections[0].Facts, 2)
for _, fact := range pl.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.PotentialAction, 1)
assert.Len(t, pl.PotentialAction[0].Targets, 1)
assert.Equal(t, "http://localhost:3000/test/repo/releases/tag/v1.0", pl.PotentialAction[0].Targets[0].URI)
// Check basic structure
require.Equal(t, "AdaptiveCard", pl.Type)
require.GreaterOrEqual(t, len(pl.Body), 2)
// Verify content
assert.True(t, findTextInBody(pl, "Release created: v1.0"))
require.Len(t, pl.Actions, 1)
assert.Equal(t, "View in Forgejo", pl.Actions[0].Title)
assert.Equal(t, "http://localhost:3000/test/repo/releases/tag/v1.0", pl.Actions[0].URL)
})
}
@@ -422,6 +380,7 @@ func TestMSTeamsJSONPayload(t *testing.T) {
p := pushTestPayload()
data, err := p.JSONPayload()
require.NoError(t, err)
require.NotNil(t, data)
hook := &webhook_model.Webhook{
RepoID: 3,
@@ -438,7 +397,7 @@ func TestMSTeamsJSONPayload(t *testing.T) {
PayloadVersion: 2,
}
req, reqBody, err := msteamsHandler{}.NewRequest(t.Context(), hook, task)
req, reqBody, err := msteamsHandler{}.NewRequest(context.Background(), hook, task)
require.NotNil(t, req)
require.NotNil(t, reqBody)
require.NoError(t, err)
@@ -448,7 +407,10 @@ func TestMSTeamsJSONPayload(t *testing.T) {
assert.Equal(t, "sha256=", req.Header.Get("X-Hub-Signature-256"))
assert.Equal(t, "application/json", req.Header.Get("Content-Type"))
var body MSTeamsPayload
err = json.NewDecoder(req.Body).Decode(&body)
err = json.NewDecoder(bytes.NewReader(reqBody)).Decode(&body)
require.NoError(t, err)
assert.Equal(t, "[test/repo:test] 2 new commits", body.Summary)
// Verify payload structure
assert.Equal(t, "AdaptiveCard", body.Type)
assert.Equal(t, "1.5", body.Version)
}
+14 -7
View File
@@ -119,6 +119,13 @@ func SlackTextFormatter(s string) string {
return s
}
var slackPayloadFormatter = webhookPayloadFormatter{
linkFormatter: SlackLinkFormatter,
nameFormatter: SlackNameFormatter,
withSender: true,
withRepoName: true,
}
// SlackNameFormatter puts the name into an inline code block.
// This way names do not trigger unwanted message notifications, as users usually don't want to get notified about their own actions.
func SlackNameFormatter(name string) string {
@@ -174,7 +181,7 @@ func (s slackConvertor) Fork(p *api.ForkPayload) (SlackPayload, error) {
// Issue implements payloadConvertor Issue method
func (s slackConvertor) Issue(p *api.IssuePayload) (SlackPayload, error) {
text, issueTitle, attachmentText, color := getIssuesPayloadInfo(p, SlackLinkFormatter, SlackNameFormatter, true)
text, issueTitle, attachmentText, color := slackPayloadFormatter.getIssuesPayloadInfo(p)
var attachments []SlackAttachment
if attachmentText != "" {
@@ -193,7 +200,7 @@ func (s slackConvertor) Issue(p *api.IssuePayload) (SlackPayload, error) {
// IssueComment implements payloadConvertor IssueComment method
func (s slackConvertor) IssueComment(p *api.IssueCommentPayload) (SlackPayload, error) {
text, issueTitle, color := getIssueCommentPayloadInfo(p, SlackLinkFormatter, SlackNameFormatter, true)
text, issueTitle, color := slackPayloadFormatter.getIssueCommentPayloadInfo(p)
return s.createPayload(text, []SlackAttachment{{
Color: fmt.Sprintf("%x", color),
@@ -205,20 +212,20 @@ func (s slackConvertor) IssueComment(p *api.IssueCommentPayload) (SlackPayload,
// Wiki implements payloadConvertor Wiki method
func (s slackConvertor) Wiki(p *api.WikiPayload) (SlackPayload, error) {
text, _, _ := getWikiPayloadInfo(p, SlackLinkFormatter, SlackNameFormatter, true)
text, _, _ := slackPayloadFormatter.getWikiPayloadInfo(p, true)
return s.createPayload(text, nil), nil
}
// Release implements payloadConvertor Release method
func (s slackConvertor) Release(p *api.ReleasePayload) (SlackPayload, error) {
text, _ := getReleasePayloadInfo(p, SlackLinkFormatter, SlackNameFormatter, true)
text, _ := slackPayloadFormatter.getReleasePayloadInfo(p)
return s.createPayload(text, nil), nil
}
func (s slackConvertor) Package(p *api.PackagePayload) (SlackPayload, error) {
text, _ := getPackagePayloadInfo(p, SlackLinkFormatter, SlackNameFormatter, true)
text, _ := slackPayloadFormatter.getPackagePayloadInfo(p)
return s.createPayload(text, nil), nil
}
@@ -265,7 +272,7 @@ func (s slackConvertor) Push(p *api.PushPayload) (SlackPayload, error) {
// PullRequest implements payloadConvertor PullRequest method
func (s slackConvertor) PullRequest(p *api.PullRequestPayload) (SlackPayload, error) {
text, issueTitle, attachmentText, color := getPullRequestPayloadInfo(p, SlackLinkFormatter, SlackNameFormatter, true)
text, issueTitle, attachmentText, color := slackPayloadFormatter.getPullRequestPayloadInfo(p)
var attachments []SlackAttachment
if attachmentText != "" {
@@ -316,7 +323,7 @@ func (s slackConvertor) Repository(p *api.RepositoryPayload) (SlackPayload, erro
}
func (s slackConvertor) Action(p *api.ActionPayload) (SlackPayload, error) {
text, _ := getActionPayloadInfo(p, SlackLinkFormatter)
text, _ := slackPayloadFormatter.getActionPayloadInfo(p)
return s.createPayload(text, nil), nil
}
+3 -3
View File
@@ -132,19 +132,19 @@ func TestSlackPayload(t *testing.T) {
pl, err := sc.Wiki(p)
require.NoError(t, err)
assert.Equal(t, "[test/repo] New wiki page '<http://localhost:3000/test/repo/wiki/index|index>' (Wiki change comment) by `user1`", pl.Text)
assert.Equal(t, "[test/repo] New wiki page \"<http://localhost:3000/test/repo/wiki/index|index>\" (Wiki change comment) by `user1`", pl.Text)
p.Action = api.HookWikiEdited
pl, err = sc.Wiki(p)
require.NoError(t, err)
assert.Equal(t, "[test/repo] Wiki page '<http://localhost:3000/test/repo/wiki/index|index>' edited (Wiki change comment) by `user1`", pl.Text)
assert.Equal(t, "[test/repo] Wiki page \"<http://localhost:3000/test/repo/wiki/index|index>\" edited (Wiki change comment) by `user1`", pl.Text)
p.Action = api.HookWikiDeleted
pl, err = sc.Wiki(p)
require.NoError(t, err)
assert.Equal(t, "[test/repo] Wiki page '<http://localhost:3000/test/repo/wiki/index|index>' deleted by `user1`", pl.Text)
assert.Equal(t, "[test/repo] Wiki page \"<http://localhost:3000/test/repo/wiki/index|index>\" deleted by `user1`", pl.Text)
})
t.Run("Release", func(t *testing.T) {
+14 -7
View File
@@ -75,6 +75,13 @@ func (telegramHandler) Metadata(w *webhook_model.Webhook) any {
return s
}
var telegramPayloadFormatter = webhookPayloadFormatter{
linkFormatter: htmlLinkFormatter,
nameFormatter: noneNameFormatter,
withSender: true,
withRepoName: true,
}
// Create implements PayloadConvertor Create method
func (t telegramConvertor) Create(p *api.CreatePayload) (TelegramPayload, error) {
// created tag/branch
@@ -136,21 +143,21 @@ func (t telegramConvertor) Push(p *api.PushPayload) (TelegramPayload, error) {
// Issue implements PayloadConvertor Issue method
func (t telegramConvertor) Issue(p *api.IssuePayload) (TelegramPayload, error) {
text, _, attachmentText, _ := getIssuesPayloadInfo(p, htmlLinkFormatter, noneNameFormatter, true)
text, _, attachmentText, _ := telegramPayloadFormatter.getIssuesPayloadInfo(p)
return createTelegramPayload(text + "\n\n" + attachmentText), nil
}
// IssueComment implements PayloadConvertor IssueComment method
func (t telegramConvertor) IssueComment(p *api.IssueCommentPayload) (TelegramPayload, error) {
text, _, _ := getIssueCommentPayloadInfo(p, htmlLinkFormatter, noneNameFormatter, true)
text, _, _ := telegramPayloadFormatter.getIssueCommentPayloadInfo(p)
return createTelegramPayload(text + "\n" + p.Comment.Body), nil
}
// PullRequest implements PayloadConvertor PullRequest method
func (t telegramConvertor) PullRequest(p *api.PullRequestPayload) (TelegramPayload, error) {
text, _, attachmentText, _ := getPullRequestPayloadInfo(p, htmlLinkFormatter, noneNameFormatter, true)
text, _, attachmentText, _ := telegramPayloadFormatter.getPullRequestPayloadInfo(p)
return createTelegramPayload(text + "\n" + attachmentText), nil
}
@@ -187,26 +194,26 @@ func (t telegramConvertor) Repository(p *api.RepositoryPayload) (TelegramPayload
// Wiki implements PayloadConvertor Wiki method
func (t telegramConvertor) Wiki(p *api.WikiPayload) (TelegramPayload, error) {
text, _, _ := getWikiPayloadInfo(p, htmlLinkFormatter, noneNameFormatter, true)
text, _, _ := telegramPayloadFormatter.getWikiPayloadInfo(p, true)
return createTelegramPayload(text), nil
}
// Release implements PayloadConvertor Release method
func (t telegramConvertor) Release(p *api.ReleasePayload) (TelegramPayload, error) {
text, _ := getReleasePayloadInfo(p, htmlLinkFormatter, noneNameFormatter, true)
text, _ := telegramPayloadFormatter.getReleasePayloadInfo(p)
return createTelegramPayload(text), nil
}
func (t telegramConvertor) Package(p *api.PackagePayload) (TelegramPayload, error) {
text, _ := getPackagePayloadInfo(p, htmlLinkFormatter, noneNameFormatter, true)
text, _ := telegramPayloadFormatter.getPackagePayloadInfo(p)
return createTelegramPayload(text), nil
}
func (telegramConvertor) Action(p *api.ActionPayload) (TelegramPayload, error) {
text, _ := getActionPayloadInfo(p, htmlLinkFormatter)
text, _ := telegramPayloadFormatter.getActionPayloadInfo(p)
return createTelegramPayload(text), nil
}
+3 -3
View File
@@ -148,19 +148,19 @@ good job`, pl.Message)
pl, err := tc.Wiki(p)
require.NoError(t, err)
assert.Equal(t, `[test/repo] New wiki page &#39;<a href="http://localhost:3000/test/repo/wiki/index" rel="nofollow">index</a>&#39; (Wiki change comment) by user1`, pl.Message)
assert.Equal(t, `[test/repo] New wiki page &#34;<a href="http://localhost:3000/test/repo/wiki/index" rel="nofollow">index</a>&#34; (Wiki change comment) by user1`, pl.Message)
p.Action = api.HookWikiEdited
pl, err = tc.Wiki(p)
require.NoError(t, err)
assert.Equal(t, `[test/repo] Wiki page &#39;<a href="http://localhost:3000/test/repo/wiki/index" rel="nofollow">index</a>&#39; edited (Wiki change comment) by user1`, pl.Message)
assert.Equal(t, `[test/repo] Wiki page &#34;<a href="http://localhost:3000/test/repo/wiki/index" rel="nofollow">index</a>&#34; edited (Wiki change comment) by user1`, pl.Message)
p.Action = api.HookWikiDeleted
pl, err = tc.Wiki(p)
require.NoError(t, err)
assert.Equal(t, `[test/repo] Wiki page &#39;<a href="http://localhost:3000/test/repo/wiki/index" rel="nofollow">index</a>&#39; deleted by user1`, pl.Message)
assert.Equal(t, `[test/repo] Wiki page &#34;<a href="http://localhost:3000/test/repo/wiki/index" rel="nofollow">index</a>&#34; deleted by user1`, pl.Message)
})
t.Run("Release", func(t *testing.T) {
+14 -7
View File
@@ -70,6 +70,13 @@ func newWechatworkMarkdownPayload(title string) WechatworkPayload {
}
}
var wechatworkPayloadFormatter = webhookPayloadFormatter{
linkFormatter: noneLinkFormatter,
nameFormatter: noneNameFormatter,
withSender: true,
withRepoName: true,
}
// Create implements PayloadConvertor Create method
func (wc wechatworkConvertor) Create(p *api.CreatePayload) (WechatworkPayload, error) {
// created tag/branch
@@ -126,7 +133,7 @@ func (wc wechatworkConvertor) Push(p *api.PushPayload) (WechatworkPayload, error
// Issue implements PayloadConvertor Issue method
func (wc wechatworkConvertor) Issue(p *api.IssuePayload) (WechatworkPayload, error) {
text, issueTitle, attachmentText, _ := getIssuesPayloadInfo(p, noneLinkFormatter, noneNameFormatter, true)
text, issueTitle, attachmentText, _ := wechatworkPayloadFormatter.getIssuesPayloadInfo(p)
var content string
content += fmt.Sprintf(" ><font color=\"info\">%s</font>\n >%s \n ><font color=\"warning\"> %s</font> \n [%s](%s)", text, attachmentText, issueTitle, p.Issue.HTMLURL, p.Issue.HTMLURL)
@@ -135,7 +142,7 @@ func (wc wechatworkConvertor) Issue(p *api.IssuePayload) (WechatworkPayload, err
// IssueComment implements PayloadConvertor IssueComment method
func (wc wechatworkConvertor) IssueComment(p *api.IssueCommentPayload) (WechatworkPayload, error) {
text, issueTitle, _ := getIssueCommentPayloadInfo(p, noneLinkFormatter, noneNameFormatter, true)
text, issueTitle, _ := wechatworkPayloadFormatter.getIssueCommentPayloadInfo(p)
var content string
content += fmt.Sprintf(" ><font color=\"info\">%s</font>\n >%s \n ><font color=\"warning\">%s</font> \n [%s](%s)", text, p.Comment.Body, issueTitle, p.Comment.HTMLURL, p.Comment.HTMLURL)
@@ -144,7 +151,7 @@ func (wc wechatworkConvertor) IssueComment(p *api.IssueCommentPayload) (Wechatwo
// PullRequest implements PayloadConvertor PullRequest method
func (wc wechatworkConvertor) PullRequest(p *api.PullRequestPayload) (WechatworkPayload, error) {
text, issueTitle, attachmentText, _ := getPullRequestPayloadInfo(p, noneLinkFormatter, noneNameFormatter, true)
text, issueTitle, attachmentText, _ := wechatworkPayloadFormatter.getPullRequestPayloadInfo(p)
pr := fmt.Sprintf("> <font color=\"info\"> %s </font> \r\n > <font color=\"comment\">%s </font> \r\n > <font color=\"comment\">%s </font> \r\n",
text, issueTitle, attachmentText)
@@ -183,26 +190,26 @@ func (wc wechatworkConvertor) Repository(p *api.RepositoryPayload) (WechatworkPa
// Wiki implements PayloadConvertor Wiki method
func (wc wechatworkConvertor) Wiki(p *api.WikiPayload) (WechatworkPayload, error) {
text, _, _ := getWikiPayloadInfo(p, noneLinkFormatter, noneNameFormatter, true)
text, _, _ := wechatworkPayloadFormatter.getWikiPayloadInfo(p, true)
return newWechatworkMarkdownPayload(text), nil
}
// Release implements PayloadConvertor Release method
func (wc wechatworkConvertor) Release(p *api.ReleasePayload) (WechatworkPayload, error) {
text, _ := getReleasePayloadInfo(p, noneLinkFormatter, noneNameFormatter, true)
text, _ := wechatworkPayloadFormatter.getReleasePayloadInfo(p)
return newWechatworkMarkdownPayload(text), nil
}
func (wc wechatworkConvertor) Package(p *api.PackagePayload) (WechatworkPayload, error) {
text, _ := getPackagePayloadInfo(p, noneLinkFormatter, noneNameFormatter, true)
text, _ := wechatworkPayloadFormatter.getPackagePayloadInfo(p)
return newWechatworkMarkdownPayload(text), nil
}
func (wc wechatworkConvertor) Action(p *api.ActionPayload) (WechatworkPayload, error) {
text, _ := getActionPayloadInfo(p, noneLinkFormatter)
text, _ := wechatworkPayloadFormatter.getActionPayloadInfo(p)
return newWechatworkMarkdownPayload(text), nil
}