]>
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
) OnMessageDelivered(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 relayMessageToHost(s
, env
, zap
.NewNop(), env
.RcptTo
[0].Address
, l
.Addr().String())
49 if len(s
.messages
) != 1 {
50 t
.Errorf("Expected 1 message to be delivered, got %d", len(s
.messages
))
54 received
:= s
.messages
[0]
56 if env
.MailFrom
.Address
!= received
.MailFrom
.Address
{
57 t
.Errorf("Expected MailFrom %s, got %s", env
.MailFrom
.Address
, received
.MailFrom
.Address
)
59 if len(received
.RcptTo
) != 1 {
60 t
.Errorf("Expected 1 RcptTo, got %d", len(received
.RcptTo
))
63 if env
.RcptTo
[0].Address
!= received
.RcptTo
[0].Address
{
64 t
.Errorf("Expected RcptTo %s, got %s", env
.RcptTo
[0].Address
, received
.RcptTo
[0].Address
)
67 if !bytes
.HasSuffix(received
.Data
, env
.Data
) {
68 t
.Errorf("Delivered message does not match relayed one. Delivered=%q Relayed=%q", string(env
.Data
), string(received
.Data
))
72 func TestDeliveryFailureMessage(t
*testing
.T
) {
73 s
:= &deliveryServer
{}
76 MailFrom
: mail
.Address
{Address
: "from@sender.org"},
77 RcptTo
: []mail
.Address
{{Address
: "to@receive.net"}},
78 Data
: []byte("Message\n"),
80 EHLO
: "mx.receive.net",
81 RemoteAddr
: &net
.IPAddr
{net
.IPv4(127, 0, 0, 1), ""},
84 errorStr1
:= "internal message"
85 errorStr2
:= "general error 122"
86 deliverRelayFailure(s
, env
, zap
.NewNop(), env
.RcptTo
[0].Address
, errorStr1
, fmt
.Errorf(errorStr2
))
88 if len(s
.messages
) != 1 {
89 t
.Errorf("Expected 1 failure notification, got %d", len(s
.messages
))
93 failure
:= s
.messages
[0]
95 if failure
.RcptTo
[0].Address
!= env
.MailFrom
.Address
{
96 t
.Errorf("Failure message should be delivered to sender %s, actually %s", env
.MailFrom
.Address
, failure
.RcptTo
[0].Address
)
99 // Read the failure message.
100 buf
:= bytes
.NewBuffer(failure
.Data
)
101 msg
, err
:= mail
.ReadMessage(buf
)
103 t
.Errorf("Failed to read message: %v", err
)
107 // Parse out the Content-Type to get the multipart boundary string.
108 mediatype
, mtheaders
, err
:= mime
.ParseMediaType(msg
.Header
["Content-Type"][0])
110 t
.Errorf("Failed to parse MIME headers: %v", err
)
114 expected
:= "multipart/report"
115 if mediatype
!= expected
{
116 t
.Errorf("Expected MIME type of %q, got %q", expected
, mediatype
)
119 expected
= "delivery-status"
120 if mtheaders
["report-type"] != expected
{
121 t
.Errorf("Expected report-type of %q, got %q", expected
, mtheaders
["report-type"])
124 boundary
:= mtheaders
["boundary"]
126 expected
= "Delivery Status Notification (Failure)"
127 if msg
.Header
["Subject"][0] != expected
{
128 t
.Errorf("Subject did not match %q, got %q", expected
, mtheaders
["Subject"])
131 if msg
.Header
["To"][0] != "<"+env
.MailFrom
.Address
+">" {
132 t
.Errorf("To field does not match %q, got %q", env
.MailFrom
.Address
, msg
.Header
["To"][0])
135 // Parse the multi-part messsage.
136 mpr
:= multipart
.NewReader(msg
.Body
, boundary
)
137 part
, err
:= mpr
.NextPart()
139 t
.Errorf("Error reading part 0: %v", err
)
143 // First part is the human-readable error.
144 expected
= "text/plain; charset=UTF-8"
145 if part
.Header
["Content-Type"][0] != expected
{
146 t
.Errorf("Part 0 type expected %q, got %q", expected
, part
.Header
["Content-Type"][0])
149 content
, err
:= ioutil
.ReadAll(part
)
151 t
.Errorf("Failed to read part 0 content: %v", err
)
154 contentStr
:= string(content
)
156 if !strings
.Contains(contentStr
, "Delivery Failure") {
157 t
.Errorf("Missing Delivery Failure")
160 expected
= fmt
.Sprintf("%s:\n%s", errorStr1
, errorStr2
)
161 if !strings
.Contains(contentStr
, expected
) {
162 t
.Errorf("Missing error string %q", expected
)
165 // Second part is the status information.
166 part
, err
= mpr
.NextPart()
168 t
.Errorf("Error reading part 1: %v", err
)
172 expected
= "delivery-status"
173 if part
.Header
["Content-Type"][0] != expected
{
174 t
.Errorf("Part 1 type expected %q, got %q", expected
, part
.Header
["Content-Type"][0])
177 content
, err
= ioutil
.ReadAll(part
)
179 t
.Errorf("Failed to read part 1 content: %v", err
)
182 contentStr
= string(content
)
184 expected
= "Original-Envelope-ID: " + env
.ID
+ "\n"
185 if !strings
.Contains(contentStr
, expected
) {
186 t
.Errorf("Missing %q in %q", expected
, contentStr
)
189 expected
= "Reporting-UA: " + env
.EHLO
+ "\n"
190 if !strings
.Contains(contentStr
, expected
) {
191 t
.Errorf("Missing %q in %q", expected
, contentStr
)
194 expected
= "Reporting-MTA: localhost\n"
195 if !strings
.Contains(contentStr
, expected
) {
196 t
.Errorf("Missing %q in %q", expected
, contentStr
)
199 expected
= "X-Remote-Address: 127.0.0.1\n"
200 if !strings
.Contains(contentStr
, expected
) {
201 t
.Errorf("Missing %q in %q", expected
, contentStr
)
204 // Third part is the original message.
205 part
, err
= mpr
.NextPart()
207 t
.Errorf("Error reading part 2: %v", err
)
211 expected
= "message/rfc822"
212 if part
.Header
["Content-Type"][0] != expected
{
213 t
.Errorf("Part 2 type expected %q, got %q", expected
, part
.Header
["Content-Type"][0])
216 content
, err
= ioutil
.ReadAll(part
)
218 t
.Errorf("Failed to read part 2 content: %v", err
)
222 if !bytes
.Equal(content
, env
.Data
) {
223 t
.Errorf("Byte content of original message does not match")