forgejo/tests/integration/incoming_email_test.go
TheFox0x7 4de909747b
Some checks are pending
/ release (push) Waiting to run
testing / test-remote-cacher (map[image:redis:7.2 port:6379]) (push) Blocked by required conditions
testing / test-remote-cacher (map[image:registry.redict.io/redict:7.3.0-scratch port:6379]) (push) Blocked by required conditions
testing / test-mysql (push) Blocked by required conditions
testing / test-pgsql (push) Blocked by required conditions
testing / backend-checks (push) Waiting to run
testing / frontend-checks (push) Waiting to run
testing / test-unit (push) Blocked by required conditions
testing / test-remote-cacher (map[image:docker.io/valkey/valkey:7.2.5-alpine3.19 port:6379]) (push) Blocked by required conditions
testing / test-remote-cacher (map[image:ghcr.io/microsoft/garnet-alpine:1.0.14 port:6379]) (push) Blocked by required conditions
testing / test-sqlite (push) Blocked by required conditions
testing / security-check (push) Blocked by required conditions
Add testifylint to lint checks (#4535)
go-require lint is ignored for now

Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/4535
Reviewed-by: Gusted <gusted@noreply.codeberg.org>
Co-authored-by: TheFox0x7 <thefox0x7@gmail.com>
Co-committed-by: TheFox0x7 <thefox0x7@gmail.com>
2024-07-30 19:41:10 +00:00

244 lines
7.4 KiB
Go

// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package integration
import (
"io"
"net"
"net/smtp"
"strings"
"testing"
"time"
"code.gitea.io/gitea/models/db"
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"
token_service "code.gitea.io/gitea/services/mailer/token"
"code.gitea.io/gitea/tests"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"gopkg.in/gomail.v2"
)
func TestIncomingEmail(t *testing.T) {
defer tests.PrepareTestEnv(t)()
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)
require.Error(t, err)
issuePayload, err := incoming_payload.CreateReferencePayload(issue)
require.NoError(t, err)
commentPayload, err := incoming_payload.CreateReferencePayload(comment)
require.NoError(t, err)
_, err = incoming_payload.GetReferenceFromPayload(db.DefaultContext, []byte{1, 2, 3})
require.Error(t, err)
ref, err := incoming_payload.GetReferenceFromPayload(db.DefaultContext, issuePayload)
require.NoError(t, err)
assert.IsType(t, ref, new(issues_model.Issue))
assert.EqualValues(t, issue.ID, ref.(*issues_model.Issue).ID)
ref, err = incoming_payload.GetReferenceFromPayload(db.DefaultContext, commentPayload)
require.NoError(t, err)
assert.IsType(t, ref, new(issues_model.Comment))
assert.EqualValues(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)
require.NoError(t, err)
assert.NotEmpty(t, token)
ht, u, p, err := token_service.ExtractToken(db.DefaultContext, token)
require.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) {
checkReply := func(t *testing.T, payload []byte, issue *issues_model.Issue, commentType issues_model.CommentType) {
t.Helper()
handler := &incoming.ReplyHandler{}
require.Error(t, handler.Handle(db.DefaultContext, &incoming.MailContent{}, nil, payload))
require.NoError(t, handler.Handle(db.DefaultContext, &incoming.MailContent{}, user, payload))
content := &incoming.MailContent{
Content: "reply by mail",
Attachments: []*incoming.Attachment{
{
Name: "attachment.txt",
Content: []byte("test"),
},
},
}
require.NoError(t, handler.Handle(db.DefaultContext, content, user, payload))
comments, err := issues_model.FindComments(db.DefaultContext, &issues_model.FindCommentsOptions{
IssueID: issue.ID,
Type: commentType,
})
require.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)
require.NoError(t, comment.LoadAttachments(db.DefaultContext))
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("Issue", func(t *testing.T) {
defer tests.PrintCurrentTest(t)()
payload, err := incoming_payload.CreateReferencePayload(issue)
require.NoError(t, err)
checkReply(t, payload, issue, issues_model.CommentTypeComment)
})
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})
payload, err := incoming_payload.CreateReferencePayload(comment)
require.NoError(t, err)
checkReply(t, payload, issue, issues_model.CommentTypeCode)
})
t.Run("Comment", func(t *testing.T) {
defer tests.PrintCurrentTest(t)()
comment := unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{ID: 2})
issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: comment.IssueID})
payload, err := incoming_payload.CreateReferencePayload(comment)
require.NoError(t, err)
checkReply(t, payload, issue, issues_model.CommentTypeComment)
})
})
t.Run("Unsubscribe", func(t *testing.T) {
defer tests.PrintCurrentTest(t)()
watching, err := issues_model.CheckIssueWatch(db.DefaultContext, user, issue)
require.NoError(t, err)
assert.True(t, watching)
handler := &incoming.UnsubscribeHandler{}
content := &incoming.MailContent{
Content: "unsub me",
}
payload, err := incoming_payload.CreateReferencePayload(issue)
require.NoError(t, err)
require.NoError(t, handler.Handle(db.DefaultContext, content, user, payload))
watching, err = issues_model.CheckIssueWatch(db.DefaultContext, user, issue)
require.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)
require.NoError(t, err)
token, err := token_service.CreateToken(token_service.ReplyHandlerType, user, payload)
require.NoError(t, err)
msg := gomail.NewMessage()
msg.SetHeader("To", strings.Replace(setting.IncomingEmail.ReplyToAddress, setting.IncomingEmail.TokenPlaceholder, token, 1))
msg.SetHeader("From", user.Email)
msg.SetBody("text/plain", token)
err = gomail.Send(&smtpTestSender{}, msg)
require.NoError(t, err)
assert.Eventually(t, func() bool {
comments, err := issues_model.FindComments(db.DefaultContext, &issues_model.FindCommentsOptions{
IssueID: issue.ID,
Type: issues_model.CommentTypeComment,
})
require.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()
}