aboutsummaryrefslogtreecommitdiffstats
path: root/services/mailer/mail_test.go
diff options
context:
space:
mode:
authorKN4CK3R <admin@oldschoolhack.me>2023-01-14 16:57:10 +0100
committerGitHub <noreply@github.com>2023-01-14 23:57:10 +0800
commitfc037b4b825f0501a1489e10d7c822435d825cb7 (patch)
tree551590b5ec197d8efca8b7bc3a9acc5961637d9d /services/mailer/mail_test.go
parent20e3ffd2085d7066b3206809dfae7b6ebd59cb5d (diff)
downloadgitea-fc037b4b825f0501a1489e10d7c822435d825cb7.tar.gz
gitea-fc037b4b825f0501a1489e10d7c822435d825cb7.zip
Add support for incoming emails (#22056)
closes #13585 fixes #9067 fixes #2386 ref #6226 ref #6219 fixes #745 This PR adds support to process incoming emails to perform actions. Currently I added handling of replies and unsubscribing from issues/pulls. In contrast to #13585 the IMAP IDLE command is used instead of polling which results (in my opinion 😉) in cleaner code. Procedure: - When sending an issue/pull reply email, a token is generated which is present in the Reply-To and References header. - IMAP IDLE waits until a new email arrives - The token tells which action should be performed A possible signature and/or reply gets stripped from the content. I added a new service to the drone pipeline to test the receiving of incoming mails. If we keep this in, we may test our outgoing emails too in future. Co-authored-by: silverwind <me@silverwind.io> Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
Diffstat (limited to 'services/mailer/mail_test.go')
-rw-r--r--services/mailer/mail_test.go57
1 files changed, 30 insertions, 27 deletions
diff --git a/services/mailer/mail_test.go b/services/mailer/mail_test.go
index 6ed4fed9bd..64f2f740ca 100644
--- a/services/mailer/mail_test.go
+++ b/services/mailer/mail_test.go
@@ -8,6 +8,7 @@ import (
"context"
"fmt"
"html/template"
+ "regexp"
"strings"
"testing"
texttmpl "text/template"
@@ -66,6 +67,9 @@ func prepareMailerTest(t *testing.T) (doer *user_model.User, repo *repo_model.Re
func TestComposeIssueCommentMessage(t *testing.T) {
doer, _, issue, comment := prepareMailerTest(t)
+ setting.IncomingEmail.Enabled = true
+ defer func() { setting.IncomingEmail.Enabled = false }()
+
subjectTemplates = texttmpl.Must(texttmpl.New("issue/comment").Parse(subjectTpl))
bodyTemplates = template.Must(template.New("issue/comment").Parse(bodyTpl))
@@ -78,18 +82,20 @@ func TestComposeIssueCommentMessage(t *testing.T) {
assert.NoError(t, err)
assert.Len(t, msgs, 2)
gomailMsg := msgs[0].ToMessage()
- mailto := gomailMsg.GetHeader("To")
- subject := gomailMsg.GetHeader("Subject")
- messageID := gomailMsg.GetHeader("Message-ID")
- inReplyTo := gomailMsg.GetHeader("In-Reply-To")
- references := gomailMsg.GetHeader("References")
-
- assert.Len(t, mailto, 1, "exactly one recipient is expected in the To field")
- assert.Equal(t, "Re: ", subject[0][:4], "Comment reply subject should contain Re:")
- assert.Equal(t, "Re: [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/comment/2@localhost>", messageID[0], "Message-ID header doesn't match")
+ replyTo := gomailMsg.GetHeader("Reply-To")[0]
+ subject := gomailMsg.GetHeader("Subject")[0]
+
+ assert.Len(t, gomailMsg.GetHeader("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.GetHeader("In-Reply-To")[0], "In-Reply-To header doesn't match")
+ assert.ElementsMatch(t, []string{"<user2/repo1/issues/1@localhost>", "<reply-" + token + "@localhost>"}, gomailMsg.GetHeader("References"), "References header doesn't match")
+ assert.Equal(t, "<user2/repo1/issues/1/comment/2@localhost>", gomailMsg.GetHeader("Message-ID")[0], "Message-ID header doesn't match")
+ assert.Equal(t, "<mailto:"+replyTo+">", gomailMsg.GetHeader("List-Post")[0])
+ assert.Len(t, gomailMsg.GetHeader("List-Unsubscribe"), 2) // url + mailto
}
func TestComposeIssueMessage(t *testing.T) {
@@ -119,6 +125,8 @@ func TestComposeIssueMessage(t *testing.T) {
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.GetHeader("List-Post")) // incoming mail feature disabled
+ assert.Len(t, gomailMsg.GetHeader("List-Unsubscribe"), 1) // url without mailto
}
func TestTemplateSelection(t *testing.T) {
@@ -238,7 +246,6 @@ func TestGenerateAdditionalHeaders(t *testing.T) {
expected := map[string]string{
"List-ID": "user2/repo1 <repo1.user2.localhost>",
"List-Archive": "<https://try.gitea.io/user2/repo1>",
- "List-Unsubscribe": "https://try.gitea.io/user2/repo1/issues/1",
"X-Gitea-Reason": "dummy-reason",
"X-Gitea-Sender": "< U<se>r Tw<o > ><",
"X-Gitea-Recipient": "Test",
@@ -271,7 +278,6 @@ func Test_createReference(t *testing.T) {
name string
args args
prefix string
- suffix string
}{
{
name: "Open Issue",
@@ -279,7 +285,7 @@ func Test_createReference(t *testing.T) {
issue: issue,
actionType: activities_model.ActionCreateIssue,
},
- prefix: fmt.Sprintf("%s/issues/%d@%s", issue.Repo.FullName(), issue.Index, setting.Domain),
+ prefix: fmt.Sprintf("<%s/issues/%d@%s>", issue.Repo.FullName(), issue.Index, setting.Domain),
},
{
name: "Open Pull",
@@ -287,7 +293,7 @@ func Test_createReference(t *testing.T) {
issue: pullIssue,
actionType: activities_model.ActionCreatePullRequest,
},
- prefix: fmt.Sprintf("%s/pulls/%d@%s", issue.Repo.FullName(), issue.Index, setting.Domain),
+ prefix: fmt.Sprintf("<%s/pulls/%d@%s>", issue.Repo.FullName(), issue.Index, setting.Domain),
},
{
name: "Comment Issue",
@@ -296,7 +302,7 @@ func Test_createReference(t *testing.T) {
comment: comment,
actionType: activities_model.ActionCommentIssue,
},
- prefix: fmt.Sprintf("%s/issues/%d/comment/%d@%s", issue.Repo.FullName(), issue.Index, comment.ID, setting.Domain),
+ prefix: fmt.Sprintf("<%s/issues/%d/comment/%d@%s>", issue.Repo.FullName(), issue.Index, comment.ID, setting.Domain),
},
{
name: "Comment Pull",
@@ -305,7 +311,7 @@ func Test_createReference(t *testing.T) {
comment: comment,
actionType: activities_model.ActionCommentPull,
},
- prefix: fmt.Sprintf("%s/pulls/%d/comment/%d@%s", issue.Repo.FullName(), issue.Index, comment.ID, setting.Domain),
+ prefix: fmt.Sprintf("<%s/pulls/%d/comment/%d@%s>", issue.Repo.FullName(), issue.Index, comment.ID, setting.Domain),
},
{
name: "Close Issue",
@@ -313,7 +319,7 @@ func Test_createReference(t *testing.T) {
issue: issue,
actionType: activities_model.ActionCloseIssue,
},
- prefix: fmt.Sprintf("%s/issues/%d/close/", issue.Repo.FullName(), issue.Index),
+ prefix: fmt.Sprintf("<%s/issues/%d/close/", issue.Repo.FullName(), issue.Index),
},
{
name: "Close Pull",
@@ -321,7 +327,7 @@ func Test_createReference(t *testing.T) {
issue: pullIssue,
actionType: activities_model.ActionClosePullRequest,
},
- prefix: fmt.Sprintf("%s/pulls/%d/close/", issue.Repo.FullName(), issue.Index),
+ prefix: fmt.Sprintf("<%s/pulls/%d/close/", issue.Repo.FullName(), issue.Index),
},
{
name: "Reopen Issue",
@@ -329,7 +335,7 @@ func Test_createReference(t *testing.T) {
issue: issue,
actionType: activities_model.ActionReopenIssue,
},
- prefix: fmt.Sprintf("%s/issues/%d/reopen/", issue.Repo.FullName(), issue.Index),
+ prefix: fmt.Sprintf("<%s/issues/%d/reopen/", issue.Repo.FullName(), issue.Index),
},
{
name: "Reopen Pull",
@@ -337,7 +343,7 @@ func Test_createReference(t *testing.T) {
issue: pullIssue,
actionType: activities_model.ActionReopenPullRequest,
},
- prefix: fmt.Sprintf("%s/pulls/%d/reopen/", issue.Repo.FullName(), issue.Index),
+ prefix: fmt.Sprintf("<%s/pulls/%d/reopen/", issue.Repo.FullName(), issue.Index),
},
{
name: "Merge Pull",
@@ -345,7 +351,7 @@ func Test_createReference(t *testing.T) {
issue: pullIssue,
actionType: activities_model.ActionMergePullRequest,
},
- prefix: fmt.Sprintf("%s/pulls/%d/merge/", issue.Repo.FullName(), issue.Index),
+ prefix: fmt.Sprintf("<%s/pulls/%d/merge/", issue.Repo.FullName(), issue.Index),
},
{
name: "Ready Pull",
@@ -353,7 +359,7 @@ func Test_createReference(t *testing.T) {
issue: pullIssue,
actionType: activities_model.ActionPullRequestReadyForReview,
},
- prefix: fmt.Sprintf("%s/pulls/%d/ready/", issue.Repo.FullName(), issue.Index),
+ prefix: fmt.Sprintf("<%s/pulls/%d/ready/", issue.Repo.FullName(), issue.Index),
},
}
for _, tt := range tests {
@@ -362,9 +368,6 @@ func Test_createReference(t *testing.T) {
if !strings.HasPrefix(got, tt.prefix) {
t.Errorf("createReference() = %v, want %v", got, tt.prefix)
}
- if !strings.HasSuffix(got, tt.suffix) {
- t.Errorf("createReference() = %v, want %v", got, tt.prefix)
- }
})
}
}