fix(send_queue): 修复重复消息 ID 的失败重排并清理 MIME

- 投递成功后清空 `mime_base64`,避免已投递队列项保留原始 MIME。
- 当同一 `message_id` 的旧队列项已达到最大失败次数时,允许复用并重新入队。
- 补充测试覆盖已投递项清理与终态失败重排场景。
This commit is contained in:
LanQin_
2026-06-24 11:33:40 +08:00
parent 7b0717412c
commit 2476fe0c19
2 changed files with 71 additions and 4 deletions
+47
View File
@@ -894,6 +894,13 @@ func TestSendQueueRecoversStaleSendingItems(t *testing.T) {
if status != sendQueueStatusDelivered {
t.Fatalf("queue status=%q, want delivered", status)
}
var mimeBase64 string
if err := a.db.QueryRow(`SELECT mime_base64 FROM send_queue WHERE id=?`, queueID).Scan(&mimeBase64); err != nil {
t.Fatal(err)
}
if mimeBase64 != "" {
t.Fatal("delivered queue item should not retain raw MIME")
}
}
func TestSubmissionAuthRequiresMailboxPasswordAndSendPermission(t *testing.T) {
@@ -1082,6 +1089,46 @@ func TestSubmissionSentCopyDedupesByMessageID(t *testing.T) {
}
}
func TestSubmissionRequeuesTerminalFailedDuplicateMessageID(t *testing.T) {
a := newTestApp(t)
a.cfg.SMTPHost = "127.0.0.1"
a.cfg.SMTPPort = "1"
user, mb, err := a.authenticateSubmission(context.Background(), "admin@lanqin.local", "ChangeMe123!")
if err != nil {
t.Fatal(err)
}
raw := "From: admin@lanqin.local\r\nTo: person@example.com\r\nSubject: requeue\r\nMessage-ID: <requeue@example.test>\r\n\r\nbody"
if err := a.submitSMTPMessage(context.Background(), user, mb, mb.Address, []string{"person@example.com"}, strings.NewReader(raw)); err != nil {
t.Fatal(err)
}
if _, err := a.db.Exec(`UPDATE send_queue SET status=?,attempt_count=max_attempts,next_attempt_at=?,last_error='terminal' WHERE mailbox_id=? AND message_id=?`, sendQueueStatusFailed, a.now().UTC().Add(time.Hour).Format(time.RFC3339Nano), mb.ID, "<requeue@example.test>"); err != nil {
t.Fatal(err)
}
host, port, received := startCapturingSMTP(t, 1)
a.cfg.SMTPHost = host
a.cfg.SMTPPort = port
if err := a.submitSMTPMessage(context.Background(), user, mb, mb.Address, []string{"person@example.com"}, strings.NewReader(raw)); err != nil {
t.Fatal(err)
}
if err := a.processDueSendQueue(context.Background()); err != nil {
t.Fatal(err)
}
select {
case <-received:
case <-time.After(2 * time.Second):
t.Fatal("requeued terminal failure was not relayed")
}
var status string
var attemptCount int
if err := a.db.QueryRow(`SELECT status,attempt_count FROM send_queue WHERE mailbox_id=? AND message_id=?`, mb.ID, "<requeue@example.test>").Scan(&status, &attemptCount); err != nil {
t.Fatal(err)
}
if status != sendQueueStatusDelivered || attemptCount != 1 {
t.Fatalf("queue status=%q attempts=%d, want delivered attempts=1", status, attemptCount)
}
}
func TestSubmissionAllowsAuthorizedAliasSendAs(t *testing.T) {
a := newTestApp(t)
ctx := context.Background()