mirror of https://github.com/go-gitea/gitea.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
256 lines
7.7 KiB
256 lines
7.7 KiB
// Copyright 2023 The Gitea Authors. All rights reserved. |
|
// SPDX-License-Identifier: MIT |
|
|
|
package integration |
|
|
|
import ( |
|
"io" |
|
"net" |
|
"net/smtp" |
|
"net/url" |
|
"strings" |
|
"testing" |
|
"time" |
|
|
|
issues_model "code.gitea.io/gitea/models/issues" |
|
"code.gitea.io/gitea/models/unittest" |
|
user_model "code.gitea.io/gitea/models/user" |
|
"code.gitea.io/gitea/modules/setting" |
|
"code.gitea.io/gitea/services/mailer/incoming" |
|
incoming_payload "code.gitea.io/gitea/services/mailer/incoming/payload" |
|
sender_service "code.gitea.io/gitea/services/mailer/sender" |
|
token_service "code.gitea.io/gitea/services/mailer/token" |
|
"code.gitea.io/gitea/tests" |
|
|
|
"github.com/stretchr/testify/assert" |
|
) |
|
|
|
func TestIncomingEmail(t *testing.T) { |
|
onGiteaRun(t, func(t *testing.T, u *url.URL) { |
|
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) |
|
issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 1}) |
|
|
|
t.Run("Payload", func(t *testing.T) { |
|
defer tests.PrintCurrentTest(t)() |
|
|
|
comment := unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{ID: 1}) |
|
|
|
_, err := incoming_payload.CreateReferencePayload(user) |
|
assert.Error(t, err) |
|
|
|
issuePayload, err := incoming_payload.CreateReferencePayload(issue) |
|
assert.NoError(t, err) |
|
commentPayload, err := incoming_payload.CreateReferencePayload(comment) |
|
assert.NoError(t, err) |
|
|
|
_, err = incoming_payload.GetReferenceFromPayload(t.Context(), []byte{1, 2, 3}) |
|
assert.Error(t, err) |
|
|
|
ref, err := incoming_payload.GetReferenceFromPayload(t.Context(), issuePayload) |
|
assert.NoError(t, err) |
|
assert.IsType(t, ref, new(issues_model.Issue)) |
|
assert.Equal(t, issue.ID, ref.(*issues_model.Issue).ID) |
|
|
|
ref, err = incoming_payload.GetReferenceFromPayload(t.Context(), commentPayload) |
|
assert.NoError(t, err) |
|
assert.IsType(t, ref, new(issues_model.Comment)) |
|
assert.Equal(t, comment.ID, ref.(*issues_model.Comment).ID) |
|
}) |
|
|
|
t.Run("Token", func(t *testing.T) { |
|
defer tests.PrintCurrentTest(t)() |
|
|
|
payload := []byte{1, 2, 3, 4, 5} |
|
|
|
token, err := token_service.CreateToken(token_service.ReplyHandlerType, user, payload) |
|
assert.NoError(t, err) |
|
assert.NotEmpty(t, token) |
|
|
|
ht, u, p, err := token_service.ExtractToken(t.Context(), token) |
|
assert.NoError(t, err) |
|
assert.Equal(t, token_service.ReplyHandlerType, ht) |
|
assert.Equal(t, user.ID, u.ID) |
|
assert.Equal(t, payload, p) |
|
}) |
|
|
|
t.Run("Handler", func(t *testing.T) { |
|
t.Run("Reply", func(t *testing.T) { |
|
t.Run("Comment", func(t *testing.T) { |
|
defer tests.PrintCurrentTest(t)() |
|
|
|
handler := &incoming.ReplyHandler{} |
|
|
|
payload, err := incoming_payload.CreateReferencePayload(issue) |
|
assert.NoError(t, err) |
|
|
|
assert.Error(t, handler.Handle(t.Context(), &incoming.MailContent{}, nil, payload)) |
|
assert.NoError(t, handler.Handle(t.Context(), &incoming.MailContent{}, user, payload)) |
|
|
|
content := &incoming.MailContent{ |
|
Content: "reply by mail", |
|
Attachments: []*incoming.Attachment{ |
|
{ |
|
Name: "attachment.txt", |
|
Content: []byte("test"), |
|
}, |
|
}, |
|
} |
|
|
|
assert.NoError(t, handler.Handle(t.Context(), content, user, payload)) |
|
|
|
comments, err := issues_model.FindComments(t.Context(), &issues_model.FindCommentsOptions{ |
|
IssueID: issue.ID, |
|
Type: issues_model.CommentTypeComment, |
|
}) |
|
assert.NoError(t, err) |
|
assert.NotEmpty(t, comments) |
|
comment := comments[len(comments)-1] |
|
assert.Equal(t, user.ID, comment.PosterID) |
|
assert.Equal(t, content.Content, comment.Content) |
|
assert.NoError(t, comment.LoadAttachments(t.Context())) |
|
assert.Len(t, comment.Attachments, 1) |
|
attachment := comment.Attachments[0] |
|
assert.Equal(t, content.Attachments[0].Name, attachment.Name) |
|
assert.EqualValues(t, 4, attachment.Size) |
|
}) |
|
|
|
t.Run("CodeComment", func(t *testing.T) { |
|
defer tests.PrintCurrentTest(t)() |
|
|
|
comment := unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{ID: 6}) |
|
issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: comment.IssueID}) |
|
|
|
handler := &incoming.ReplyHandler{} |
|
content := &incoming.MailContent{ |
|
Content: "code reply by mail", |
|
Attachments: []*incoming.Attachment{ |
|
{ |
|
Name: "attachment.txt", |
|
Content: []byte("test"), |
|
}, |
|
}, |
|
} |
|
|
|
payload, err := incoming_payload.CreateReferencePayload(comment) |
|
assert.NoError(t, err) |
|
|
|
assert.NoError(t, handler.Handle(t.Context(), content, user, payload)) |
|
|
|
comments, err := issues_model.FindComments(t.Context(), &issues_model.FindCommentsOptions{ |
|
IssueID: issue.ID, |
|
Type: issues_model.CommentTypeCode, |
|
}) |
|
assert.NoError(t, err) |
|
assert.NotEmpty(t, comments) |
|
comment = comments[len(comments)-1] |
|
assert.Equal(t, user.ID, comment.PosterID) |
|
assert.Equal(t, content.Content, comment.Content) |
|
assert.NoError(t, comment.LoadAttachments(t.Context())) |
|
assert.Len(t, comment.Attachments, 1) |
|
attachment := comment.Attachments[0] |
|
assert.Equal(t, content.Attachments[0].Name, attachment.Name) |
|
assert.EqualValues(t, 4, attachment.Size) |
|
}) |
|
}) |
|
|
|
t.Run("Unsubscribe", func(t *testing.T) { |
|
defer tests.PrintCurrentTest(t)() |
|
|
|
watching, err := issues_model.CheckIssueWatch(t.Context(), user, issue) |
|
assert.NoError(t, err) |
|
assert.True(t, watching) |
|
|
|
handler := &incoming.UnsubscribeHandler{} |
|
|
|
content := &incoming.MailContent{ |
|
Content: "unsub me", |
|
} |
|
|
|
payload, err := incoming_payload.CreateReferencePayload(issue) |
|
assert.NoError(t, err) |
|
|
|
assert.NoError(t, handler.Handle(t.Context(), content, user, payload)) |
|
|
|
watching, err = issues_model.CheckIssueWatch(t.Context(), user, issue) |
|
assert.NoError(t, err) |
|
assert.False(t, watching) |
|
}) |
|
}) |
|
|
|
if setting.IncomingEmail.Enabled { |
|
// This test connects to the configured email server and is currently only enabled for MySql integration tests. |
|
// It sends a reply to create a comment. If the comment is not detected after 10 seconds the test fails. |
|
t.Run("IMAP", func(t *testing.T) { |
|
defer tests.PrintCurrentTest(t)() |
|
|
|
payload, err := incoming_payload.CreateReferencePayload(issue) |
|
assert.NoError(t, err) |
|
token, err := token_service.CreateToken(token_service.ReplyHandlerType, user, payload) |
|
assert.NoError(t, err) |
|
|
|
msg := sender_service.NewMessageFrom( |
|
strings.Replace(setting.IncomingEmail.ReplyToAddress, setting.IncomingEmail.TokenPlaceholder, token, 1), |
|
"", |
|
user.Email, |
|
"", |
|
token, |
|
) |
|
|
|
err = sender_service.Send(&smtpTestSender{}, msg) |
|
assert.NoError(t, err) |
|
|
|
assert.Eventually(t, func() bool { |
|
comments, err := issues_model.FindComments(t.Context(), &issues_model.FindCommentsOptions{ |
|
IssueID: issue.ID, |
|
Type: issues_model.CommentTypeComment, |
|
}) |
|
assert.NoError(t, err) |
|
assert.NotEmpty(t, comments) |
|
|
|
comment := comments[len(comments)-1] |
|
|
|
return comment.PosterID == user.ID && comment.Content == token |
|
}, 10*time.Second, 1*time.Second) |
|
}) |
|
} |
|
}) |
|
} |
|
|
|
// A simple SMTP mail sender used for integration tests. |
|
type smtpTestSender struct{} |
|
|
|
func (s *smtpTestSender) Send(from string, to []string, msg io.WriterTo) error { |
|
conn, err := net.Dial("tcp", net.JoinHostPort(setting.IncomingEmail.Host, "25")) |
|
if err != nil { |
|
return err |
|
} |
|
defer conn.Close() |
|
|
|
client, err := smtp.NewClient(conn, setting.IncomingEmail.Host) |
|
if err != nil { |
|
return err |
|
} |
|
|
|
if err = client.Mail(from); err != nil { |
|
return err |
|
} |
|
|
|
for _, rec := range to { |
|
if err = client.Rcpt(rec); err != nil { |
|
return err |
|
} |
|
} |
|
|
|
w, err := client.Data() |
|
if err != nil { |
|
return err |
|
} |
|
if _, err := msg.WriteTo(w); err != nil { |
|
return err |
|
} |
|
if err := w.Close(); err != nil { |
|
return err |
|
} |
|
|
|
return client.Quit() |
|
}
|
|
|