blob: 402ff1180ad052e0b8fc210bd98e0832968d8e59 [file] [log] [blame]
Dusan Kasan17e497e2017-04-10 22:44:22 +02001package parsemail_test
2
3import (
4 "testing"
5 "github.com/DusanKasan/parsemail"
6 "strings"
7 "time"
8 "net/mail"
9 "encoding/base64"
10 "io/ioutil"
11)
12
13func TestParseEmail(t *testing.T) {
14 var testData = []struct{
15 mailData string
16
17 subject string
18 from []string
19 sender string
20 to []string
21 replyTo []string
22 cc []string
23 bcc []string
24 messageID string
25 inReplyTo []string
26 references []string
27 date time.Time
28 htmlBody string
29 textBody string
30 attachments []attachmentData
31 embeddedFiles []embeddedFileData
32 headerCheck func (mail.Header, *testing.T)
33 }{
34 {
35 mailData: Data1,
36 subject: "Test Subject 1",
37 from: []string{"Peter Paholík <peter.paholik@gmail.com>"},
38 to: []string{"dusan@kasan.sk"},
39 messageID: "CACtgX4kNXE7T5XKSKeH_zEcfUUmf2vXVASxYjaaK9cCn-3zb_g@mail.gmail.com",
40 date: parseDate("Fri, 07 Apr 2017 09:17:26 +0200"),
41 htmlBody: "<div dir=\"ltr\"><br></div>",
42 attachments: []attachmentData{
43 {
44 filename: "Peter Paholík 1 4 2017 2017-04-07.pdf",
45 base64data: "JVBERi0xLjQNCiW1tbW1DQoxIDAgb2JqDQo8PC9UeXBlL0NhdGFsb2cvUGFnZXMgMiAwIFIvTGFuZyhlbi1VUykgL1N0cnVjdFRyZWVSb290IDY3IDAgUi9NYXJrSW5mbzw8L01hcmtlZCB0cnVlPj4vT3V0cHV0SW50ZW50c1s8PC9UeXBlL091dHB1dEludGVudC9TL0dUU19QREZBMS9PdXRwdXRDb25kZXYgMzk1MzYyDQo+Pg0Kc3RhcnR4cmVmDQo0MTk4ODUNCiUlRU9GDQo=",
46 },
47 },
48 },
49 {
50 mailData: Data2,
51 subject: "Re: Test Subject 2",
52 from: []string{"Sender Man <sender@domain.com>"},
53 to: []string{"info@receiver.com"},
54 cc: []string{"Cc Man <ccman@gmail.com>"},
55 messageID: "0e9a21b4-01dc-e5c1-dcd6-58ce5aa61f4f@receiver.com",
56 inReplyTo: []string{"9ff38d03-c4ab-89b7-9328-e99d5e24e3ba@receiver.eu"},
57 references: []string{"2f6b7595-c01e-46e5-42bc-f263e1c4282d@receiver.com", "9ff38d03-c4ab-89b7-9328-e99d5e24e3ba@domain.com"},
58 date: parseDate("Fri, 07 Apr 2017 12:59:55 +0200"),
59 htmlBody: `<html>data<img src="part2.9599C449.04E5EC81@develhell.com"/></html>`,
60 textBody: `First level
61> Second level
62>> Third level
63>
64`,
65 embeddedFiles: []embeddedFileData{
66 {
67 cid: "part2.9599C449.04E5EC81@develhell.com",
68 base64data: "iVBORw0KGgoAAAANSUhEUgAAAQEAAAAYCAIAAAB1IN9NAAAACXBIWXMAAAsTAAALEwEAmpwYYKUKF+Os3baUndC0pDnwNAmLy1SUr2Gw0luxQuV/AwC6cEhVV5VRrwAAAABJRU5ErkJggg==",
69 },
70 },
71 },
72 }
73
74 for _, td := range testData {
75 e, err := parsemail.Parse(strings.NewReader(td.mailData))
76 if err != nil {
77 t.Error(err)
78 }
79
80 if td.subject != e.Subject() {
81 t.Errorf("Wrong subject. Expected: %s, Got: %s", td.subject, e.Subject())
82 }
83
84 if td.sender != e.Sender() {
85 t.Errorf("Wrong sender. Expected: %s, Got: %s", td.sender, e.Sender())
86 }
87
88 if !assertSliceEq(td.from, e.From()) {
89 t.Errorf("Wrong from. Expected: %s, Got: %s", td.from, e.From())
90 }
91
92 if !assertSliceEq(td.inReplyTo, e.InReplyTo()) {
93 t.Errorf("Wrong in reply to. Expected: %s, Got: %s", td.inReplyTo, e.InReplyTo())
94 }
95
96 if !assertSliceEq(td.references, e.References()) {
97 t.Errorf("Wrong references. Expected: %s, Got: %s", td.references, e.References())
98 }
99
100 if !assertSliceEq(td.to, e.To()) {
101 t.Errorf("Wrong to. Expected: %s, Got: %s", td.to, e.To())
102 }
103
104 if !assertSliceEq(td.replyTo, e.ReplyTo()) {
105 t.Errorf("Wrong reply to. Expected: %s, Got: %s", td.replyTo, e.ReplyTo())
106 }
107
108 if !assertSliceEq(td.cc, e.Cc()) {
109 t.Errorf("Wrong cc. Expected: %s, Got: %s", td.cc, e.Cc())
110 }
111
112 if !assertSliceEq(td.bcc, e.Bcc()) {
113 t.Errorf("Wrong cc. Expected: %s, Got: %s", td.cc, e.Cc())
114 }
115
116 date, err := e.Date()
117 if err != nil {
118 t.Error(err)
119 } else if td.date != date {
120 t.Errorf("Wrong date. Expected: %v, Got: %v", td.date, date)
121 }
122
123 if td.htmlBody != e.HTMLBody {
124 t.Errorf("Wrong html body. Expected: '%s', Got: '%s'", td.htmlBody, e.HTMLBody)
125 }
126
127 if td.textBody != e.TextBody {
128 t.Errorf("Wrong text body. Expected: '%s', Got: '%s'", td.textBody, e.TextBody)
129 }
130
131 if td.messageID != e.MessageID() {
132 t.Errorf("Wrong messageID. Expected: '%s', Got: '%s'", td.messageID, e.MessageID())
133 }
134
135 if len(td.attachments) != len(e.Attachments) {
136 t.Errorf("Incorrect number of attachments! Expected: %v, Got: %v.", len(td.attachments), len(e.Attachments))
137 } else {
138 attachs := e.Attachments[:]
139
140 for _, ad := range(td.attachments) {
141 found := false
142
143 for i, ra := range(attachs) {
144 b, err := ioutil.ReadAll(ra.Data)
145 if err != nil {
146 t.Error(err)
147 }
148
149 encoded := base64.StdEncoding.EncodeToString(b)
150 if ra.Filename == ad.filename && encoded == ad.base64data {
151 found = true
152 attachs = append(attachs[:i], attachs[i+1:]...)
153 }
154 }
155
156 if !found {
157 t.Errorf("Attachment not found: %s", ad.filename)
158 }
159 }
160
161 if len(attachs) != 0 {
162 t.Errorf("Email contains %v unexpected attachments: %v", len(attachs), attachs)
163 }
164 }
165
166 if len(td.embeddedFiles) != len(e.EmbeddedFiles) {
167 t.Errorf("Incorrect number of embedded files! Expected: %s, Got: %s.", len(td.embeddedFiles), len(e.EmbeddedFiles))
168 } else {
169 embeds := e.EmbeddedFiles[:]
170
171 for _, ad := range(td.embeddedFiles) {
172 found := false
173
174 for i, ra := range(embeds) {
175 b, err := ioutil.ReadAll(ra.Data)
176 if err != nil {
177 t.Error(err)
178 }
179
180 encoded := base64.StdEncoding.EncodeToString(b)
181
182 if ra.CID == ad.cid && encoded == ad.base64data {
183 found = true
184 embeds = append(embeds[:i], embeds[i+1:]...)
185 }
186 }
187
188 if !found {
189 t.Errorf("Embedded file not found: %s", ad.cid)
190 }
191 }
192
193 if len(embeds) != 0 {
194 t.Errorf("Email contains %v unexpected embedded files: %v", len(embeds), embeds)
195 }
196 }
197 }
198}
199
200func parseDate(in string) time.Time {
201 out, err := time.Parse(time.RFC1123Z, in)
202 if err != nil {
203 panic(err)
204 }
205
206 return out
207}
208
209type attachmentData struct{
210 filename string
211 base64data string
212}
213
214type embeddedFileData struct{
215 cid string
216 base64data string
217}
218
219func assertSliceEq(a, b []string) bool {
220 if len(a) == len(b) && len(a) == 0 {
221 return true
222 }
223
224 if a == nil && b == nil {
225 return true;
226 }
227
228 if a == nil || b == nil {
229 return false;
230 }
231
232 if len(a) != len(b) {
233 return false
234 }
235
236 for i := range a {
237 if a[i] != b[i] {
238 return false
239 }
240 }
241
242 return true
243}
244
245var Data1 = `From: =?UTF-8?Q?Peter_Pahol=C3=ADk?= <peter.paholik@gmail.com>
246Date: Fri, 7 Apr 2017 09:17:26 +0200
247Message-ID: <CACtgX4kNXE7T5XKSKeH_zEcfUUmf2vXVASxYjaaK9cCn-3zb_g@mail.gmail.com>
248Subject: Test Subject 1
249To: dusan@kasan.sk
250Content-Type: multipart/mixed; boundary=f403045f1dcc043a44054c8e6bbf
251
252--f403045f1dcc043a44054c8e6bbf
253Content-Type: multipart/alternative; boundary=f403045f1dcc043a3f054c8e6bbd
254
255--f403045f1dcc043a3f054c8e6bbd
256Content-Type: text/plain; charset=UTF-8
257
258
259
260--f403045f1dcc043a3f054c8e6bbd
261Content-Type: text/html; charset=UTF-8
262
263<div dir="ltr"><br></div>
264
265--f403045f1dcc043a3f054c8e6bbd--
266--f403045f1dcc043a44054c8e6bbf
267Content-Type: application/pdf;
268 name="=?UTF-8?Q?Peter_Paholi=CC=81k_1?=
269 =?UTF-8?Q?_4_2017_2017=2D04=2D07=2Epdf?="
270Content-Disposition: attachment;
271 filename="=?UTF-8?Q?Peter_Paholi=CC=81k_1?=
272 =?UTF-8?Q?_4_2017_2017=2D04=2D07=2Epdf?="
273Content-Transfer-Encoding: base64
274X-Attachment-Id: f_j17i0f0d0
275
276JVBERi0xLjQNCiW1tbW1DQoxIDAgb2JqDQo8PC9UeXBlL0NhdGFsb2cvUGFnZXMgMiAwIFIvTGFu
277Zyhlbi1VUykgL1N0cnVjdFRyZWVSb290IDY3IDAgUi9NYXJrSW5mbzw8L01hcmtlZCB0cnVlPj4v
278T3V0cHV0SW50ZW50c1s8PC9UeXBlL091dHB1dEludGVudC9TL0dUU19QREZBMS9PdXRwdXRDb25k
279ZXYgMzk1MzYyDQo+Pg0Kc3RhcnR4cmVmDQo0MTk4ODUNCiUlRU9GDQo=
280--f403045f1dcc043a44054c8e6bbf--
281`
282
283var Data2 = `Subject: Re: Test Subject 2
284To: info@receiver.com
285References: <2f6b7595-c01e-46e5-42bc-f263e1c4282d@receiver.com>
286 <9ff38d03-c4ab-89b7-9328-e99d5e24e3ba@domain.com>
287Cc: Cc Man <ccman@gmail.com>
288From: Sender Man <sender@domain.com>
289Message-ID: <0e9a21b4-01dc-e5c1-dcd6-58ce5aa61f4f@receiver.com>
290Date: Fri, 7 Apr 2017 12:59:55 +0200
291User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.12; rv:45.0)
292 Gecko/20100101 Thunderbird/45.8.0
293MIME-Version: 1.0
294In-Reply-To: <9ff38d03-c4ab-89b7-9328-e99d5e24e3ba@receiver.eu>
295Content-Type: multipart/alternative;
296 boundary="------------C70C0458A558E585ACB75FB4"
297
298This is a multi-part message in MIME format.
299--------------C70C0458A558E585ACB75FB4
300Content-Type: text/plain; charset=utf-8; format=flowed
301Content-Transfer-Encoding: 8bit
302
303First level
304> Second level
305>> Third level
306>
307
308
309--------------C70C0458A558E585ACB75FB4
310Content-Type: multipart/related;
311 boundary="------------5DB4A1356834BB602A5F88B2"
312
313
314--------------5DB4A1356834BB602A5F88B2
315Content-Type: text/html; charset=utf-8
316Content-Transfer-Encoding: 8bit
317
318<html>data<img src="part2.9599C449.04E5EC81@develhell.com"/></html>
319
320--------------5DB4A1356834BB602A5F88B2
321Content-Type: image/png
322Content-Transfer-Encoding: base64
323Content-ID: <part2.9599C449.04E5EC81@develhell.com>
324
325iVBORw0KGgoAAAANSUhEUgAAAQEAAAAYCAIAAAB1IN9NAAAACXBIWXMAAAsTAAALEwEAmpwY
326YKUKF+Os3baUndC0pDnwNAmLy1SUr2Gw0luxQuV/AwC6cEhVV5VRrwAAAABJRU5ErkJggg==
327--------------5DB4A1356834BB602A5F88B2
328
329--------------C70C0458A558E585ACB75FB4--
330`