]>
src.bluestatic.org Git - mailpopbox.git/blob - smtp/relay_test.go
2 // Copyright 2020 Blue Static <https://www.bluestatic.org>
3 // This program is free software licensed under the GNU General Public License,
4 // version 3.0. The full text of the license can be found in LICENSE.txt.
5 // SPDX-License-Identifier: GPL-3.0-only
23 type deliveryServer
struct {
28 func (s
*deliveryServer
) DeliverMessage(env Envelope
) *ReplyLine
{
29 s
.messages
= append(s
.messages
, env
)
33 func TestRelayRoundTrip(t
*testing
.T
) {
35 testServer
: testServer
{domain
: "receive.net"},
41 MailFrom
: mail
.Address
{Address
: "from@sender.org"},
42 RcptTo
: []mail
.Address
{{Address
: "to@receive.net"}},
43 Data
: []byte("~~~Message~~~\n"),
47 host
, port
, _
:= net
.SplitHostPort(l
.Addr().String())
52 mta
.relayMessageToHost(env
, zap
.NewNop(), env
.RcptTo
[0].Address
, host
, port
)
54 if want
, got
:= 1, len(s
.messages
); want
!= got
{
55 t
.Errorf("Want %d message to be delivered, got %d", want
, got
)
59 received
:= s
.messages
[0]
61 if want
, got
:= env
.MailFrom
.Address
, received
.MailFrom
.Address
; want
!= got
{
62 t
.Errorf("Want MailFrom %s, got %s", want
, got
)
64 if want
, got
:= 1, len(received
.RcptTo
); want
!= got
{
65 t
.Errorf("Want %d RcptTo, got %d", want
, got
)
68 if want
, got
:= env
.RcptTo
[0].Address
, received
.RcptTo
[0].Address
; want
!= got
{
69 t
.Errorf("Want RcptTo %s, got %s", want
, got
)
72 if !bytes
.HasSuffix(received
.Data
, env
.Data
) {
73 t
.Errorf("Delivered message does not match relayed one. Delivered=%q Relayed=%q", string(env
.Data
), string(received
.Data
))
77 func TestDeliveryFailureMessage(t
*testing
.T
) {
78 s
:= &deliveryServer
{}
81 MailFrom
: mail
.Address
{Address
: "from@sender.org"},
82 RcptTo
: []mail
.Address
{{Address
: "to@receive.net"}},
83 Data
: []byte("Message\n"),
85 EHLO
: "mx.receive.net",
86 RemoteAddr
: &net
.IPAddr
{net
.IPv4(127, 0, 0, 1), ""},
89 errorStr1
:= "internal message"
90 errorStr2
:= "general error 122"
95 mta
.deliverRelayFailure(env
, zap
.NewNop(), env
.RcptTo
[0].Address
, errorStr1
, fmt
.Errorf(errorStr2
))
97 if want
, got
:= 1, len(s
.messages
); want
!= got
{
98 t
.Errorf("Want %d failure notification, got %d", want
, got
)
102 failure
:= s
.messages
[0]
104 if want
, got
:= env
.MailFrom
.Address
, failure
.RcptTo
[0].Address
; want
!= got
{
105 t
.Errorf("Failure message should be delivered to sender %s, actually %s", want
, got
)
108 // Read the failure message.
109 buf
:= bytes
.NewBuffer(failure
.Data
)
110 msg
, err
:= mail
.ReadMessage(buf
)
112 t
.Errorf("Failed to read message: %v", err
)
116 // Parse out the Content-Type to get the multipart boundary string.
117 mediatype
, mtheaders
, err
:= mime
.ParseMediaType(msg
.Header
["Content-Type"][0])
119 t
.Errorf("Failed to parse MIME headers: %v", err
)
123 if want
, got
:= "multipart/report", mediatype
; want
!= got
{
124 t
.Errorf("Want MIME type of %q, got %q", want
, got
)
127 if want
, got
:= "delivery-status", mtheaders
["report-type"]; want
!= got
{
128 t
.Errorf("Want report-type of %q, got %q", want
, got
)
131 boundary
:= mtheaders
["boundary"]
133 if want
, got
:= "Delivery Status Notification (Failure)", msg
.Header
["Subject"][0]; want
!= got
{
134 t
.Errorf("Want Subject field %q, got %q", want
, got
)
137 if want
, got
:= "<"+env
.MailFrom
.Address
+">", msg
.Header
["To"][0]; want
!= got
{
138 t
.Errorf("Want To field %q, got %q", want
, got
)
141 // Parse the multi-part messsage.
142 mpr
:= multipart
.NewReader(msg
.Body
, boundary
)
143 part
, err
:= mpr
.NextPart()
145 t
.Errorf("Error reading part 0: %v", err
)
149 // First part is the human-readable error.
150 if want
, got
:= "text/plain; charset=UTF-8", part
.Header
["Content-Type"][0]; want
!= got
{
151 t
.Errorf("Part 0 type want %q, got %q", want
, got
)
154 content
, err
:= ioutil
.ReadAll(part
)
156 t
.Errorf("Failed to read part 0 content: %v", err
)
159 contentStr
:= string(content
)
161 if !strings
.Contains(contentStr
, "Delivery Failure") {
162 t
.Errorf("Missing Delivery Failure")
165 if want
:= fmt
.Sprintf("%s:\n%s", errorStr1
, errorStr2
); !strings
.Contains(contentStr
, want
) {
166 t
.Errorf("Missing error string %q", want
)
169 // Second part is the status information.
170 part
, err
= mpr
.NextPart()
172 t
.Errorf("Error reading part 1: %v", err
)
176 if want
, got
:= "message/delivery-status", part
.Header
["Content-Type"][0]; want
!= got
{
177 t
.Errorf("Part 1 type want %q, got %q", want
, got
)
180 content
, err
= ioutil
.ReadAll(part
)
182 t
.Errorf("Failed to read part 1 content: %v", err
)
185 contentStr
= string(content
)
187 if want
:= "Original-Envelope-ID: " + env
.ID
+ "\n"; !strings
.Contains(contentStr
, want
) {
188 t
.Errorf("Missing %q in %q", want
, contentStr
)
191 if want
:= "Reporting-UA: " + env
.EHLO
+ "\n"; !strings
.Contains(contentStr
, want
) {
192 t
.Errorf("Missing %q in %q", want
, contentStr
)
195 if want
:= "Reporting-MTA: dns; localhost [127.0.0.1]\n"; !strings
.Contains(contentStr
, want
) {
196 t
.Errorf("Missing %q in %q", want
, contentStr
)
199 // Third part is the original message.
200 part
, err
= mpr
.NextPart()
202 t
.Errorf("Error reading part 2: %v", err
)
206 if want
, got
:= "message/rfc822", part
.Header
["Content-Type"][0]; want
!= got
{
207 t
.Errorf("Part 2 type want %q, got %q", want
, got
)
210 content
, err
= ioutil
.ReadAll(part
)
212 t
.Errorf("Failed to read part 2 content: %v", err
)
216 if !bytes
.Equal(content
, env
.Data
) {
217 t
.Errorf("Byte content of original message does not match")