优先使用原始邮件数据转发,保留原始发件人、主题、正文、MIME 和 DKIM 签名,避免重建邮件导致严格 DMARC 收件方拒收。 当原始转发被拒时,回退为使用本地转发账号重新投递,同时保持原始主题不变。 同时修正本地域名转发逻辑:精确识别本地域名,自转发时报错,转发给其他本地用户时直接投递到本地收件箱,避免旧的字符串包含判断误拦截。 测试:go test -v ./services/rule ./dto/parsemail ./utils/send Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
86 lines
2.3 KiB
Go
86 lines
2.3 KiB
Go
package rule
|
|
|
|
import (
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/Jinnrry/pmail/config"
|
|
"github.com/Jinnrry/pmail/db"
|
|
"github.com/Jinnrry/pmail/dto/parsemail"
|
|
"github.com/Jinnrry/pmail/models"
|
|
"github.com/Jinnrry/pmail/utils/context"
|
|
_ "modernc.org/sqlite"
|
|
"xorm.io/xorm"
|
|
)
|
|
|
|
func TestForwardToLocalUser(t *testing.T) {
|
|
oldConfig := config.Instance
|
|
oldDB := db.Instance
|
|
defer func() {
|
|
config.Instance = oldConfig
|
|
db.Instance = oldDB
|
|
}()
|
|
|
|
config.Instance = &config.Config{
|
|
Domain: "example.com",
|
|
Domains: []string{"example.com"},
|
|
}
|
|
|
|
engine, err := xorm.NewEngine("sqlite", ":memory:")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer engine.Close()
|
|
db.Instance = engine
|
|
|
|
if err := engine.Sync2(&models.User{}, &models.UserEmail{}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
sender := &models.User{ID: 1, Account: "sender", Name: "sender"}
|
|
recipient := &models.User{ID: 2, Account: "recipient", Name: "recipient", IsAdmin: 1}
|
|
if _, err := engine.Insert(sender, recipient); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
email := &parsemail.Email{MessageId: 123, Status: 0}
|
|
if err := doForward(&context.Context{}, email, "recipient@example.com", sender, nil); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
var links []models.UserEmail
|
|
if err := engine.Table(&models.UserEmail{}).Where("email_id=?", email.MessageId).Find(&links); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if len(links) != 1 || links[0].UserID != recipient.ID {
|
|
t.Fatalf("expected one user_email link for recipient, got %+v", links)
|
|
}
|
|
|
|
if err := doForward(&context.Context{}, email, "recipient@example.com", sender, nil); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
var count int64
|
|
count, err = engine.Table(&models.UserEmail{}).Where("email_id=? and user_id=?", email.MessageId, recipient.ID).Count()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if count != 1 {
|
|
t.Fatalf("expected idempotent local forward link, got %d links", count)
|
|
}
|
|
}
|
|
|
|
func TestForwardToLocalUserRejectsSelfLoop(t *testing.T) {
|
|
oldConfig := config.Instance
|
|
defer func() { config.Instance = oldConfig }()
|
|
|
|
config.Instance = &config.Config{
|
|
Domain: "example.com",
|
|
Domains: []string{"example.com"},
|
|
}
|
|
|
|
err := doForward(&context.Context{}, &parsemail.Email{MessageId: 1}, "sender@example.com", &models.User{Account: "sender"}, nil)
|
|
if err == nil || !strings.Contains(err.Error(), "loop forwarding to self") {
|
|
t.Fatalf("expected self-loop error, got %v", err)
|
|
}
|
|
}
|