blob: 3c037f38385209ec3b23555d7d5c0a9475359370 [file] [log] [blame]
David Symonds32e36442011-06-01 14:10:21 +10001// Copyright 2011 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5package mail
6
7import (
8 "bytes"
9 "io/ioutil"
10 "reflect"
11 "testing"
12 "time"
13)
14
15var parseTests = []struct {
16 in string
17 header Header
18 body string
19}{
20 {
21 // RFC 5322, Appendix A.1.1
22 in: `From: John Doe <jdoe@machine.example>
23To: Mary Smith <mary@example.net>
24Subject: Saying Hello
25Date: Fri, 21 Nov 1997 09:55:06 -0600
26Message-ID: <1234@local.machine.example>
27
28This is a message just to say hello.
29So, "Hello".
30`,
31 header: Header{
32 "From": []string{"John Doe <jdoe@machine.example>"},
33 "To": []string{"Mary Smith <mary@example.net>"},
34 "Subject": []string{"Saying Hello"},
35 "Date": []string{"Fri, 21 Nov 1997 09:55:06 -0600"},
36 "Message-Id": []string{"<1234@local.machine.example>"},
37 },
38 body: "This is a message just to say hello.\nSo, \"Hello\".\n",
39 },
40}
41
42func TestParsing(t *testing.T) {
43 for i, test := range parseTests {
44 msg, err := ReadMessage(bytes.NewBuffer([]byte(test.in)))
45 if err != nil {
46 t.Errorf("test #%d: Failed parsing message: %v", i, err)
47 continue
48 }
49 if !headerEq(msg.Header, test.header) {
50 t.Errorf("test #%d: Incorrectly parsed message header.\nGot:\n%+v\nWant:\n%+v",
51 i, msg.Header, test.header)
52 }
53 body, err := ioutil.ReadAll(msg.Body)
54 if err != nil {
55 t.Errorf("test #%d: Failed reading body: %v", i, err)
56 continue
57 }
58 bodyStr := string(body)
59 if bodyStr != test.body {
60 t.Errorf("test #%d: Incorrectly parsed message body.\nGot:\n%+v\nWant:\n%+v",
61 i, bodyStr, test.body)
62 }
63 }
64}
65
66func headerEq(a, b Header) bool {
67 if len(a) != len(b) {
68 return false
69 }
70 for k, as := range a {
71 bs, ok := b[k]
72 if !ok {
73 return false
74 }
75 if !reflect.DeepEqual(as, bs) {
76 return false
77 }
78 }
79 return true
80}
81
82func TestDateParsing(t *testing.T) {
83 tests := []struct {
84 dateStr string
Russ Cox03823b82011-11-30 12:01:46 -050085 exp time.Time
David Symonds32e36442011-06-01 14:10:21 +100086 }{
87 // RFC 5322, Appendix A.1.1
88 {
89 "Fri, 21 Nov 1997 09:55:06 -0600",
Russ Cox03823b82011-11-30 12:01:46 -050090 time.Date(1997, 11, 21, 9, 55, 6, 0, time.FixedZone("", -6*60*60)),
David Symonds32e36442011-06-01 14:10:21 +100091 },
92 // RFC5322, Appendix A.6.2
93 // Obsolete date.
94 {
95 "21 Nov 97 09:55:06 GMT",
Russ Cox03823b82011-11-30 12:01:46 -050096 time.Date(1997, 11, 21, 9, 55, 6, 0, time.FixedZone("GMT", 0)),
David Symonds32e36442011-06-01 14:10:21 +100097 },
Bill Thiede132dbb62012-05-25 09:19:21 +100098 // Commonly found format not specified by RFC 5322.
99 {
100 "Fri, 21 Nov 1997 09:55:06 -0600 (MDT)",
101 time.Date(1997, 11, 21, 9, 55, 6, 0, time.FixedZone("", -6*60*60)),
102 },
David Symonds32e36442011-06-01 14:10:21 +1000103 }
104 for _, test := range tests {
105 hdr := Header{
106 "Date": []string{test.dateStr},
107 }
108 date, err := hdr.Date()
109 if err != nil {
110 t.Errorf("Failed parsing %q: %v", test.dateStr, err)
111 continue
112 }
David Symondsdbaeb0c2011-12-05 10:05:29 +1100113 if !date.Equal(test.exp) {
David Symonds32e36442011-06-01 14:10:21 +1000114 t.Errorf("Parse of %q: got %+v, want %+v", test.dateStr, date, test.exp)
115 }
116 }
117}
David Symondsff0198b2011-06-06 16:46:14 +1000118
119func TestAddressParsing(t *testing.T) {
120 tests := []struct {
121 addrsStr string
122 exp []*Address
123 }{
124 // Bare address
125 {
126 `jdoe@machine.example`,
Russ Coxdcf1d7b2011-12-02 14:14:25 -0500127 []*Address{{
David Symondsff0198b2011-06-06 16:46:14 +1000128 Address: "jdoe@machine.example",
129 }},
130 },
131 // RFC 5322, Appendix A.1.1
132 {
133 `John Doe <jdoe@machine.example>`,
Russ Coxdcf1d7b2011-12-02 14:14:25 -0500134 []*Address{{
David Symondsff0198b2011-06-06 16:46:14 +1000135 Name: "John Doe",
136 Address: "jdoe@machine.example",
137 }},
138 },
139 // RFC 5322, Appendix A.1.2
140 {
141 `"Joe Q. Public" <john.q.public@example.com>`,
Russ Coxdcf1d7b2011-12-02 14:14:25 -0500142 []*Address{{
David Symondsff0198b2011-06-06 16:46:14 +1000143 Name: "Joe Q. Public",
144 Address: "john.q.public@example.com",
145 }},
146 },
147 {
148 `Mary Smith <mary@x.test>, jdoe@example.org, Who? <one@y.test>`,
149 []*Address{
Russ Coxdcf1d7b2011-12-02 14:14:25 -0500150 {
David Symondsff0198b2011-06-06 16:46:14 +1000151 Name: "Mary Smith",
152 Address: "mary@x.test",
153 },
Russ Coxdcf1d7b2011-12-02 14:14:25 -0500154 {
David Symondsff0198b2011-06-06 16:46:14 +1000155 Address: "jdoe@example.org",
156 },
Russ Coxdcf1d7b2011-12-02 14:14:25 -0500157 {
David Symondsff0198b2011-06-06 16:46:14 +1000158 Name: "Who?",
159 Address: "one@y.test",
160 },
161 },
162 },
163 {
164 `<boss@nil.test>, "Giant; \"Big\" Box" <sysservices@example.net>`,
165 []*Address{
Russ Coxdcf1d7b2011-12-02 14:14:25 -0500166 {
David Symondsff0198b2011-06-06 16:46:14 +1000167 Address: "boss@nil.test",
168 },
Russ Coxdcf1d7b2011-12-02 14:14:25 -0500169 {
David Symondsff0198b2011-06-06 16:46:14 +1000170 Name: `Giant; "Big" Box`,
171 Address: "sysservices@example.net",
172 },
173 },
174 },
175 // RFC 5322, Appendix A.1.3
176 // TODO(dsymonds): Group addresses.
David Symondsffd01f22011-06-09 10:18:36 +1000177
178 // RFC 2047 "Q"-encoded ISO-8859-1 address.
179 {
180 `=?iso-8859-1?q?J=F6rg_Doe?= <joerg@example.com>`,
181 []*Address{
Russ Coxdcf1d7b2011-12-02 14:14:25 -0500182 {
David Symondsffd01f22011-06-09 10:18:36 +1000183 Name: `Jörg Doe`,
184 Address: "joerg@example.com",
185 },
186 },
187 },
188 // RFC 2047 "Q"-encoded UTF-8 address.
189 {
190 `=?utf-8?q?J=C3=B6rg_Doe?= <joerg@example.com>`,
191 []*Address{
Russ Coxdcf1d7b2011-12-02 14:14:25 -0500192 {
David Symondsffd01f22011-06-09 10:18:36 +1000193 Name: `Jörg Doe`,
194 Address: "joerg@example.com",
195 },
196 },
197 },
David Symonds63639dd2011-06-10 08:47:27 +1000198 // RFC 2047, Section 8.
199 {
200 `=?ISO-8859-1?Q?Andr=E9?= Pirard <PIRARD@vm1.ulg.ac.be>`,
201 []*Address{
Russ Coxdcf1d7b2011-12-02 14:14:25 -0500202 {
David Symonds63639dd2011-06-10 08:47:27 +1000203 Name: `André Pirard`,
204 Address: "PIRARD@vm1.ulg.ac.be",
205 },
206 },
207 },
David Symonds371aa142011-06-14 17:32:47 +1000208 // Custom example of RFC 2047 "B"-encoded ISO-8859-1 address.
209 {
210 `=?ISO-8859-1?B?SvZyZw==?= <joerg@example.com>`,
211 []*Address{
Russ Coxdcf1d7b2011-12-02 14:14:25 -0500212 {
David Symonds371aa142011-06-14 17:32:47 +1000213 Name: `Jörg`,
214 Address: "joerg@example.com",
215 },
216 },
217 },
218 // Custom example of RFC 2047 "B"-encoded UTF-8 address.
219 {
David Symonds371aa142011-06-14 17:32:47 +1000220 `=?UTF-8?B?SsO2cmc=?= <joerg@example.com>`,
221 []*Address{
Russ Coxdcf1d7b2011-12-02 14:14:25 -0500222 {
David Symonds371aa142011-06-14 17:32:47 +1000223 Name: `Jörg`,
224 Address: "joerg@example.com",
225 },
226 },
227 },
Ryan Slade73b8baa2013-08-08 10:00:24 -0700228 // Custom example with "." in name. For issue 4938
229 {
230 `Asem H. <noreply@example.com>`,
231 []*Address{
232 {
233 Name: `Asem H.`,
234 Address: "noreply@example.com",
235 },
236 },
237 },
David Symondsff0198b2011-06-06 16:46:14 +1000238 }
239 for _, test := range tests {
Graham Miller9f807fc2012-10-05 10:08:54 +1000240 if len(test.exp) == 1 {
241 addr, err := ParseAddress(test.addrsStr)
242 if err != nil {
243 t.Errorf("Failed parsing (single) %q: %v", test.addrsStr, err)
244 continue
245 }
246 if !reflect.DeepEqual([]*Address{addr}, test.exp) {
247 t.Errorf("Parse (single) of %q: got %+v, want %+v", test.addrsStr, addr, test.exp)
248 }
249 }
250
251 addrs, err := ParseAddressList(test.addrsStr)
David Symondsff0198b2011-06-06 16:46:14 +1000252 if err != nil {
Graham Miller9f807fc2012-10-05 10:08:54 +1000253 t.Errorf("Failed parsing (list) %q: %v", test.addrsStr, err)
David Symondsff0198b2011-06-06 16:46:14 +1000254 continue
255 }
256 if !reflect.DeepEqual(addrs, test.exp) {
Graham Miller9f807fc2012-10-05 10:08:54 +1000257 t.Errorf("Parse (list) of %q: got %+v, want %+v", test.addrsStr, addrs, test.exp)
David Symondsff0198b2011-06-06 16:46:14 +1000258 }
259 }
260}
David Symonds5c32c962011-06-08 13:32:47 +1000261
262func TestAddressFormatting(t *testing.T) {
263 tests := []struct {
264 addr *Address
265 exp string
266 }{
267 {
268 &Address{Address: "bob@example.com"},
269 "<bob@example.com>",
270 },
271 {
272 &Address{Name: "Bob", Address: "bob@example.com"},
273 `"Bob" <bob@example.com>`,
274 },
275 {
276 // note the ö (o with an umlaut)
277 &Address{Name: "Böb", Address: "bob@example.com"},
278 `=?utf-8?q?B=C3=B6b?= <bob@example.com>`,
279 },
280 }
281 for _, test := range tests {
282 s := test.addr.String()
283 if s != test.exp {
284 t.Errorf("Address%+v.String() = %v, want %v", *test.addr, s, test.exp)
285 }
286 }
287}