mime 0.1 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,616 @@
1
+ # THIS IS STILL HERE BECAUSE A LOT OF WORK WENT INTO MAKING TESTS AGNOSTIC OF MIME HEADER ORDER.
2
+ # HOWEVER, THINGS GOT MESSY SO REVERTED BACK TO COMPARING AGAINST KNOWN GOOD MESSAGES.
3
+ # THIS IS HERE JUST IN CASE. IN THE FUTURE, REMOVE IT IF THERE IS NO NEED FOR IT.
4
+
5
+ gem 'minitest' # minitest in 1.9 stdlib is crufty
6
+ require 'minitest/autorun'
7
+ require 'mime'
8
+
9
+ # may be able to remove in 2.0
10
+ Encoding.default_external = 'ASCII-8BIT'
11
+
12
+ class MIMETest < Minitest::Test
13
+
14
+ CRLF = "\r\n"
15
+ HDR_BDY_SEP = CRLF * 2
16
+ MESSAGE_ID = /^Message-ID: <\d+@\d+>\r\n/
17
+ CONTENT_ID = /^Content-ID: <\d+\.\d+>\r\n/
18
+
19
+ CDISPOSITION = /^Content-Disposition: .*\r\n/
20
+ CTYPE = /^Content-Type: .*\r\n/
21
+
22
+ DATE = /^Date: ..., \d{1,2} ... \d{4} \d\d:\d\d:\d\d -\d{4}\r\n/
23
+ VERSION = /^MIME-Version: 1.0 \(Ruby MIME v\d\.\d\)\r\n/
24
+ #BOUNDARY = /^--Boundary_\d+\.\d+\r\n/
25
+ BOUNDARY = /^--Boundary_\d+\.\d+(--)?\r\n/
26
+ BOUNDARY_LAST = /^--Boundary_\d+\.\d+--\r\n/
27
+ CID = /cid:\d+\.\d+/
28
+
29
+ CTYPE_TEXT_PLAIN = /^Content-Type: text\/plain; charset=us-ascii\r\n/
30
+ CTYPE_TEXT_HTML = /^Content-Type: text\/html\r\n/
31
+ CTYPE_TEXT_XML = /^Content-Type: text\/xml\r\n/
32
+ CTYPE_IMAGE_JPEG = /^Content-Type: image\/jpeg\r\n/
33
+ CTYPE_IMAGE_PNG = /^Content-Type: image\/png\r\n/
34
+ CTYPE_VIDEO_MPEG = /^Content-Type: video\/mpeg\r\n/
35
+ CTYPE_AUDIO_MIDI = /^Content-Type: audio\/midi\r\n/
36
+ CTYPE_APPLICATION = /^Content-Type: application\/octet-stream\r\n/
37
+
38
+ CTYPE_MPART_FORM = /^Content-Type: multipart\/form-data; boundary=Boundary_\d+\.\d+\r\n/
39
+ CTYPE_MPART_ALT = /^Content-Type: multipart\/alternative; boundary=Boundary_\d+\.\d+\r\n/
40
+ CTYPE_MPART_MIXED = /^Content-Type: multipart\/mixed; boundary=Boundary_\d+\.\d+\r\n/
41
+
42
+ XFER_ENC_BINARY = /^Content-Transfer-Encoding: binary\r\n/
43
+ XFER_ENC_8BIT = /^Content-Transfer-Encoding: 8bit\r\n/
44
+
45
+ BINARY_DATA = '0110000101110101011001000110100101101111'
46
+
47
+ def test_make_top_level_rfc2822_message
48
+ rfc2822_msg = MIME::Message.new
49
+ rfc2822_msg.body = "\r\nmessage body"
50
+ msg = rfc2822_msg.to_s
51
+
52
+ assert_match MESSAGE_ID, msg
53
+ assert_match DATE, msg
54
+ assert_match VERSION, msg
55
+ assert_equal_num_headers 3, msg
56
+ assert_equal_body "message body", msg
57
+ end
58
+
59
+ # TODO remove audio.msg
60
+ def test_make_audio_message
61
+ audio_media = MIME::AudioMedia.new(BINARY_DATA, 'audio/midi')
62
+ audio_media.content_transfer_encoding = 'binary'
63
+ msg = MIME::Message.new(audio_media).to_s
64
+
65
+ [CONTENT_ID, XFER_ENC_BINARY, CTYPE_AUDIO_MIDI].each do |header|
66
+ assert_match header, msg
67
+ end
68
+ assert_equal_num_headers 6, msg
69
+ assert_equal_body BINARY_DATA, msg
70
+ end
71
+
72
+ # TODO remove expected_mime_msg = IO.read(sd('/application.msg'))
73
+ def test_make_application_message
74
+ application_media = MIME::ApplicationMedia.new(BINARY_DATA)
75
+ application_media.content_transfer_encoding = 'binary'
76
+ msg = MIME::Message.new(application_media).to_s
77
+
78
+ [CONTENT_ID, XFER_ENC_BINARY, CTYPE_APPLICATION].each do |header|
79
+ assert_match header, msg
80
+ end
81
+ assert_equal_num_headers 6, msg
82
+ assert_equal_body BINARY_DATA, msg
83
+ end
84
+
85
+ # TODO remove image.msg
86
+ def test_make_image_message
87
+ image = IO.read(sd('/image.jpg'))
88
+ image_media = MIME::ImageMedia.new(image)
89
+ image_media.content_type = 'image/jpeg'
90
+ image_media.content_transfer_encoding = 'binary'
91
+ msg = MIME::Message.new(image_media).to_s
92
+
93
+ [CONTENT_ID, XFER_ENC_BINARY, CTYPE_IMAGE_JPEG].each do |header|
94
+ assert_match header, msg
95
+ end
96
+
97
+ assert_equal_num_headers 6, msg
98
+ assert_equal_body image, msg
99
+ end
100
+
101
+ # TODO remove text.msg
102
+ def test_make_text_message
103
+ body = 'a plain text message'
104
+ msg = MIME::Message.new(MIME::TextMedia.new(body)).to_s
105
+
106
+ [CTYPE_TEXT_PLAIN].each do |header|
107
+ assert_match header, msg
108
+ end
109
+ end
110
+
111
+ # TODO remove video.msg
112
+ def test_make_video_message
113
+ video_media = MIME::VideoMedia.new(BINARY_DATA)
114
+ video_media.content_type = 'video/mpeg'
115
+ video_media.content_transfer_encoding = 'binary'
116
+ msg = MIME::Message.new(video_media).to_s
117
+
118
+ [CONTENT_ID, XFER_ENC_BINARY, CTYPE_VIDEO_MPEG].each do |header|
119
+ assert_match header, msg
120
+ end
121
+ assert_equal_num_headers 6, msg
122
+ assert_equal_body BINARY_DATA, msg
123
+ end
124
+
125
+ def test_no_instantiation_of_abstract_classes
126
+ e = MIME::AbstractClassError
127
+ assert_raises(e) {MIME::MediaType.new(nil, nil)}
128
+ assert_raises(e) {MIME::DiscreteMediaType.new(nil)}
129
+ assert_raises(e) {MIME::CompositeMediaType.new(nil)}
130
+ assert_raises(e) {MIME::MultipartMedia.new(nil)}
131
+ end
132
+
133
+ # def test_boundaries
134
+ # # CREATE a multipart message; take simplified version of next form data
135
+ # # test
136
+ # e = msg.scan(BOUNDARY).each
137
+ # first_boundary = e.next
138
+ # assert_equal first_boundary, e.next
139
+ # assert_equal first_boundary, e.next
140
+ # last_boundary = e.next
141
+ # refute_equal first_boundary, last_boundary
142
+ # assert_match /^--Boundary_\d+\.\d+\r\n/, first_boundary
143
+ # assert_match /^--Boundary_\d+\.\d+--\r\n/, last_boundary
144
+ # end
145
+ #
146
+ # def test_unique_content_ids_in_multipart_message
147
+ #
148
+ # content_ids = msg.scan(/^Content-ID: <(\d+.\d+)>/)
149
+ # assert_equal 4, content_ids.flatten.uniq.size # all IDs must be unique
150
+ # end
151
+
152
+ # TODO rm multipart_form_data_text.msg
153
+ def test_multipart_form_data_with_text_entities
154
+ txt_data = 'text body'
155
+ htm_data = IO.read(sd('data.htm'))
156
+ xml_data = IO.read(sd('data.xml'))
157
+
158
+ txt = MIME::TextMedia.new('text body')
159
+ htm = MIME::TextMedia.new(htm_data, 'text/html')
160
+ xml = MIME::TextMedia.new(xml_data, 'text/xml')
161
+
162
+ form_data = MIME::MultipartMedia::FormData.new
163
+ form_data.add_entity txt, 'txt'
164
+ form_data.add_entity htm, 'htm'
165
+ form_data.add_entity xml, 'xml'
166
+ msg = form_data.to_s
167
+
168
+ parts = msg.split(BOUNDARY)
169
+ assert_equal 5, parts.size, 'main header, 3 entities, last boundary'
170
+ assert_equal '--', parts.pop, 'remnant of last boundary marker'
171
+
172
+ # main header
173
+ assert_match CONTENT_ID, parts[0]
174
+ assert_match CTYPE_MPART_FORM, parts[0]
175
+ assert_equal_num_headers 2, parts[0]
176
+ assert_equal_body '', parts[0]
177
+
178
+ # xml entity
179
+ assert_match CONTENT_ID, parts[1]
180
+ assert_match CTYPE_TEXT_XML, parts[1]
181
+ assert_match /^Content-Disposition: form-data; name="xml"\r\n/, parts[1]
182
+ assert_equal_num_headers 3, parts[1]
183
+ assert_equal_body xml_data, parts[1]
184
+
185
+ # html entity
186
+ assert_match CONTENT_ID, parts[2]
187
+ assert_match CTYPE_TEXT_HTML, parts[2]
188
+ assert_match /^Content-Disposition: form-data; name="htm"\r\n/, parts[2]
189
+ assert_equal_num_headers 3, parts[2]
190
+ assert_equal_body htm_data, parts[2]
191
+
192
+ # text entity
193
+ assert_match CONTENT_ID, parts[3]
194
+ assert_match CTYPE_TEXT_PLAIN, parts[3]
195
+ assert_match /^Content-Disposition: form-data; name="txt"\r\n/, parts[3]
196
+ assert_equal_num_headers 3, parts[3]
197
+ assert_equal_body txt_data, parts[3]
198
+ end
199
+
200
+ # TODO test that only basename of the file is included in header
201
+ # test if no filename is used, then probably no filename param
202
+ # see next test for ideas
203
+ def test_content_disposition_filename
204
+
205
+ pass
206
+ end
207
+
208
+ # rm multipart_form_data_file.msg
209
+ # rm multipart_form_data_file_and_text.msg
210
+ def test_multipart_form_data_with_text_and_file_entities
211
+ img1_filename = 'image.jpg'
212
+ img2_filename = 'ruby.png'
213
+ img1_data = IO.read(sd(img1_filename))
214
+ img2_data = IO.read(sd(img2_filename))
215
+ img1 = MIME::ImageMedia.new(img1_data, 'image/jpeg')
216
+ img2 = MIME::ImageMedia.new(img2_data, 'image/png')
217
+ img1.content_transfer_encoding = '8bit'
218
+ img2.content_transfer_encoding = '8bit'
219
+
220
+ desc_data = 'This is plain text description of images.'
221
+ desc = MIME::TextMedia.new(desc_data)
222
+
223
+ form_data = MIME::MultipartMedia::FormData.new
224
+ form_data.add_entity desc, 'description'
225
+ form_data.add_entity img2, 'image_2', img2_filename
226
+ form_data.add_entity img1, 'image_1', img1_filename
227
+ msg = form_data.to_s
228
+
229
+ parts = msg.split(BOUNDARY)
230
+ assert_equal 5, parts.size, 'main header, 3 entities, last boundary'
231
+ assert_equal '--', parts.pop, 'remnant of last boundary marker'
232
+
233
+ # main header
234
+ assert_match CONTENT_ID, parts[0]
235
+ assert_match CTYPE_MPART_FORM, parts[0]
236
+ assert_equal_num_headers 2, parts[0]
237
+ assert_equal_body '', parts[0]
238
+
239
+ # first image entity
240
+ assert_match CONTENT_ID, parts[1]
241
+ assert_match CTYPE_IMAGE_JPEG, parts[1]
242
+ assert_match XFER_ENC_8BIT, parts[1]
243
+ assert_match /^Content-Disposition: form-data; name="image_1"; filename="#{img1_filename}"\r\n/, parts[1]
244
+ assert_equal_num_headers 4, parts[1]
245
+ assert_equal_body img1_data, parts[1]
246
+
247
+ # second image entity
248
+ assert_match CONTENT_ID, parts[2]
249
+ assert_match CTYPE_IMAGE_PNG, parts[2]
250
+ assert_match XFER_ENC_8BIT, parts[2]
251
+ assert_match /^Content-Disposition: form-data; name="image_2"; filename="#{img2_filename}"\r\n/, parts[2]
252
+ assert_equal_num_headers 4, parts[2]
253
+ assert_equal_body img2_data, parts[2]
254
+
255
+ # plain text entity
256
+ assert_match CONTENT_ID, parts[3]
257
+ assert_match CTYPE_TEXT_PLAIN, parts[3]
258
+ assert_match /^Content-Disposition: form-data; name="description"\r\n/, parts[3]
259
+ assert_equal_num_headers 3, parts[3]
260
+ assert_equal_body desc_data, parts[3]
261
+ end
262
+
263
+ # rm plain_text_email.msg
264
+ def test_construction_of_plain_text_email_message
265
+ subject = 'This is an important email'
266
+ body = 'This is the all important email body.'
267
+
268
+ email_msg = MIME::Message.new
269
+ email_msg.to = {
270
+ 'john@example.com' => 'John',
271
+ 'paul@example.com' => nil,
272
+ 'mary@example.com' => 'Mary'
273
+ }
274
+ email_msg.cc = {'Head Honcho' => 'boss@example.com'}
275
+ email_msg.from = {'jane@example.com' => nil}
276
+ email_msg.subject = subject
277
+ email_msg.body = MIME::TextMedia.new(body)
278
+
279
+ msg = email_msg.to_s
280
+
281
+ assert_equal_num_headers 9, msg
282
+ assert_match MESSAGE_ID, msg
283
+ assert_match CONTENT_ID, msg
284
+ assert_match CTYPE_TEXT_PLAIN, msg
285
+ assert_match DATE, msg
286
+ assert_match VERSION, msg
287
+ assert_match /^Subject: #{subject}\r\n/, msg
288
+ assert_match /^From: jane@example.com\r\n/, msg
289
+ assert_match /^To: .+, .+, .+\r\n/, msg # 3 addresses
290
+ assert_match /^To: .*John <john@example.com>.*\r\n/, msg
291
+ assert_match /^To: .*Mary <mary@example.com>.*\r\n/, msg
292
+ assert_match /^To: .*paul@example.com.*\r\n/, msg
293
+ assert_match /^Cc: boss@example.com <Head Honcho>\r\n/, msg
294
+ assert_equal_body body, msg
295
+ end
296
+
297
+ def test_content_type_detection
298
+ (o = Object.new).extend(MIME::ContentTypes)
299
+
300
+ # test using file path, relative and absolute
301
+ assert_equal 'application/pdf', o.file_type('book.pdf')
302
+ assert_equal 'video/quicktime', o.file_type('mini.mov')
303
+ assert_equal 'application/octet-stream', o.file_type('dsk.iso')
304
+ assert_equal 'audio/mpeg', o.file_type('/tmp/song.mp3')
305
+ assert_equal 'text/css', o.file_type('/tmp/main.css')
306
+ assert_equal nil, o.file_type('unknown.yyy')
307
+
308
+ # test using file object
309
+ img_type = open(sd('ruby.png')) {|f| o.file_type(f)}
310
+ assert_equal 'image/png', img_type
311
+ refute_equal 'image/jpeg', img_type
312
+ end
313
+
314
+ def test_object_instantiation_using_discrete_media_factory
315
+ app_file = sd('book.pdf')
316
+ audio_file = sd('song.mp3')
317
+ text_file = sd('data.xml')
318
+ video_file = sd('mini.mov')
319
+ image_file = sd('image.jpg')
320
+ unknown_file = sd('unknown.yyy')
321
+
322
+ dmf = MIME::DiscreteMediaFactory
323
+
324
+ # test using file path
325
+ assert_kind_of MIME::ApplicationMedia, dmf.create(app_file)
326
+ assert_kind_of MIME::AudioMedia, dmf.create(audio_file)
327
+ assert_kind_of MIME::TextMedia, dmf.create(text_file)
328
+ assert_kind_of MIME::VideoMedia, dmf.create(video_file)
329
+ assert_kind_of MIME::ImageMedia, dmf.create(image_file)
330
+
331
+ # test using file object
332
+ open(image_file) do |image_file_obj|
333
+ assert_kind_of MIME::ImageMedia, dmf.create(image_file_obj)
334
+ end
335
+ open(text_file) do |text_file_obj|
336
+ assert_kind_of MIME::TextMedia, dmf.create(text_file_obj)
337
+ end
338
+
339
+ # raise for unknown file path and File object
340
+ assert_raises(MIME::UnknownContentError) {dmf.create(unknown_file)}
341
+ open(unknown_file) do |unknown_file_obj|
342
+ assert_raises(MIME::UnknownContentError) {dmf.create(unknown_file_obj)}
343
+ end
344
+ end
345
+
346
+ def test_discrete_media_factory_creates_path_singleton_method
347
+ pdf_file_path = sd('book.pdf')
348
+
349
+ media_obj = MIME::DiscreteMediaFactory.create(pdf_file_path)
350
+ assert_equal pdf_file_path, media_obj.path
351
+
352
+ open(pdf_file_path) do |pdf_file_obj|
353
+ media_obj = MIME::DiscreteMediaFactory.create(pdf_file_obj)
354
+ assert_equal pdf_file_path, media_obj.path
355
+ end
356
+ end
357
+
358
+ # rm multipart_alternative.msg
359
+ def test_multipart_alternative_message_construction
360
+ txt_data = "*Header*\nmessage"
361
+ htm_data = "<html><body><h1>Header</h1><p>message</p></body></html>"
362
+ txt_msg = MIME::TextMedia.new(txt_data)
363
+ htm_msg = MIME::TextMedia.new(htm_data)
364
+
365
+ txt_msg.content_type = (txt_type = 'text/enhanced; charset=us-ascii')
366
+ htm_msg.content_type = (htm_type = 'text/html; charset=iso-8859-1')
367
+
368
+ alt_msg = MIME::MultipartMedia::Alternative.new
369
+ alt_msg.add_entity htm_msg
370
+ alt_msg.add_entity txt_msg
371
+ msg = alt_msg.to_s
372
+
373
+ parts = msg.split(BOUNDARY)
374
+ assert_equal 4, parts.size, 'main header, 2 entities, last boundary'
375
+ assert_equal '--', parts.pop, 'remnant of last boundary marker'
376
+
377
+ # main header
378
+ assert_match CONTENT_ID, parts[0]
379
+ assert_match CTYPE_MPART_ALT, parts[0]
380
+ assert_equal_num_headers 2, parts[0]
381
+ assert_equal_body '', parts[0]
382
+
383
+ # text entity
384
+ assert_match CONTENT_ID, parts[1]
385
+ assert_match /^Content-Type: #{txt_type}\r\n/, parts[1]
386
+ assert_equal_num_headers 2, parts[1]
387
+ assert_equal_body txt_data, parts[1]
388
+
389
+ # html entity
390
+ assert_match CONTENT_ID, parts[2]
391
+ assert_match /^Content-Type: #{htm_type}\r\n/, parts[2]
392
+ assert_equal_num_headers 2, parts[2]
393
+ assert_equal_body htm_data, parts[2]
394
+ end
395
+
396
+ # rm multipart_mixed_inline_and_attachment.msg
397
+ def test_multipart_mixed_with_inline_and_attachment
398
+ mixed_msg = MIME::MultipartMedia::Mixed.new
399
+
400
+ img_filename = 'image.jpg'
401
+ img_data = ''
402
+ open(sd(img_filename)) do |img_file|
403
+ img_data = img_file.read
404
+ img_msg = MIME::ImageMedia.new(img_data, 'image/jpeg')
405
+ mixed_msg.attach_entity(img_msg, 'filename' => img_file.path)
406
+ end
407
+
408
+ txt_data = 'This is plain text.'
409
+ mixed_msg.inline_entity(MIME::TextMedia.new(txt_data))
410
+ msg = mixed_msg.to_s
411
+
412
+ parts = msg.split(BOUNDARY)
413
+ assert_equal 4, parts.size, 'main header, 2 entities, last boundary'
414
+ assert_equal '--', parts.pop, 'remnant of last boundary marker'
415
+
416
+ # main header
417
+ assert_match CONTENT_ID, parts[0]
418
+ assert_match CTYPE_MPART_MIXED, parts[0]
419
+ assert_equal_num_headers 2, parts[0]
420
+ assert_equal_body '', parts[0]
421
+
422
+ # text entity
423
+ assert_match CONTENT_ID, parts[1]
424
+ assert_match CTYPE_TEXT_PLAIN, parts[1]
425
+ assert_match /^Content-Disposition: inline\r\n/, parts[1]
426
+ assert_equal_num_headers 3, parts[1]
427
+ assert_equal_body txt_data, parts[1]
428
+
429
+ # image entity
430
+ assert_match CONTENT_ID, parts[2]
431
+ assert_match CTYPE_IMAGE_JPEG, parts[2]
432
+ assert_match /^Content-Disposition: attachment; filename="#{img_filename}"\r\n/, parts[2]
433
+ assert_equal_num_headers 3, parts[2]
434
+ assert_equal_body img_data, parts[2]
435
+ end
436
+
437
+ # rm multipart_mixed_inline_and_attachment2.msg
438
+ def test_multipart_mixed_message_construction_using_media_factory
439
+ img1 = sd(img1_filename = 'image.jpg')
440
+ img2 = sd(img2_filename = 'ruby.png')
441
+ txt = sd(txt_filename = 'data.htm')
442
+ bot_img = MIME::DiscreteMediaFactory.create(img1)
443
+ top_img = MIME::DiscreteMediaFactory.create(img2)
444
+ top_txt = MIME::DiscreteMediaFactory.create(txt)
445
+
446
+ mixed_msg = MIME::MultipartMedia::Mixed.new
447
+ mixed_msg.attach_entity(bot_img)
448
+ mixed_msg.attach_entity(top_img)
449
+ mixed_msg.inline_entity(top_txt)
450
+ msg = mixed_msg.to_s
451
+
452
+ parts = msg.split(BOUNDARY)
453
+ assert_equal 5, parts.size, 'main header, 2 entities, last boundary'
454
+ assert_equal '--', parts.pop, 'remnant of last boundary marker'
455
+
456
+ # main header
457
+ assert_match CONTENT_ID, parts[0]
458
+ assert_match CTYPE_MPART_MIXED, parts[0]
459
+ assert_equal_num_headers 2, parts[0]
460
+ assert_equal_body '', parts[0]
461
+
462
+ # html entity
463
+ assert_match CONTENT_ID, parts[1]
464
+ assert_match CTYPE_TEXT_HTML, parts[1]
465
+ assert_match /^Content-Disposition: inline; filename="#{txt_filename}"\r\n/, parts[1]
466
+ assert_equal_num_headers 3, parts[1]
467
+ assert_equal_body top_txt.send(:body), parts[1]
468
+
469
+ # png image entity
470
+ assert_match CONTENT_ID, parts[2]
471
+ assert_match CTYPE_IMAGE_PNG, parts[2]
472
+ assert_match /^Content-Disposition: attachment; filename="#{img2_filename}"\r\n/, parts[2]
473
+ assert_equal_num_headers 3, parts[2]
474
+ assert_equal_body top_img.send(:body), parts[2]
475
+
476
+ # jpg image entity
477
+ assert_match CONTENT_ID, parts[3]
478
+ assert_match CTYPE_IMAGE_JPEG, parts[3]
479
+ assert_match /^Content-Disposition: attachment; filename="#{img1_filename}"\r\n/, parts[3]
480
+ assert_equal_num_headers 3, parts[3]
481
+ assert_equal_body bot_img.send(:body), parts[3]
482
+ end
483
+
484
+ def test_multipart_form_data_with_mixed_entity
485
+ txt = MIME::TextMedia.new('Joe Blow')
486
+ img1 = MIME::DiscreteMediaFactory.create(sd('image.jpg'))
487
+ img2 = MIME::DiscreteMediaFactory.create(sd('ruby.png'))
488
+
489
+ mixed_msg = MIME::MultipartMedia::Mixed.new
490
+ mixed_msg.attach_entity(img2)
491
+ mixed_msg.attach_entity(img1)
492
+
493
+ form = MIME::MultipartMedia::FormData.new
494
+ form.add_entity(mixed_msg, 'pics')
495
+ form.add_entity(txt, 'field1')
496
+
497
+ # similar to example 6 in RFC1867
498
+ expected = IO.read(sd('multipart_form_data_mixed.msg'))
499
+
500
+ assert_equal_mime_message expected, form.to_s
501
+
502
+ #IO.write('/tmp/mime.out', msg)
503
+ end
504
+
505
+ # def test_multipart_related_html_message_with_embedded_image
506
+ # img = DiscreteMediaFactory.create(sd('/ruby.png'))
507
+ # img.content_transfer_encoding = 'binary'
508
+ #
509
+ # html_msg = TextMedia.new(<<-html, 'text/html; charset=iso-8859-1')
510
+ # <html>
511
+ # <body>
512
+ # <h1>HTML multipart/related message</h1>
513
+ # <p>txt before pix</p>
514
+ # <img alt="cool ruby" src="cid:#{img.content_id}"/>
515
+ # <p>txt after pix</p>
516
+ # </body>
517
+ # </html>
518
+ # html
519
+ # html_msg.content_transfer_encoding = '7bit'
520
+ #
521
+ # related_msg = MultipartMedia::Related.new
522
+ # related_msg.inline_entity(img)
523
+ # related_msg.add_entity(html_msg)
524
+ #
525
+ # expected = IO.read(sd('/multipart_related.msg'))
526
+ #
527
+ # assert_equal_mime_message expected, related_msg.to_s
528
+ # end
529
+ #
530
+ # def test_multipart_alternative_with_related_html_entity
531
+ # img = DiscreteMediaFactory.create(sd('/ruby.png'))
532
+ # img.content_transfer_encoding = 'binary'
533
+ #
534
+ # html_msg = TextMedia.new(<<-html, 'text/html; charset=iso-8859-1')
535
+ # <html>
536
+ # <body>
537
+ # <h1>HTML multipart/alternative message</h1>
538
+ # <p>txt before pix</p>
539
+ # <img alt="cool ruby" src="cid:#{img.content_id}"/>
540
+ # <p>txt after pix</p>
541
+ # </body>
542
+ # </html>
543
+ # html
544
+ # html_msg.content_transfer_encoding = '7bit'
545
+ #
546
+ # text_msg = TextMedia.new(<<-text)
547
+ # *HTML multipart/alternative message*
548
+ # txt before pix
549
+ # <cool ruby image>
550
+ # txt after pix
551
+ # text
552
+ # text_msg.content_transfer_encoding = '7bit'
553
+ #
554
+ # related_msg = MultipartMedia::Related.new
555
+ # related_msg.inline_entity(img)
556
+ # related_msg.add_entity(html_msg)
557
+ #
558
+ # alt_msg = MultipartMedia::Alternative.new
559
+ # alt_msg.add_entity(related_msg)
560
+ # alt_msg.add_entity(text_msg)
561
+ #
562
+ # expected = IO.read(sd('/multipart_alternative_related.msg'))
563
+ #
564
+ # assert_equal_mime_message expected, alt_msg.to_s
565
+ # end
566
+
567
+
568
+ private
569
+
570
+ #
571
+ # Test the equality of the normalized +expected+ and +actual+ MIME messages.
572
+ #
573
+ def assert_equal_mime_message expected, actual
574
+ assert_equal normalize_message(expected), normalize_message(actual)
575
+ end
576
+
577
+ #
578
+ # Make messages comparable by removing *-ID header values, library version
579
+ # comment in MIME-Version header, Date header value, multipart/related
580
+ # content IDs, and boundaries.
581
+ #
582
+ def normalize_message message
583
+ # these are very delicate REs that are inter-dependent, be careful
584
+ match_id_headers = /-ID: <[^>]+>\r\n/
585
+ match_boundaries = /Boundary_\d+\.\d+/
586
+ match_lib_version = / \(Ruby MIME v\d\.\d\)/
587
+ match_date_header = /^Date: .*\d{4}\r\n/
588
+ match_related_cid = /cid:\d+\.\d+/
589
+
590
+ message.
591
+ gsub(match_related_cid, "cid").
592
+ gsub(match_id_headers, "-ID:\r\n").
593
+ gsub(match_boundaries, "Boundary_").
594
+ sub(match_date_header, "Date:\r\n").
595
+ sub(match_lib_version, " (Ruby MIME v0.0)")
596
+ end
597
+
598
+ def assert_equal_num_headers num, msg
599
+ assert_equal num, msg.split(HDR_BDY_SEP).first.split(CRLF).count
600
+ end
601
+
602
+ def assert_equal_body expected_body, msg
603
+ # FIXME the next line should be a fix
604
+ headers, actual_body = msg.split(HDR_BDY_SEP)
605
+ assert_equal expected_body, actual_body.to_s.chomp
606
+ #assert_equal body, msg.split("\r\n").last
607
+ end
608
+
609
+ #
610
+ # Return the absolute path of +file+ under the test/scaffold directory.
611
+ #
612
+ def sd file
613
+ File.join(File.dirname(__FILE__), 'scaffold', file)
614
+ end
615
+
616
+ end