astrotrain 0.3.1

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.
Files changed (57) hide show
  1. data/.gitignore +26 -0
  2. data/LICENSE +20 -0
  3. data/README +47 -0
  4. data/Rakefile +122 -0
  5. data/VERSION +1 -0
  6. data/astrotrain.gemspec +129 -0
  7. data/config/sample.rb +12 -0
  8. data/lib/astrotrain.rb +53 -0
  9. data/lib/astrotrain/api.rb +52 -0
  10. data/lib/astrotrain/logged_mail.rb +46 -0
  11. data/lib/astrotrain/mapping.rb +157 -0
  12. data/lib/astrotrain/mapping/http_post.rb +18 -0
  13. data/lib/astrotrain/mapping/jabber.rb +28 -0
  14. data/lib/astrotrain/mapping/transport.rb +55 -0
  15. data/lib/astrotrain/message.rb +330 -0
  16. data/lib/astrotrain/tmail.rb +58 -0
  17. data/lib/astrotrain/worker.rb +65 -0
  18. data/lib/vendor/rest-client/README.rdoc +104 -0
  19. data/lib/vendor/rest-client/Rakefile +84 -0
  20. data/lib/vendor/rest-client/bin/restclient +65 -0
  21. data/lib/vendor/rest-client/foo.diff +66 -0
  22. data/lib/vendor/rest-client/lib/rest_client.rb +188 -0
  23. data/lib/vendor/rest-client/lib/rest_client/net_http_ext.rb +23 -0
  24. data/lib/vendor/rest-client/lib/rest_client/payload.rb +185 -0
  25. data/lib/vendor/rest-client/lib/rest_client/request_errors.rb +75 -0
  26. data/lib/vendor/rest-client/lib/rest_client/resource.rb +103 -0
  27. data/lib/vendor/rest-client/rest-client.gemspec +18 -0
  28. data/lib/vendor/rest-client/spec/base.rb +5 -0
  29. data/lib/vendor/rest-client/spec/master_shake.jpg +0 -0
  30. data/lib/vendor/rest-client/spec/payload_spec.rb +71 -0
  31. data/lib/vendor/rest-client/spec/request_errors_spec.rb +44 -0
  32. data/lib/vendor/rest-client/spec/resource_spec.rb +52 -0
  33. data/lib/vendor/rest-client/spec/rest_client_spec.rb +219 -0
  34. data/test/api_test.rb +28 -0
  35. data/test/fixtures/apple_multipart.txt +100 -0
  36. data/test/fixtures/bad_content_type.txt +27 -0
  37. data/test/fixtures/basic.txt +14 -0
  38. data/test/fixtures/custom.txt +15 -0
  39. data/test/fixtures/fwd.txt +0 -0
  40. data/test/fixtures/gb2312_encoding.txt +16 -0
  41. data/test/fixtures/gb2312_encoding_invalid.txt +15 -0
  42. data/test/fixtures/html.txt +16 -0
  43. data/test/fixtures/iso-8859-1.txt +13 -0
  44. data/test/fixtures/mapped.txt +13 -0
  45. data/test/fixtures/multipart.txt +213 -0
  46. data/test/fixtures/multipart2.txt +213 -0
  47. data/test/fixtures/multiple.txt +13 -0
  48. data/test/fixtures/multiple_delivered_to.txt +14 -0
  49. data/test/fixtures/multiple_with_body_recipients.txt +15 -0
  50. data/test/fixtures/reply.txt +16 -0
  51. data/test/fixtures/utf-8.txt +13 -0
  52. data/test/logged_mail_test.rb +67 -0
  53. data/test/mapping_test.rb +129 -0
  54. data/test/message_test.rb +440 -0
  55. data/test/test_helper.rb +57 -0
  56. data/test/transport_test.rb +111 -0
  57. metadata +225 -0
@@ -0,0 +1,440 @@
1
+ require File.join(File.dirname(__FILE__), "test_helper")
2
+
3
+ class Astrotrain::MessageTest < Astrotrain::TestCase
4
+ describe "mapping" do
5
+ describe "against default domain" do
6
+ before :all do
7
+ Astrotrain::Mapping.transaction do
8
+ Astrotrain::LoggedMail.all.destroy!
9
+ Astrotrain::Mapping.all.destroy!
10
+ @mapping = Astrotrain::Mapping.create!(:email_user => 'xyz')
11
+ @mapping2 = Astrotrain::Mapping.create!(:email_user => 'xyz', :email_domain => 'sample.com')
12
+ end
13
+ end
14
+
15
+ describe "without mapping" do
16
+ before do
17
+ Astrotrain::LoggedMail.all.destroy!
18
+ end
19
+
20
+ it "doesn't log message" do
21
+ @msg = Astrotrain::Message.receive(mail(:basic))
22
+ @log = Astrotrain::LoggedMail.first
23
+ assert_nil @log
24
+ end
25
+
26
+ it "logs message if Astrotrain::LoggedMail.log_processed" do
27
+ Astrotrain::LoggedMail.log_processed = true
28
+ @msg = Astrotrain::Message.receive(mail(:basic))
29
+ @log = Astrotrain::LoggedMail.first
30
+ assert @log
31
+ assert @log.error_message.blank?
32
+ end
33
+
34
+ it "calls pre_mapping callback" do
35
+ Astrotrain::LoggedMail.log_processed = true
36
+ callback_msg = nil
37
+ Astrotrain.callback(:pre_mapping) do |message|
38
+ callback_msg = message
39
+ end
40
+
41
+ @msg = Astrotrain::Message.receive(mail(:mapped))
42
+ assert_equal callback_msg, @msg
43
+ end
44
+
45
+ it "calls pre_processing callback" do
46
+ Astrotrain::LoggedMail.log_processed = true
47
+ callback_msg, callback_map = nil
48
+ Astrotrain.callback(:pre_processing) do |message, mapping|
49
+ callback_msg = message
50
+ callback_map = mapping
51
+ end
52
+
53
+ @msg = Astrotrain::Message.receive(mail(:mapped))
54
+ @log = Astrotrain::LoggedMail.first
55
+ assert_equal callback_msg, @msg
56
+ assert_equal callback_map, @log.mapping
57
+ end
58
+
59
+ it "calls post_processing callback" do
60
+ Astrotrain::LoggedMail.log_processed = true
61
+ callback_msg, callback_map, callback_log = nil
62
+ Astrotrain.callback(:post_processing) do |message, mapping, log|
63
+ callback_msg = message
64
+ callback_map = mapping
65
+ callback_log = log
66
+ end
67
+
68
+ @msg = Astrotrain::Message.receive(mail(:mapped))
69
+ @log = Astrotrain::LoggedMail.first
70
+ assert_equal callback_msg, @msg
71
+ assert_equal callback_map, @log.mapping
72
+ assert_equal callback_log, @log
73
+ end
74
+
75
+ after do
76
+ Astrotrain::LoggedMail.log_processed = false
77
+ Astrotrain.clear_callbacks
78
+ end
79
+ end
80
+
81
+ describe "erroring" do
82
+ before do
83
+ Astrotrain::LoggedMail.all.destroy!
84
+ end
85
+
86
+ it "logs message without mappping" do
87
+ stub(Astrotrain::Mapping).match { raise RuntimeError }
88
+ @msg = Astrotrain::Message.receive(mail(:basic))
89
+ @log = Astrotrain::LoggedMail.first
90
+ assert @log
91
+ assert_nil @log.delivered_at
92
+ assert_match /RuntimeError/, @log.error_message
93
+ assert_nil @log.mapping
94
+ end
95
+
96
+ it "logs message with mappping" do
97
+ stub(Astrotrain::Mapping).match {@mapping}
98
+ stub(@mapping).process { raise RuntimeError }
99
+ @msg = Astrotrain::Message.receive(mail(:basic))
100
+ @log = Astrotrain::LoggedMail.first
101
+ assert @log
102
+ assert_nil @log.delivered_at
103
+ assert_match /RuntimeError/, @log.error_message
104
+ assert_equal @mapping, @log.mapping
105
+ end
106
+ end
107
+
108
+ describe "with mapping" do
109
+ before :all do
110
+ @msg = Astrotrain::Message.receive(mail(:mapped))
111
+ @log = Astrotrain::LoggedMail.first
112
+ end
113
+
114
+ it "does not log message" do
115
+ assert_nil @log
116
+ end
117
+ end
118
+ end
119
+ end
120
+
121
+ describe "parsing" do
122
+ before :all do
123
+ @body = "---------- Forwarded message ----------\nblah blah"
124
+ end
125
+
126
+ describe "basic, with bad content type header" do
127
+ before :all do
128
+ @raw = mail(:bad_content_type)
129
+ @message = Astrotrain::Message.parse(@raw)
130
+ end
131
+
132
+ it "parses body" do
133
+ expected = "--====boundary====\nContent-Type: text/plain; charset=\"us-ascii\"\n\nThis message is being generated automatically to notify you\nthat PowerMTA has crashed on mtasv.net.\n\nAs the information below is likely to be essential for debugging\nthe problem, please forward this message to <support@port25.com>.\nThank you.\n\n--====boundary====\nContent-Type: text/plain; charset=\"us-ascii\"\n\nYo\n--====boundary====--"
134
+ assert_equal expected, @message.body
135
+ end
136
+
137
+ it "attempts parsing bad header" do
138
+ assert_equal "multipart/mixed; boundary=\"====boundary=\"===\"\"", @message.header('content-type')
139
+ end
140
+ end
141
+
142
+ describe "basic, single sender/recipient" do
143
+ before :all do
144
+ @raw = mail(:basic)
145
+ @message = Astrotrain::Message.parse(@raw)
146
+ end
147
+
148
+ it "parses TMail::Mail headers" do
149
+ expected = {'mime-version' => '1.0', 'content-type' => 'text/plain; charset=ISO-8859-1', 'to' => 'Processor <processor@astrotrain.com>',
150
+ 'x-custom' => 'reply', 'content-transfer-encoding' => '7bit', 'content-disposition' => 'inline', 'message-id' => '<a16be7390810161014n52b603e9k1aa6bb803c6735aa@mail.gmail.com>'}
151
+ assert_equal expected, @message.headers
152
+ end
153
+
154
+ it "#parse parses TMail::Mail object from raw text" do
155
+ assert_kind_of TMail::Mail, @message.mail
156
+ end
157
+
158
+ it "recognizes Delivered-To and To: headers as recipients" do
159
+ assert_equal %w(processor@astrotrain.com), @message.recipients
160
+ end
161
+
162
+ it "recognizes From: header as sender" do
163
+ assert_equal %(Bob <user@example.com>), @message.sender
164
+ end
165
+
166
+ it "recognizes Subject: header" do
167
+ assert_equal 'Fwd: blah blah', @message.subject
168
+ end
169
+
170
+ it "recognizes message body" do
171
+ assert_equal @body, @message.body
172
+ end
173
+
174
+ it "retains raw message" do
175
+ assert_equal @raw, @message.raw
176
+ end
177
+ end
178
+
179
+ describe "iso 8859 1 encoded headers" do
180
+ before :all do
181
+ @raw = mail("iso-8859-1")
182
+ @message = Astrotrain::Message.parse(@raw)
183
+ end
184
+
185
+ it "recognizes From: header with strange encoding" do
186
+ assert_equal %(Matthéw <user@example.com>), @message.sender
187
+ end
188
+ end
189
+
190
+ describe "gb2312 encoded body" do
191
+ before :all do
192
+ @raw = mail("gb2312_encoding")
193
+ @message = Astrotrain::Message.parse(@raw)
194
+ end
195
+
196
+ it "converts to UTF-8" do
197
+ assert_equal "Dear Sirs, \r\nWe are given to understand that you are Manufacturer of plstic Bottles\r\nAdd: blah China",
198
+ @message.body
199
+ end
200
+ end
201
+
202
+ describe "gb2312 encoded body with invalid charset in mime version header" do
203
+ before :all do
204
+ @raw = mail("gb2312_encoding_invalid")
205
+ @message = Astrotrain::Message.parse(@raw)
206
+ end
207
+
208
+ it "converts to UTF-8" do
209
+ assert_equal "Dear Sirs, \r\nWe are given to understand that you are Manufacturer of plstic Bottles\r\nAdd: blah China",
210
+ @message.body
211
+ end
212
+ end
213
+
214
+ describe "utf 8 encoded headers" do
215
+ before :all do
216
+ @raw = mail("utf-8")
217
+ @message = Astrotrain::Message.parse(@raw)
218
+ end
219
+
220
+ it "recognizes From: header with strange encoding" do
221
+ assert_equal %(isnard naiké <user@example.com>), @message.sender
222
+ end
223
+ end
224
+
225
+ describe "multipart message with name property on Content Type" do
226
+ before :all do
227
+ @raw = mail(:multipart)
228
+ @message = Astrotrain::Message.parse(@raw)
229
+ end
230
+
231
+ it "#parse parses TMail::Mail object from raw text" do
232
+ assert_kind_of TMail::Mail, @message.mail
233
+ end
234
+
235
+ it "recognizes Delivered-To/To: headers as recipient" do
236
+ assert_equal %w(foo@example.com), @message.recipients
237
+ end
238
+
239
+ it "recognizes message body" do
240
+ assert_equal "Testing out rich emails with attachments!\nThis one has a name property on Content-Type.\n[state:hold responsible:rick]\n\n",
241
+ @message.body
242
+ end
243
+
244
+ it "retrieves attachments" do
245
+ assert_equal 1, @message.attachments.size
246
+ end
247
+
248
+ it "retrieves attachment filename" do
249
+ assert_equal 'bandit.jpg', @message.attachments.first.filename
250
+ end
251
+
252
+ it "retrieves attachment content_type" do
253
+ assert_equal 'image/jpeg', @message.attachments.first.content_type
254
+ end
255
+ end
256
+
257
+ describe "multipart message with filename property on Content Disposition" do
258
+ before :all do
259
+ @raw = mail(:multipart2)
260
+ @message = Astrotrain::Message.parse(@raw)
261
+ end
262
+
263
+ it "#parse parses TMail::Mail object from raw text" do
264
+ assert_kind_of TMail::Mail, @message.mail
265
+ end
266
+
267
+ it "recognizes Delivered-To/To: headers as recipient" do
268
+ assert_equal %w(foo@example.com), @message.recipients
269
+ end
270
+
271
+ it "recognizes message body" do
272
+ assert_equal "Testing out rich emails with attachments!\nThis one has NO name property on Content-Type.\n[state:hold responsible:rick]\n\n",
273
+ @message.body
274
+ end
275
+
276
+ it "retrieves attachments" do
277
+ assert_equal 1, @message.attachments.size
278
+ end
279
+
280
+ it "retrieves attachment filename" do
281
+ assert_equal 'bandit.jpg', @message.attachments.first.filename
282
+ end
283
+
284
+ it "retrieves attachment content_type" do
285
+ assert_equal 'image/jpeg', @message.attachments.first.content_type
286
+ end
287
+ end
288
+
289
+ describe "apple multipart message" do
290
+ before :all do
291
+ @raw = mail(:apple_multipart)
292
+ @message = Astrotrain::Message.parse(@raw)
293
+ end
294
+
295
+ it "#parse parses TMail::Mail object from raw text" do
296
+ assert_kind_of TMail::Mail, @message.mail
297
+ end
298
+
299
+ it "recognizes To: header as recipient" do
300
+ assert_equal %w(foo@example.com), @message.recipients
301
+ end
302
+
303
+ it "recognizes message body" do
304
+ assert_equal "Let's have a test here:\r\n\r\n\r\n\nYum\r\n\r\n\r\nOn Feb 10, 2009, at 3:37 PM, Tender Support wrote:\r\n\r\n> // Add your reply above here\r\n> ==================================================\r\n> From: Tyler Durden\r\n> Subject: Email attachments and file upload\r\n>\r\n> not at the moment ... let me test\r\n>\r\n> View this Discussion online: http://foobar.com\r\n> .\r\n\r\n\r\n\r\n\r\n--Apple-Mail-7-451386929--",
305
+ @message.body
306
+ end
307
+
308
+ it "retrieves attachments" do
309
+ assert_equal 1, @message.attachments.size
310
+ end
311
+
312
+ it "retrieves attachment filename" do
313
+ assert_equal 'logo.gif', @message.attachments.first.filename
314
+ end
315
+
316
+ it "retrieves attachment content_type" do
317
+ assert_equal 'image/gif', @message.attachments.first.content_type
318
+ end
319
+ end
320
+
321
+ describe "multiple sender/recipients" do
322
+ before :all do
323
+ @raw = mail(:multiple)
324
+ @message = Astrotrain::Message.parse(@raw)
325
+ end
326
+
327
+ it "#parse parses TMail::Mail object from raw text" do
328
+ assert_kind_of TMail::Mail, @message.mail
329
+ end
330
+
331
+ it "recognizes To: headers as recipients" do
332
+ assert_equal %w(processor@astrotrain.com other@example.com), @message.recipients
333
+ end
334
+
335
+ it "recognizes To: headers as recipients with custom header order" do
336
+ assert_equal %w(other@example.com processor@astrotrain.com), @message.recipients(%w(to original_to delivered_to))
337
+ end
338
+
339
+ it "recognizes From: header as sender" do
340
+ assert_equal %(user@example.com, boss@example.com), @message.sender
341
+ end
342
+
343
+ it "recognizes Subject: header" do
344
+ assert_equal 'Fwd: blah blah', @message.subject
345
+ end
346
+
347
+ it "recognizes message body" do
348
+ assert_equal @body, @message.body
349
+ end
350
+
351
+ it "retains raw message" do
352
+ assert_equal @raw, @message.raw
353
+ end
354
+ end
355
+
356
+ describe "recipients in the body" do
357
+ before :all do
358
+ @raw = mail(:multiple_with_body_recipients)
359
+ @message = Astrotrain::Message.parse(@raw)
360
+ end
361
+
362
+ it "recognizes in-body emails and To: headers as recipients" do
363
+ assert_equal %w(processor+foobar@astrotrain.com processor+blah@astrotrain.com processor@astrotrain.com other@example.com),
364
+ @message.recipients
365
+ end
366
+ end
367
+
368
+ describe "with only HTML body" do
369
+ before :all do
370
+ @raw = mail(:html)
371
+ @message = Astrotrain::Message.parse(@raw)
372
+ end
373
+
374
+ it "parses emtpy body" do
375
+ assert_equal '', @message.body
376
+ end
377
+
378
+ it "parses HTML body" do
379
+ assert_equal "<p>ABC</p>\n------", @message.html
380
+ end
381
+ end
382
+
383
+ describe "with X Original To header" do
384
+ before :all do
385
+ @raw = mail(:custom)
386
+ @message = Astrotrain::Message.parse(@raw)
387
+ end
388
+
389
+ it "#parse parses TMail::Mail object from raw text" do
390
+ assert_kind_of TMail::Mail, @message.mail
391
+ end
392
+
393
+ it "recognizes X-Original-to: header as recipient" do
394
+ assert_equal %w(processor-reply-57@custom.com processor-delivered@astrotrain.com processor@astrotrain.com), @message.recipients
395
+ end
396
+
397
+ it "recognizes Delivered-To: header as recipient with custom header order" do
398
+ assert_equal %w(processor-delivered@astrotrain.com processor-reply-57@custom.com processor@astrotrain.com), @message.recipients(%w(delivered_to original_to to))
399
+ end
400
+
401
+ it "recognizes To: header as recipient with custom header order" do
402
+ assert_equal %w(processor@astrotrain.com processor-reply-57@custom.com processor-delivered@astrotrain.com), @message.recipients(%w(to original_to delivered_to))
403
+ end
404
+
405
+ it "recognizes From: header as sender" do
406
+ assert_equal %(user@example.com, boss@example.com), @message.sender
407
+ end
408
+
409
+ it "recognizes Subject: header" do
410
+ assert_equal 'Fwd: blah blah', @message.subject
411
+ end
412
+
413
+ it "recognizes message body" do
414
+ assert_equal @body, @message.body
415
+ end
416
+
417
+ it "retains raw message" do
418
+ assert_equal @raw, @message.raw
419
+ end
420
+ end
421
+
422
+ describe "with multiple Delivered To headers" do
423
+ before :all do
424
+ @raw = mail(:multiple_delivered_to)
425
+ @message = Astrotrain::Message.parse(@raw)
426
+ end
427
+
428
+ it "recognizes Delivered-to: header as recipient" do
429
+ assert_equal %w(processor-reply-57@custom.com processor-delivered@astrotrain.com processor@astrotrain.com), @message.recipients
430
+ end
431
+ end
432
+ end
433
+
434
+ describe "queueing" do
435
+ it "writes contents queue path" do
436
+ filename = Astrotrain::Message.queue("boo!")
437
+ assert_equal 'boo!', IO.read(filename)
438
+ end
439
+ end
440
+ end
@@ -0,0 +1,57 @@
1
+ $testing = true
2
+ $LOAD_PATH.unshift File.join(File.dirname(__FILE__), '..', 'lib')
3
+
4
+ require "rubygems"
5
+ require "context"
6
+ require 'rr'
7
+ require 'astrotrain'
8
+ require 'astrotrain/api'
9
+ require 'sinatra/test'
10
+
11
+ module Astrotrain
12
+ load File.dirname(__FILE__) do
13
+ DataMapper.setup(:default, {
14
+ :adapter => "mysql",
15
+ :database => "astrotrain_test",
16
+ :username => "root",
17
+ :host => "localhost"
18
+ })
19
+ end
20
+
21
+ LoggedMail.auto_migrate!
22
+ Mapping.auto_migrate!
23
+
24
+ LoggedMail.log_path = Astrotrain.root / 'messages'
25
+ FileUtils.rm_rf LoggedMail.log_path
26
+ FileUtils.mkdir_p LoggedMail.log_path
27
+
28
+ Message.queue_path = root / 'fixtures' / 'queue'
29
+ FileUtils.rm_rf Message.queue_path
30
+ FileUtils.mkdir_p Message.queue_path
31
+
32
+ class TestCase < Test::Unit::TestCase
33
+ include RR::Adapters::RRMethods
34
+
35
+ before do
36
+ RR.reset
37
+ end
38
+
39
+ after do
40
+ RR.verify
41
+ end
42
+
43
+ def mail(filename)
44
+ IO.read(File.join(File.dirname(__FILE__), 'fixtures', "#{filename}.txt"))
45
+ end
46
+ end
47
+
48
+ class ApiTestCase < TestCase
49
+ include Sinatra::Test
50
+ end
51
+ end
52
+
53
+ begin
54
+ require 'ruby-debug'
55
+ Debugger.start
56
+ rescue LoadError
57
+ end