Files
forge/services/mailer/mail_test.go
zeekayandHanzo Dev aa87442fdd Record upstream authorship centrally in NOTICE instead of in every file header
Source files keep their SPDX-License-Identifier and Hanzo copyright line; the
Gitea, Gogs and LFS Test Server copyrights they used to repeat now live once in
NOTICE, alongside LICENSE. The header lint accepts any copyright line above the
SPDX line, so it no longer demands an upstream one.

Co-authored-by: Hanzo Dev <[email protected]>
2026-09-10 17:50:35 -07:00

592 lines
22 KiB
Go

// Copyright 2026 Hanzo AI, Inc. All rights reserved.
// SPDX-License-Identifier: MIT
package mailer
import (
"bytes"
"context"
"encoding/base64"
"fmt"
"html/template"
"io"
"mime/quotedprintable"
"regexp"
"strings"
"testing"
texttmpl "text/template"
actions_model "github.com/hanzoai/git/models/actions"
activities_model "github.com/hanzoai/git/models/activities"
"github.com/hanzoai/git/models/asymkey"
git_model "github.com/hanzoai/git/models/git"
"github.com/hanzoai/git/models/gituser"
issues_model "github.com/hanzoai/git/models/issues"
repo_model "github.com/hanzoai/git/models/repo"
"github.com/hanzoai/git/models/unittest"
user_model "github.com/hanzoai/git/models/user"
"github.com/hanzoai/git/modules/git"
"github.com/hanzoai/git/modules/markup"
"github.com/hanzoai/git/modules/setting"
"github.com/hanzoai/git/modules/storage"
"github.com/hanzoai/git/modules/templates"
"github.com/hanzoai/git/modules/test"
"github.com/hanzoai/git/services/attachment"
sender_service "github.com/hanzoai/git/services/mailer/sender"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
const subjectTpl = `
{{.SubjectPrefix}}[{{.Repo}}] @{{.Doer.Name}} #{{.Issue.Index}} - {{.Issue.Title}}
`
const bodyTpl = `
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>{{.Subject}}</title>
</head>
<body>
<p>{{.Body}}</p>
<p>
---
<br>
<a href="{{.Link}}">View it on the forge</a>.
</p>
</body>
</html>
`
func prepareMailerTest(t *testing.T) (doer *user_model.User, repo *repo_model.Repository, issue *issues_model.Issue, comment *issues_model.Comment) {
assert.NoError(t, unittest.PrepareTestDatabase())
setting.MailService = &setting.Mailer{From: "[email protected]"}
setting.Domain = "localhost"
setting.AppURL = "https://forge.example/"
doer = unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
repo = unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1, Owner: doer})
issue = unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 1, Repo: repo, Poster: doer})
comment = unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{ID: 2, Issue: issue})
require.NoError(t, issue.LoadRepo(t.Context()))
return doer, repo, issue, comment
}
func prepareMailerBase64Test(t *testing.T) (doer *user_model.User, repo *repo_model.Repository, issue *issues_model.Issue, att1, att2 *repo_model.Attachment) {
user, repo, issue, comment := prepareMailerTest(t)
setting.MailService.EmbedAttachmentImages = true
att1, err := attachment.NewAttachment(t.Context(), &repo_model.Attachment{
RepoID: repo.ID,
IssueID: issue.ID,
UploaderID: user.ID,
CommentID: comment.ID,
Name: "test.png",
}, bytes.NewReader([]byte("\x89\x50\x4e\x47\x0d\x0a\x1a\x0a")), 8)
require.NoError(t, err)
att2, err = attachment.NewAttachment(t.Context(), &repo_model.Attachment{
RepoID: repo.ID,
IssueID: issue.ID,
UploaderID: user.ID,
CommentID: comment.ID,
Name: "test.png",
}, bytes.NewReader([]byte("\x89\x50\x4e\x47\x0d\x0a\x1a\x0a"+strings.Repeat("\x00", 1024))), 8+1024)
require.NoError(t, err)
return user, repo, issue, att1, att2
}
func mockMailTemplates(name, subjectTmpl, bodyTmpl string) func() {
return templates.MailRenderer().MockTemplate(name, subjectTmpl, bodyTmpl)
}
func TestComposeIssueComment(t *testing.T) {
doer, _, issue, comment := prepareMailerTest(t)
markup.Init(&markup.RenderHelperFuncs{
IsUsernameMentionable: func(ctx context.Context, username string) bool {
return username == doer.Name
},
})
defer test.MockVariableValue(&setting.IncomingEmail.Enabled, true)()
defer mockMailTemplates("repo/issue/comment", subjectTpl, bodyTpl)()
recipients := []*user_model.User{{Name: "Test", Email: "[email protected]"}, {Name: "Test2", Email: "[email protected]"}}
msgs, err := composeIssueCommentMessages(t.Context(), &mailComment{
Issue: issue, Doer: doer, ActionType: activities_model.ActionCommentIssue,
Content: fmt.Sprintf("test @%s %s#%d body", doer.Name, issue.Repo.FullName(), issue.Index),
Comment: comment,
}, "en-US", recipients, false, "issue comment")
assert.NoError(t, err)
assert.Len(t, msgs, 2)
gomailMsg := msgs[0].ToMessage()
replyTo := gomailMsg.GetGenHeader("Reply-To")[0]
subject := gomailMsg.GetGenHeader("Subject")[0]
assert.Len(t, gomailMsg.GetAddrHeader("To"), 1, "exactly one recipient is expected in the To field")
tokenRegex := regexp.MustCompile(`\Aincoming\+(.+)@localhost\z`)
assert.Regexp(t, tokenRegex, replyTo)
token := tokenRegex.FindAllStringSubmatch(replyTo, 1)[0][1]
assert.Equal(t, "Re: ", subject[:4], "Comment reply subject should contain Re:")
assert.Equal(t, "Re: [user2/repo1] @user2 #1 - issue1", subject)
assert.Equal(t, "<user2/repo1/issues/1@localhost>", gomailMsg.GetGenHeader("In-Reply-To")[0], "In-Reply-To header doesn't match")
assert.ElementsMatch(t, []string{"<user2/repo1/issues/1@localhost>", "<reply-" + token + "@localhost>"}, gomailMsg.GetGenHeader("References"), "References header doesn't match")
assert.Equal(t, "<user2/repo1/issues/1/comment/2@localhost>", gomailMsg.GetGenHeader("Message-ID")[0], "Message-ID header doesn't match")
assert.Equal(t, "<mailto:"+replyTo+">", gomailMsg.GetGenHeader("List-Post")[0])
assert.Len(t, gomailMsg.GetGenHeader("List-Unsubscribe"), 2) // url + mailto
var buf bytes.Buffer
_, err = gomailMsg.WriteTo(&buf)
require.NoError(t, err)
b, err := io.ReadAll(quotedprintable.NewReader(&buf))
assert.NoError(t, err)
// text/plain
assert.Contains(t, string(b), fmt.Sprintf(`( %s )`, doer.HTMLURL(t.Context())))
assert.Contains(t, string(b), fmt.Sprintf(`( %s )`, issue.HTMLURL(t.Context())))
// text/html
assert.Contains(t, string(b), fmt.Sprintf(`href="%s"`, doer.HTMLURL(t.Context())))
assert.Contains(t, string(b), fmt.Sprintf(`href="%s"`, issue.HTMLURL(t.Context())))
}
func TestMailMentionsComment(t *testing.T) {
doer, _, issue, comment := prepareMailerTest(t)
comment.Poster = doer
defer mockMailTemplates("repo/issue/comment", subjectTpl, bodyTpl)()
mails := 0
defer test.MockVariableValue(&SendAsync, func(msgs ...*sender_service.Message) {
mails = len(msgs)
})()
err := MailParticipantsComment(t.Context(), comment, activities_model.ActionCommentIssue, issue, []*user_model.User{})
require.NoError(t, err)
assert.Equal(t, 3, mails)
}
func TestComposeIssueMessage(t *testing.T) {
doer, _, issue, _ := prepareMailerTest(t)
defer mockMailTemplates("repo/issue/new", subjectTpl, bodyTpl)()
recipients := []*user_model.User{{Name: "Test", Email: "[email protected]"}, {Name: "Test2", Email: "[email protected]"}}
msgs, err := composeIssueCommentMessages(t.Context(), &mailComment{
Issue: issue, Doer: doer, ActionType: activities_model.ActionCreateIssue,
Content: "test body",
}, "en-US", recipients, false, "issue create")
assert.NoError(t, err)
assert.Len(t, msgs, 2)
gomailMsg := msgs[0].ToMessage()
mailto := gomailMsg.GetAddrHeader("To")
subject := gomailMsg.GetGenHeader("Subject")
messageID := gomailMsg.GetGenHeader("Message-ID")
inReplyTo := gomailMsg.GetGenHeader("In-Reply-To")
references := gomailMsg.GetGenHeader("References")
assert.Len(t, mailto, 1, "exactly one recipient is expected in the To field")
assert.Equal(t, "[user2/repo1] @user2 #1 - issue1", subject[0])
assert.Equal(t, "<user2/repo1/issues/1@localhost>", inReplyTo[0], "In-Reply-To header doesn't match")
assert.Equal(t, "<user2/repo1/issues/1@localhost>", references[0], "References header doesn't match")
assert.Equal(t, "<user2/repo1/issues/1@localhost>", messageID[0], "Message-ID header doesn't match")
assert.Empty(t, gomailMsg.GetGenHeader("List-Post")) // incoming mail feature disabled
assert.Len(t, gomailMsg.GetGenHeader("List-Unsubscribe"), 1) // url without mailto
}
func TestTemplateSelection(t *testing.T) {
doer, repo, issue, comment := prepareMailerTest(t)
recipients := []*user_model.User{{Name: "Test", Email: "[email protected]"}}
defer mockMailTemplates("repo/issue/default", "repo/issue/default/subject", "repo/issue/default/body")()
defer mockMailTemplates("repo/issue/new", "repo/issue/new/subject", "repo/issue/new/body")()
defer mockMailTemplates("repo/pull/comment", "repo/pull/comment/subject", "repo/pull/comment/body")()
defer mockMailTemplates("repo/issue/close", "", "repo/issue/close/body")() // Must default to a fallback subject
expect := func(t *testing.T, msg *sender_service.Message, expSubject, expBody string) {
subject := msg.ToMessage().GetGenHeader("Subject")
msgbuf := new(bytes.Buffer)
_, _ = msg.ToMessage().WriteTo(msgbuf)
wholemsg := msgbuf.String()
assert.Equal(t, []string{expSubject}, subject)
assert.Contains(t, wholemsg, expBody)
}
msg := testComposeIssueCommentMessage(t, &mailComment{
Issue: issue, Doer: doer, ActionType: activities_model.ActionCreateIssue,
Content: "test body",
}, recipients, false, "TestTemplateSelection")
expect(t, msg, "repo/issue/new/subject", "repo/issue/new/body")
msg = testComposeIssueCommentMessage(t, &mailComment{
Issue: issue, Doer: doer, ActionType: activities_model.ActionCommentIssue,
Content: "test body", Comment: comment,
}, recipients, false, "TestTemplateSelection")
expect(t, msg, "repo/issue/default/subject", "repo/issue/default/body")
pull := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 2, Repo: repo, Poster: doer})
comment = unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{ID: 4, Issue: pull})
msg = testComposeIssueCommentMessage(t, &mailComment{
Issue: pull, Doer: doer, ActionType: activities_model.ActionCommentPull,
Content: "test body", Comment: comment,
}, recipients, false, "TestTemplateSelection")
expect(t, msg, "repo/pull/comment/subject", "repo/pull/comment/body")
msg = testComposeIssueCommentMessage(t, &mailComment{
Issue: issue, Doer: doer, ActionType: activities_model.ActionCloseIssue,
Content: "test body", Comment: comment,
}, recipients, false, "TestTemplateSelection")
expect(t, msg, "Re: [user2/repo1] issue1 (#1)", "repo/issue/close/body")
}
func TestTemplateServices(t *testing.T) {
doer, _, issue, comment := prepareMailerTest(t)
assert.NoError(t, issue.LoadRepo(t.Context()))
expect := func(t *testing.T, issue *issues_model.Issue, comment *issues_model.Comment, doer *user_model.User,
actionType activities_model.ActionType, fromMention bool, tplSubject, tplBody, expSubject, expBody string,
) {
defer mockMailTemplates("repo/issue/default", tplSubject, tplBody)()
recipients := []*user_model.User{{Name: "Test", Email: "[email protected]"}}
msg := testComposeIssueCommentMessage(t, &mailComment{
Issue: issue, Doer: doer, ActionType: actionType,
Content: "test body", Comment: comment,
}, recipients, fromMention, "TestTemplateServices")
subject := msg.ToMessage().GetGenHeader("Subject")
msgbuf := new(bytes.Buffer)
_, _ = msg.ToMessage().WriteTo(msgbuf)
wholemsg := msgbuf.String()
assert.Equal(t, []string{expSubject}, subject)
assert.Contains(t, wholemsg, "\r\n"+expBody+"\r\n")
}
expect(t, issue, comment, doer, activities_model.ActionCommentIssue, false,
"{{.SubjectPrefix}}[{{.Repo}}]: @{{.Doer.Name}} commented on #{{.Issue.Index}} - {{.Issue.Title}}",
"//{{.ActionType}},{{.ActionName}},{{if .IsMention}}norender{{end}}//",
"Re: [user2/repo1]: @user2 commented on #1 - issue1",
"//issue,comment,//")
expect(t, issue, comment, doer, activities_model.ActionCommentIssue, true,
"{{if .IsMention}}must render{{end}}",
"//subject is: {{.Subject}}//",
"must render",
"//subject is: must render//")
expect(t, issue, comment, doer, activities_model.ActionCommentIssue, true,
"{{.FallbackSubject}}",
"//{{.SubjectPrefix}}//",
"Re: [user2/repo1] issue1 (#1)",
"//Re: //")
}
func testComposeIssueCommentMessage(t *testing.T, ctx *mailComment, recipients []*user_model.User, fromMention bool, info string) *sender_service.Message {
msgs, err := composeIssueCommentMessages(t.Context(), ctx, "en-US", recipients, fromMention, info)
assert.NoError(t, err)
assert.Len(t, msgs, 1)
return msgs[0]
}
func TestGenerateAdditionalHeadersForIssue(t *testing.T) {
doer, _, issue, _ := prepareMailerTest(t)
comment := &mailComment{Issue: issue, Doer: doer}
recipient := &user_model.User{Name: "test", Email: "[email protected]"}
headers := generateAdditionalHeadersForIssue(t.Context(), comment, "dummy-reason", recipient)
expected := map[string]string{
"List-ID": "user2/repo1 <repo1.user2.localhost>",
"List-Archive": "<https://forge.example/user2/repo1>",
"X-Git-Reason": "dummy-reason",
"X-Git-Sender": "user2",
"X-Git-Recipient": "test",
"X-Git-Recipient-Address": "[email protected]",
"X-Git-Repository": "repo1",
"X-Git-Repository-Path": "user2/repo1",
"X-Git-Repository-Link": "https://forge.example/user2/repo1",
"X-Git-Issue-ID": "1",
"X-Git-Issue-Link": "https://forge.example/user2/repo1/issues/1",
}
for key, value := range expected {
if assert.Contains(t, headers, key) {
assert.Equal(t, value, headers[key])
}
}
}
func TestGenerateMessageIDForIssue(t *testing.T) {
_, _, issue, comment := prepareMailerTest(t)
_, _, pullIssue, _ := prepareMailerTest(t)
pullIssue.IsPull = true
type args struct {
issue *issues_model.Issue
comment *issues_model.Comment
actionType activities_model.ActionType
}
tests := []struct {
name string
args args
prefix string
}{
{
name: "Open Issue",
args: args{
issue: issue,
actionType: activities_model.ActionCreateIssue,
},
prefix: fmt.Sprintf("<%s/issues/%d@%s>", issue.Repo.FullName(), issue.Index, setting.Domain),
},
{
name: "Open Pull",
args: args{
issue: pullIssue,
actionType: activities_model.ActionCreatePullRequest,
},
prefix: fmt.Sprintf("<%s/pulls/%d@%s>", issue.Repo.FullName(), issue.Index, setting.Domain),
},
{
name: "Comment Issue",
args: args{
issue: issue,
comment: comment,
actionType: activities_model.ActionCommentIssue,
},
prefix: fmt.Sprintf("<%s/issues/%d/comment/%d@%s>", issue.Repo.FullName(), issue.Index, comment.ID, setting.Domain),
},
{
name: "Comment Pull",
args: args{
issue: pullIssue,
comment: comment,
actionType: activities_model.ActionCommentPull,
},
prefix: fmt.Sprintf("<%s/pulls/%d/comment/%d@%s>", issue.Repo.FullName(), issue.Index, comment.ID, setting.Domain),
},
{
name: "Close Issue",
args: args{
issue: issue,
actionType: activities_model.ActionCloseIssue,
},
prefix: fmt.Sprintf("<%s/issues/%d/close/", issue.Repo.FullName(), issue.Index),
},
{
name: "Close Pull",
args: args{
issue: pullIssue,
actionType: activities_model.ActionClosePullRequest,
},
prefix: fmt.Sprintf("<%s/pulls/%d/close/", issue.Repo.FullName(), issue.Index),
},
{
name: "Reopen Issue",
args: args{
issue: issue,
actionType: activities_model.ActionReopenIssue,
},
prefix: fmt.Sprintf("<%s/issues/%d/reopen/", issue.Repo.FullName(), issue.Index),
},
{
name: "Reopen Pull",
args: args{
issue: pullIssue,
actionType: activities_model.ActionReopenPullRequest,
},
prefix: fmt.Sprintf("<%s/pulls/%d/reopen/", issue.Repo.FullName(), issue.Index),
},
{
name: "Merge Pull",
args: args{
issue: pullIssue,
actionType: activities_model.ActionMergePullRequest,
},
prefix: fmt.Sprintf("<%s/pulls/%d/merge/", issue.Repo.FullName(), issue.Index),
},
{
name: "Ready Pull",
args: args{
issue: pullIssue,
actionType: activities_model.ActionPullRequestReadyForReview,
},
prefix: fmt.Sprintf("<%s/pulls/%d/ready/", issue.Repo.FullName(), issue.Index),
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := generateMessageIDForIssue(tt.args.issue, tt.args.comment, tt.args.actionType)
assert.True(t, strings.HasPrefix(got, tt.prefix), "%v, want %v", got, tt.prefix)
})
}
}
func TestGenerateMessageIDForRelease(t *testing.T) {
msgID := generateMessageIDForRelease(&repo_model.Release{
ID: 1,
Repo: &repo_model.Repository{OwnerName: "owner", Name: "repo"},
})
assert.Equal(t, "<owner/repo/releases/1@localhost>", msgID)
}
func TestGenerateMessageIDForActionsWorkflowRunStatusEmail(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 2})
run := unittest.AssertExistsAndLoadBean(t, &actions_model.ActionRun{ID: 795, RepoID: repo.ID})
assert.NoError(t, run.LoadAttributes(t.Context()))
msgID := generateMessageIDForActionsWorkflowRunStatusEmail(repo, run)
assert.Equal(t, "<user2/repo2/actions/runs/191@localhost>", msgID)
}
func TestFromDisplayName(t *testing.T) {
tmpl, err := texttmpl.New("mailFrom").Parse("{{ .DisplayName }}")
assert.NoError(t, err)
setting.MailService = &setting.Mailer{FromDisplayNameFormatTemplate: tmpl}
defer func() { setting.MailService = nil }()
tests := []struct {
userDisplayName string
fromDisplayName string
}{{
userDisplayName: "test",
fromDisplayName: "test",
}, {
userDisplayName: "Hi Its <Mee>",
fromDisplayName: "Hi Its <Mee>",
}, {
userDisplayName: "Æsir",
fromDisplayName: "=?utf-8?q?=C3=86sir?=",
}, {
userDisplayName: "new😀user",
fromDisplayName: "=?utf-8?q?new=F0=9F=98=80user?=",
}}
for _, tc := range tests {
t.Run(tc.userDisplayName, func(t *testing.T) {
user := &user_model.User{FullName: tc.userDisplayName, Name: "tmp"}
got := fromDisplayName(user)
assert.Equal(t, tc.fromDisplayName, got)
})
}
t.Run("template with all available vars", func(t *testing.T) {
tmpl, err = texttmpl.New("mailFrom").Parse("{{ .DisplayName }} (by {{ .AppName }} on [{{ .Domain }}])")
assert.NoError(t, err)
setting.MailService = &setting.Mailer{FromDisplayNameFormatTemplate: tmpl}
oldAppName := setting.AppName
setting.AppName = "Code IT"
oldDomain := setting.Domain
setting.Domain = "code.it"
defer func() {
setting.AppName = oldAppName
setting.Domain = oldDomain
}()
assert.Equal(t, "Mister X (by Code IT on [code.it])", fromDisplayName(&user_model.User{FullName: "Mister X", Name: "tmp"}))
})
}
func TestEmbedBase64Images(t *testing.T) {
user, repo, issue, att1, att2 := prepareMailerBase64Test(t)
// comment := &mailComment{Issue: issue, Doer: user}
imgExternalURL := "https://via.placeholder.com/10"
imgExternalImg := fmt.Sprintf(`<img src="%s"/>`, imgExternalURL)
att1URL := setting.AppURL + repo.Owner.Name + "/" + repo.Name + "/attachments/" + att1.UUID
att1Img := fmt.Sprintf(`<img src="%s"/>`, att1URL)
att1Base64 := "data:image/png;base64,iVBORw0KGgo="
att1ImgBase64 := fmt.Sprintf(`<img src="%s"/>`, att1Base64)
att2URL := setting.AppURL + repo.Owner.Name + "/" + repo.Name + "/attachments/" + att2.UUID
att2Img := fmt.Sprintf(`<img src="%s"/>`, att2URL)
att2File, err := storage.Attachments.Open(att2.RelativePath())
require.NoError(t, err)
defer att2File.Close()
att2Bytes, err := io.ReadAll(att2File)
require.NoError(t, err)
require.Greater(t, len(att2Bytes), 1024)
att2Base64 := "data:image/png;base64," + base64.StdEncoding.EncodeToString(att2Bytes)
att2ImgBase64 := fmt.Sprintf(`<img src="%s"/>`, att2Base64)
t.Run("ComposeMessage", func(t *testing.T) {
defer mockMailTemplates("repo/issue/new", subjectTpl, bodyTpl)()
issue.Content = fmt.Sprintf(`MSG-BEFORE <image src="attachments/%s"> MSG-AFTER`, att1.UUID)
require.NoError(t, issues_model.UpdateIssueCols(t.Context(), issue, "content"))
recipients := []*user_model.User{{Name: "Test", Email: "[email protected]"}}
msgs, err := composeIssueCommentMessages(t.Context(), &mailComment{
Issue: issue,
Doer: user,
ActionType: activities_model.ActionCreateIssue,
Content: issue.Content,
}, "en-US", recipients, false, "issue create")
require.NoError(t, err)
mailBody := msgs[0].Body
assert.Regexp(t, `MSG-BEFORE <a[^>]+><img src="data:image/png;base64,iVBORw0KGgo=".*/></a> MSG-AFTER`, mailBody)
})
t.Run("EmbedInstanceImageSkipExternalImage", func(t *testing.T) {
mailBody := "<html><head></head><body><p>Test1</p>" + imgExternalImg + "<p>Test2</p>" + att1Img + "<p>Test3</p></body></html>"
expectedMailBody := "<html><head></head><body><p>Test1</p>" + imgExternalImg + "<p>Test2</p>" + att1ImgBase64 + "<p>Test3</p></body></html>"
b64embedder := newMailAttachmentBase64Embedder(user, repo, 1024)
resultMailBody, err := b64embedder.Base64InlineImages(t.Context(), template.HTML(mailBody))
require.NoError(t, err)
assert.Equal(t, expectedMailBody, string(resultMailBody))
})
t.Run("LimitedEmailBodySize", func(t *testing.T) {
mailBody := fmt.Sprintf("<html><head></head><body>%s%s</body></html>", att1Img, att2Img)
b64embedder := newMailAttachmentBase64Embedder(user, repo, 1024)
resultMailBody, err := b64embedder.Base64InlineImages(t.Context(), template.HTML(mailBody))
require.NoError(t, err)
expected := fmt.Sprintf("<html><head></head><body>%s%s</body></html>", att1ImgBase64, att2Img)
assert.Equal(t, expected, string(resultMailBody))
b64embedder = newMailAttachmentBase64Embedder(user, repo, 4096)
resultMailBody, err = b64embedder.Base64InlineImages(t.Context(), template.HTML(mailBody))
require.NoError(t, err)
expected = fmt.Sprintf("<html><head></head><body>%s%s</body></html>", att1ImgBase64, att2ImgBase64)
assert.Equal(t, expected, string(resultMailBody))
})
}
func TestMailPullRequestPush(t *testing.T) {
doer, _, issue, comment := prepareMailerTest(t)
mc := &mailComment{
Issue: issue,
Comment: comment,
Doer: doer,
}
issue.IsPull = true
issue.PullRequest = &issues_model.PullRequest{BaseRepo: mc.Issue.Repo}
mc.Comment.Type = issues_model.CommentTypePullRequestPush
mc.Comment.Commits = []*git_model.SignCommitWithStatuses{
{
SignCommit: &asymkey.SignCommit{
UserCommit: &gituser.UserCommit{
GitCommit: &git.Commit{
CommitMessage: git.CommitMessage{MessageRaw: "test commit msg"},
ID: git.Sha1ObjectFormat.EmptyObjectID(),
},
},
},
},
}
msgs, err := composeIssueCommentMessages(t.Context(), mc, "mock", []*user_model.User{{Name: "Test", Email: "[email protected]"}}, false, "pull request push")
require.NoError(t, err)
assert.Contains(t, msgs[0].Body, `<a href="https://forge.example/user2/repo1/commit/0000000000000000000000000000000000000000">0000000000</a> - test commit msg`)
assert.Contains(t, msgs[0].Body, `</html>`)
}