Skip to content

Commit 49c1522

Browse files
committed
tests, new attachment gitea.png in test_data
1 parent 37428d3 commit 49c1522

File tree

6 files changed

+101
-13
lines changed

6 files changed

+101
-13
lines changed

models/fixtures/attachment.yml

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,3 +153,16 @@
153153
download_count: 0
154154
size: 0
155155
created_unix: 946684800
156+
157+
-
158+
id: 13
159+
uuid: 1b267670-1793-4cd0-abc1-449269b7cff9
160+
repo_id: 1
161+
issue_id: 1
162+
release_id: 0
163+
uploader_id: 2
164+
comment_id: 0
165+
name: gitea.png
166+
download_count: 0
167+
size: 1458
168+
created_unix: 946684800

services/mailer/mail.go

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -199,7 +199,7 @@ func SendCollaboratorMail(u, doer *user_model.User, repo *repo_model.Repository)
199199
SendAsync(msg)
200200
}
201201

202-
func composeIssueCommentMessages(ctx *mailCommentContext, lang string, recipients []*user_model.User, fromMention bool, info string) ([]*Message, error) {
202+
func composeIssueCommentMessages(ctx *MailCommentContext, lang string, recipients []*user_model.User, fromMention bool, info string) ([]*Message, error) {
203203
var (
204204
subject string
205205
link string
@@ -238,7 +238,7 @@ func composeIssueCommentMessages(ctx *mailCommentContext, lang string, recipient
238238

239239
if setting.MailService.Base64EmbedImages {
240240
bodyStr := string(body)
241-
bodyStr, err = inlineImages(bodyStr, ctx)
241+
bodyStr, err = Base64InlineImages(bodyStr, ctx)
242242
if err != nil {
243243
return nil, err
244244
}
@@ -376,7 +376,7 @@ func composeIssueCommentMessages(ctx *mailCommentContext, lang string, recipient
376376
return msgs, nil
377377
}
378378

379-
func inlineImages(body string, ctx *mailCommentContext) (string, error) {
379+
func Base64InlineImages(body string, ctx *MailCommentContext) (string, error) {
380380
doc, err := html.Parse(strings.NewReader(body))
381381
if err != nil {
382382
log.Error("Failed to parse HTML body: %v", err)
@@ -390,7 +390,7 @@ func inlineImages(body string, ctx *mailCommentContext) (string, error) {
390390
for i, attr := range n.Attr {
391391
if attr.Key == "src" {
392392
attachmentPath := attr.Val
393-
dataURI, err := attachmentSrcToDataURI(attachmentPath, ctx)
393+
dataURI, err := AttachmentSrcToBase64DataURI(attachmentPath, ctx)
394394
if err != nil {
395395
log.Trace("attachmentSrcToDataURI not possible: %v", err) // Not an error, just skip. This is probably an image from outside the gitea instance.
396396
continue
@@ -418,7 +418,10 @@ func inlineImages(body string, ctx *mailCommentContext) (string, error) {
418418
return buf.String(), nil
419419
}
420420

421-
func attachmentSrcToDataURI(attachmentPath string, ctx *mailCommentContext) (string, error) {
421+
func AttachmentSrcToBase64DataURI(attachmentPath string, ctx *MailCommentContext) (string, error) {
422+
if !strings.HasPrefix(attachmentPath, setting.AppURL) { // external image
423+
return "", fmt.Errorf("external image")
424+
}
422425
parts := strings.Split(attachmentPath, "/attachments/")
423426
if len(parts) <= 1 {
424427
return "", fmt.Errorf("invalid attachment path: %s", attachmentPath)
@@ -479,7 +482,7 @@ func generateMessageIDForRelease(release *repo_model.Release) string {
479482
return fmt.Sprintf("<%s/releases/%d@%s>", release.Repo.FullName(), release.ID, setting.Domain)
480483
}
481484

482-
func generateAdditionalHeaders(ctx *mailCommentContext, reason string, recipient *user_model.User) map[string]string {
485+
func generateAdditionalHeaders(ctx *MailCommentContext, reason string, recipient *user_model.User) map[string]string {
483486
repo := ctx.Issue.Repo
484487

485488
return map[string]string{
@@ -543,7 +546,7 @@ func SendIssueAssignedMail(ctx context.Context, issue *issues_model.Issue, doer
543546
}
544547

545548
for lang, tos := range langMap {
546-
msgs, err := composeIssueCommentMessages(&mailCommentContext{
549+
msgs, err := composeIssueCommentMessages(&MailCommentContext{
547550
Context: ctx,
548551
Issue: issue,
549552
Doer: doer,

services/mailer/mail_comment.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ func MailParticipantsComment(ctx context.Context, c *issues_model.Comment, opTyp
2626
content = ""
2727
}
2828
if err := mailIssueCommentToParticipants(
29-
&mailCommentContext{
29+
&MailCommentContext{
3030
Context: ctx,
3131
Issue: issue,
3232
Doer: c.Poster,
@@ -49,7 +49,7 @@ func MailMentionsComment(ctx context.Context, pr *issues_model.PullRequest, c *i
4949
visited := make(container.Set[int64], len(mentions)+1)
5050
visited.Add(c.Poster.ID)
5151
if err = mailIssueCommentBatch(
52-
&mailCommentContext{
52+
&MailCommentContext{
5353
Context: ctx,
5454
Issue: pr.Issue,
5555
Doer: c.Poster,

services/mailer/mail_issue.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ func fallbackMailSubject(issue *issues_model.Issue) string {
2222
return fmt.Sprintf("[%s] %s (#%d)", issue.Repo.FullName(), issue.Title, issue.Index)
2323
}
2424

25-
type mailCommentContext struct {
25+
type MailCommentContext struct {
2626
context.Context
2727
Issue *issues_model.Issue
2828
Doer *user_model.User
@@ -41,7 +41,7 @@ const (
4141
// This function sends two list of emails:
4242
// 1. Repository watchers (except for WIP pull requests) and users who are participated in comments.
4343
// 2. Users who are not in 1. but get mentioned in current issue/comment.
44-
func mailIssueCommentToParticipants(ctx *mailCommentContext, mentions []*user_model.User) error {
44+
func mailIssueCommentToParticipants(ctx *MailCommentContext, mentions []*user_model.User) error {
4545
// Required by the mail composer; make sure to load these before calling the async function
4646
if err := ctx.Issue.LoadRepo(ctx); err != nil {
4747
return fmt.Errorf("LoadRepo: %w", err)
@@ -120,7 +120,7 @@ func mailIssueCommentToParticipants(ctx *mailCommentContext, mentions []*user_mo
120120
return nil
121121
}
122122

123-
func mailIssueCommentBatch(ctx *mailCommentContext, users []*user_model.User, visited container.Set[int64], fromMention bool) error {
123+
func mailIssueCommentBatch(ctx *MailCommentContext, users []*user_model.User, visited container.Set[int64], fromMention bool) error {
124124
checkUnit := unit.TypeIssues
125125
if ctx.Issue.IsPull {
126126
checkUnit = unit.TypePullRequests
@@ -186,7 +186,7 @@ func MailParticipants(ctx context.Context, issue *issues_model.Issue, doer *user
186186
}
187187
forceDoerNotification := opType == activities_model.ActionAutoMergePullRequest
188188
if err := mailIssueCommentToParticipants(
189-
&mailCommentContext{
189+
&MailCommentContext{
190190
Context: ctx,
191191
Issue: issue,
192192
Doer: doer,
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
// Copyright 2023 The Gitea Authors. All rights reserved.
2+
// SPDX-License-Identifier: MIT
3+
4+
package integration
5+
6+
import (
7+
"context"
8+
"testing"
9+
10+
issues_model "code.gitea.io/gitea/models/issues"
11+
attachment_model "code.gitea.io/gitea/models/repo"
12+
repo_model "code.gitea.io/gitea/models/repo"
13+
"code.gitea.io/gitea/models/unittest"
14+
user_model "code.gitea.io/gitea/models/user"
15+
"code.gitea.io/gitea/modules/setting"
16+
mail "code.gitea.io/gitea/services/mailer"
17+
"code.gitea.io/gitea/tests"
18+
19+
"github.com/stretchr/testify/assert"
20+
)
21+
22+
func TestEmailEmbedB64Images(t *testing.T) {
23+
defer tests.PrepareTestEnv(t)()
24+
tests.PrepareAttachmentsStorage(t)
25+
26+
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
27+
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1, Owner: user})
28+
issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 1, Repo: repo, Poster: user})
29+
30+
attachment := unittest.AssertExistsAndLoadBean(t, &attachment_model.Attachment{ID: 13, IssueID: issue.ID, RepoID: repo.ID})
31+
ctx0 := context.Background()
32+
33+
ctx := &mail.MailCommentContext{Context: ctx0 /* TODO: use a correct context */, Issue: issue, Doer: user}
34+
35+
img1ExternalUrl := "https://via.placeholder.com/10"
36+
img1ExternalImg := "<img src=\"" + img1ExternalUrl + "\"/>"
37+
38+
img2InternalUrl := setting.AppURL + repo.Owner.Name + "/" + repo.Name + "/attachments/" + attachment.UUID
39+
img2InternalImg := "<img src=\"" + img2InternalUrl + "\"/>"
40+
img2InternalB64 := "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAAAxCAYAAABNuS5SAAAAAXNSR0IArs4c6QAABWxJREFUaEPVmm1sU1UYx/9n3RsuxmS+ECdC71YYbCjohmMDNxyJONBNxaIo29qNlZqYbTFhi0FkmZpgogH0w1g7t46ZmEA0KIl+MQsvwQSM8QOJSGDtFO0XFTQE0tG11/Rqa9fel3Pf7+6H7cN9zjnP8zv/53nOvbcElFe7n/mOsKimNAfAfpWTm3NgtCP4Df0Ya1q2DpXdl2OL7yYEbrC4O+FlwBMiif/cH6Grc4RZEotj2lphsddAyB9gSbSlqvvvZx/tqQCQ5x4pBVgUGelrAqIgQJePYY10Rslam1d7w841fSWJsW5/qZIpVI0hBM5sgCyIy8/EVc1s0OCmh3eFt9X0mwYwK4V3+h2LZtnYVYPiV73MUw91hV9c+4Z1AM6HtE2nvmllR/il2jetAZAGHgu8M+4J7U0PgmacaqkJTPDkSvdv22v3PmBWDUylsMtnPwiQHqFAky2b776ZADdWtv26o25gkQUACndcMXgJx80E+MSKV35pW//2YssClIJnNsCG5dt/dj3+7hJTAbp8zAyAfL70tDrA+vJt0+76/XazAfIemGngma3A9eXOYGf9e9wJ2oyDNNdEhGoYPUB7P0D269VpxeatW/r8VNeG98vmNUAzVVhb1nLF03jAYRZAkhurVK1AowDeteBeRGZvYSZ6MyXKmtJnLns3HlpqFkDuZYLaFDYC4FhXMCuTkzUvec+MGjgvAPLBS9IcmuzB+akTZpRfbk3tAPqZU2BRr0ckYgDTVWg5BSYJ00LR44lEDN7E2X2Y/HECvo6LyLMVpNw0EOTtgCdUQDzDJXfcJgX/V+Y0YrRHGb3qIK36+DZZb5BzXukLqYeNo2HcGzpNo0KtFSgELx3MBy+fRXHR/aLu6QWSCqCZacwPkIXbz52bU5eYStPtOkcciLPavWjPBHgSQIPQVtKmslYqFIJy5tIxjJ7un+NmY0UbWtcN0CQJZ6OVIrO+ykkFTwNRag7aKGlqX+ZcK0rq0LflE6ol1EJMZ5H6qNQ+zNwiBAvEPDACohi8q39ewlufN6F13SAaK3ZwroZ+v4DB4y2yUloXgPSdlBwKeIK9QqDVqpBGfXw2gTN7cOqnTzm3pOqibgDpIf6Lj0+RagCKBX4jcg3dE9XYvMoL52N9vPt3I3Id3RNVogC1hJdwIuu7sPMobEV/MbNUxWSu0UzAEyqUuwm0HTXz2VfIv4Rd76ZRrFq8gddEd4CJVZs+dBQsLIxFFEBUPIQm7WrKmuFtPCi4hhRktfD4sk70tzFq0lEuSaW1L30dvQHylSxRgGrSUQ5AmqcOx8Iq7Gk+Jjjtzo+XIRafFax/eqiPtwbq1V3FgNIAlOqueqtPqGlKKpAvcK1TWy3Aw5O9ODf1JQa3fo0Hi8uzXNZLfbIUKKzM0jUAe15OymbaqgVolvo0ASgGTo5ShSDG2RhyiI3qSCL26l/NBos9gSlKYVpn5ABMzil1nOHrumJj9Uxf3RWopovTgMyEkzlGC3gkl1071jF9Tkg0uipQDUAaRb52pAo3Z65zpmakryEK1AKi1BGGTx1aqO/OiK3wo+4rid8OCV66K5ADOGZfjSj5gbZ2itkNuS6gME/6x/haAKR5fWcIQK1UmA5233MnYL+nkqo7K9k4GniGpXAyACVdWSr4rdW78fQjr6bMvr18HP6Tr0sNk7xvSYB6KFGShAIDWniGKzCxYLuPaSbAFwriMmSIHHimAPxPhTEAOYYQkbGIPRyyDQxA1rdPw5pIZhwuH2MpiCzIC+Oe4GcyeHOmpgHklOhnjoKFU67TWtvns8X5vl3fR5XMaypAPbszLQy5NS9zXksA5NQ4vGw5SPQibeBq7dSCS67/D+Q9UQwzW88cAAAAAElFTkSuQmCC"
41+
img2InternalB64Img := "<img src=\"" + img2InternalB64 + "\"/>"
42+
43+
// 1st Test: convert internal image to base64
44+
t.Run("replaceSpecifiedB64ImagesInternal", func(t *testing.T) {
45+
defer tests.PrintCurrentTest(t)()
46+
47+
resultImg1Internal, err := mail.AttachmentSrcToBase64DataURI(img2InternalUrl, ctx)
48+
assert.NoError(t, err)
49+
assert.Equal(t, img2InternalB64, resultImg1Internal) // replace cause internal image
50+
})
51+
52+
// 2nd Test: convert external image to base64 -> abort cause external image
53+
t.Run("replaceSpecifiedB64ImagesExternal", func(t *testing.T) {
54+
defer tests.PrintCurrentTest(t)()
55+
56+
resultImg1External, err := mail.AttachmentSrcToBase64DataURI(img1ExternalUrl, ctx)
57+
assert.Error(t, err)
58+
assert.Equal(t, "", resultImg1External) // don't replace cause external image
59+
})
60+
61+
// 3rd Test: generate email body with 1 internal and 1 external image, expect the result to have the internal image replaced with base64 data and the external not replaced
62+
t.Run("generateEmailBody", func(t *testing.T) {
63+
defer tests.PrintCurrentTest(t)()
64+
65+
mailBody := "<html><head></head><body><p>Test1</p>" + img1ExternalImg + "<p>Test2</p>" + img2InternalImg + "<p>Test3</p></body></html>"
66+
expectedMailBody := "<html><head></head><body><p>Test1</p>" + img1ExternalImg + "<p>Test2</p>" + img2InternalB64Img + "<p>Test3</p></body></html>"
67+
resultMailBody, err := mail.Base64InlineImages(mailBody, ctx)
68+
69+
assert.NoError(t, err)
70+
assert.Equal(t, expectedMailBody, resultMailBody)
71+
})
72+
}
Binary file not shown.

0 commit comments

Comments
 (0)