- Added new API endpoints for sending, rescheduling, and canceling scheduled outbox messages. - Implemented outbox processing logic to handle attachments and manage message statuses. - Introduced a dead-letter strategy for failed outbox messages, enhancing reliability. - Updated database schema to support new outbox statuses and dead-letter entries. - Enhanced unit tests for outbox functionalities, ensuring robust error handling and validation. - Improved attachment handling in the outbox processor to support inline and regular attachments.
93 lines
2.2 KiB
Go
93 lines
2.2 KiB
Go
package smtp
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"reflect"
|
|
"testing"
|
|
)
|
|
|
|
func TestParseOutboxAttachmentsJSON(t *testing.T) {
|
|
data := []byte(`[{
|
|
"id":"att-1",
|
|
"filename":"doc.pdf",
|
|
"content_type":"application/pdf",
|
|
"size":12,
|
|
"s3_bucket":"mail-attachments",
|
|
"s3_key":"user/drafts/id/doc.pdf",
|
|
"content_id":"logo@ultimail",
|
|
"is_inline":true
|
|
}]`)
|
|
|
|
refs, err := parseOutboxAttachmentsJSON(data)
|
|
if err != nil {
|
|
t.Fatalf("parseOutboxAttachmentsJSON() error = %v", err)
|
|
}
|
|
if len(refs) != 1 {
|
|
t.Fatalf("len(refs) = %d, want 1", len(refs))
|
|
}
|
|
if refs[0].Filename != "doc.pdf" || !refs[0].IsInline || refs[0].ContentID != "logo@ultimail" {
|
|
t.Fatalf("unexpected ref: %+v", refs[0])
|
|
}
|
|
}
|
|
|
|
func TestParseOutboxAttachmentsJSON_empty(t *testing.T) {
|
|
for _, data := range [][]byte{nil, []byte{}, []byte("[]")} {
|
|
refs, err := parseOutboxAttachmentsJSON(data)
|
|
if err != nil {
|
|
t.Fatalf("parseOutboxAttachmentsJSON(%q) error = %v", data, err)
|
|
}
|
|
if refs != nil {
|
|
t.Fatalf("parseOutboxAttachmentsJSON(%q) = %v, want nil", data, refs)
|
|
}
|
|
}
|
|
}
|
|
|
|
type stubAttachmentLoader struct {
|
|
data map[string][]byte
|
|
err error
|
|
}
|
|
|
|
func (s stubAttachmentLoader) Load(_ context.Context, s3Key string) ([]byte, error) {
|
|
if s.err != nil {
|
|
return nil, s.err
|
|
}
|
|
payload, ok := s.data[s3Key]
|
|
if !ok {
|
|
return nil, errors.New("missing object")
|
|
}
|
|
return payload, nil
|
|
}
|
|
|
|
func TestResolveOutboxAttachments(t *testing.T) {
|
|
data := []byte(`[{
|
|
"filename":"note.txt",
|
|
"content_type":"text/plain",
|
|
"s3_key":"drafts/note.txt"
|
|
}]`)
|
|
loader := stubAttachmentLoader{data: map[string][]byte{
|
|
"drafts/note.txt": []byte("hello"),
|
|
}}
|
|
|
|
got, err := resolveOutboxAttachments(context.Background(), loader, data)
|
|
if err != nil {
|
|
t.Fatalf("resolveOutboxAttachments() error = %v", err)
|
|
}
|
|
want := []SendAttachment{{
|
|
Filename: "note.txt",
|
|
ContentType: "text/plain",
|
|
Data: []byte("hello"),
|
|
}}
|
|
if !reflect.DeepEqual(got, want) {
|
|
t.Fatalf("resolveOutboxAttachments() = %+v, want %+v", got, want)
|
|
}
|
|
}
|
|
|
|
func TestResolveOutboxAttachments_requiresLoader(t *testing.T) {
|
|
data := []byte(`[{"filename":"note.txt","s3_key":"drafts/note.txt"}]`)
|
|
_, err := resolveOutboxAttachments(context.Background(), nil, data)
|
|
if err == nil {
|
|
t.Fatal("expected error when loader is nil")
|
|
}
|
|
}
|