multi_mail 0.1.0 → 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +57 -34
  3. data/bin/multi_mail_post +71 -0
  4. data/lib/mail_ext/message.rb +11 -0
  5. data/lib/multi_mail.rb +25 -8
  6. data/lib/multi_mail/cloudmailin/receiver.rb +24 -3
  7. data/lib/multi_mail/mailgun/message.rb +6 -1
  8. data/lib/multi_mail/mailgun/sender.rb +4 -4
  9. data/lib/multi_mail/mandrill/message.rb +4 -3
  10. data/lib/multi_mail/mandrill/sender.rb +9 -2
  11. data/lib/multi_mail/message/base.rb +14 -0
  12. data/lib/multi_mail/postmark/message.rb +74 -0
  13. data/lib/multi_mail/postmark/receiver.rb +2 -1
  14. data/lib/multi_mail/postmark/sender.rb +54 -24
  15. data/lib/multi_mail/sendgrid/message.rb +4 -4
  16. data/lib/multi_mail/sendgrid/sender.rb +2 -2
  17. data/lib/multi_mail/simple/receiver.rb +31 -1
  18. data/lib/multi_mail/version.rb +1 -1
  19. data/multi_mail.gemspec +2 -1
  20. data/spec/cloudmailin/receiver_spec.rb +89 -84
  21. data/spec/fixtures/cloudmailin/json/attachment_store.txt +65 -0
  22. data/spec/fixtures/cloudmailin/multipart/attachment_store.txt +174 -0
  23. data/spec/fixtures/cloudmailin/raw/attachment_store.txt +162 -0
  24. data/spec/fixtures/mailgun/parsed/valid.txt +107 -101
  25. data/spec/fixtures/simple/invalid.txt +4 -0
  26. data/spec/fixtures/simple/missing.txt +4 -0
  27. data/spec/fixtures/simple/valid.txt +1 -1
  28. data/spec/mail_ext/message_spec.rb +45 -0
  29. data/spec/mailgun/message_spec.rb +38 -8
  30. data/spec/mailgun/receiver_spec.rb +104 -110
  31. data/spec/mailgun/sender_spec.rb +13 -7
  32. data/spec/mandrill/message_spec.rb +25 -1
  33. data/spec/mandrill/receiver_spec.rb +81 -83
  34. data/spec/mandrill/sender_spec.rb +13 -6
  35. data/spec/message/base_spec.rb +33 -1
  36. data/spec/postmark/message_spec.rb +292 -0
  37. data/spec/postmark/receiver_spec.rb +46 -48
  38. data/spec/postmark/sender_spec.rb +10 -10
  39. data/spec/sendgrid/message_spec.rb +6 -1
  40. data/spec/sendgrid/receiver_spec.rb +56 -58
  41. data/spec/sendgrid/sender_spec.rb +9 -7
  42. data/spec/service_spec.rb +1 -1
  43. data/spec/simple/receiver_spec.rb +38 -25
  44. data/spec/spec_helper.rb +6 -8
  45. metadata +185 -203
@@ -1,5 +1,3 @@
1
- require 'multi_mail/mailgun/message'
2
-
3
1
  module MultiMail
4
2
  module Sender
5
3
  # Mailgun's outgoing mail sender.
@@ -14,6 +12,8 @@ module MultiMail
14
12
  #
15
13
  # @param [Hash] options required and optional arguments
16
14
  # @option options [String] :api_key a Mailgun API key
15
+ # @option options [String] :domain the Mailgun email domain
16
+ # @see http://documentation.mailgun.com/api-intro.html#base-url
17
17
  def initialize(options = {})
18
18
  super
19
19
  @api_key = settings.delete(:api_key)
@@ -62,7 +62,7 @@ module MultiMail
62
62
  case response.status
63
63
  when 401
64
64
  raise InvalidAPIKey, response.body
65
- when 400
65
+ when 400, 404
66
66
  body = JSON.load(response.body)
67
67
  case body['message']
68
68
  when "'from' parameter is missing"
@@ -72,7 +72,7 @@ module MultiMail
72
72
  when "Need at least one of 'text' or 'html' parameters specified"
73
73
  raise MissingBody, body['message']
74
74
  else
75
- raise InvalidMessage, body['message']
75
+ raise InvalidRequest, body['message']
76
76
  end
77
77
  when 200
78
78
  body = JSON.load(response.body)
@@ -22,16 +22,16 @@ module MultiMail
22
22
  #
23
23
  # @return [Hash] the message headers in Mandrill format
24
24
  def mandrill_headers
25
- headers = {}
25
+ hash = {}
26
26
  header_fields.each do |field|
27
27
  key = field.name.downcase
28
28
  # Mandrill only allows Reply-To and X-* headers currently.
29
29
  # https://mandrillapp.com/api/docs/messages.ruby.html
30
30
  if key == 'reply-to' || key.start_with?('x-')
31
- headers[field.name] = field.value
31
+ hash[field.name] = field.value
32
32
  end
33
33
  end
34
- headers
34
+ hash
35
35
  end
36
36
 
37
37
  # Returns the message's attachments in Mandrill format.
@@ -65,6 +65,7 @@ module MultiMail
65
65
  'headers' => mandrill_headers,
66
66
  'attachments' => attachments,
67
67
  'images' => images,
68
+ 'tags' => tags,
68
69
  }
69
70
 
70
71
  normalize(hash)
@@ -1,5 +1,3 @@
1
- require 'multi_mail/mandrill/message'
2
-
3
1
  module MultiMail
4
2
  module Sender
5
3
  # Mandrill's outgoing mail sender.
@@ -14,13 +12,22 @@ module MultiMail
14
12
  #
15
13
  # @param [Hash] options required and optional arguments
16
14
  # @option options [String] :api_key a Mandrill API key
15
+ # @option options [Boolean] :async whether to enable a background sending
16
+ # mode optimized for bulk sending
17
+ # @option options [String] :ip_pool the name of the dedicated IP pool that
18
+ # should be used to send the message
19
+ # @option options [Time,String] :send_at when this message should be sent
17
20
  # @see https://mandrillapp.com/api/docs/index.ruby.html
21
+ # @see https://mandrillapp.com/api/docs/messages.JSON.html#method-send
18
22
  def initialize(options = {})
19
23
  super
20
24
  @api_key = settings.delete(:api_key)
21
25
  @async = settings.delete(:async) || false
22
26
  @ip_pool = settings.delete(:ip_pool)
23
27
  @send_at = settings.delete(:send_at)
28
+ unless @send_at.nil? or String === @send_at
29
+ @send_at = @send_at.utc.strftime('%Y-%m-%d %T')
30
+ end
24
31
  end
25
32
 
26
33
  # Returns the additional parameters for the API call.
@@ -22,6 +22,20 @@ module MultiMail
22
22
  end
23
23
  end
24
24
 
25
+ def tags
26
+ if self['tag']
27
+ if self['tag'].respond_to?(:map)
28
+ self['tag'].map do |field|
29
+ field.value
30
+ end
31
+ else
32
+ [self['tag'].value]
33
+ end
34
+ else
35
+ []
36
+ end
37
+ end
38
+
25
39
  private
26
40
 
27
41
  def normalize(hash)
@@ -0,0 +1,74 @@
1
+ module MultiMail
2
+ module Message
3
+ # @see http://developer.postmarkapp.com/developer-build.html#message-format
4
+ class Postmark < MultiMail::Message::Base
5
+ # Returns the message headers in Postmark format.
6
+ #
7
+ # @return [Multimap] the message headers in Postmark format
8
+ def postmark_headers
9
+ array = []
10
+ header_fields.each do |field|
11
+ key = field.name.downcase
12
+ # @see https://github.com/wildbit/postmark-gem/blob/master/lib/postmark/message_extensions/mail.rb#L74
13
+ # @see https://github.com/wildbit/postmark-gem/pull/36#issuecomment-22298955
14
+ unless %w(from to cc bcc reply-to subject tag content-type date).include?(key) || (Array === field.value && field.value.size > 1)
15
+ array << {'Name' => field.name, 'Value' => field.value}
16
+ end
17
+ end
18
+ array
19
+ end
20
+
21
+ # Returns the message's attachments in Postmark format.
22
+ #
23
+ # @return [Multimap] the attachments in Postmark format
24
+ # @see http://developer.postmarkapp.com/developer-build.html#attachments
25
+ def postmark_attachments
26
+ attachments.map do |attachment|
27
+ hash = {
28
+ 'ContentType' => attachment.content_type,
29
+ 'Name' => attachment.filename,
30
+ 'Content' => Base64.encode64(attachment.body.decoded)
31
+ }
32
+ if attachment.content_type.start_with?('image/')
33
+ hash['ContentID'] = attachment.filename
34
+ end
35
+ hash
36
+ end
37
+ end
38
+
39
+ # Returns the message as parameters to POST to Postmark.
40
+ #
41
+ # @return [Hash] the message as parameters to POST to Postmark
42
+ def to_postmark_hash
43
+ hash = {}
44
+
45
+ %w(from subject).each do |field|
46
+ if self[field]
47
+ hash[postmark_key(field)] = self[field].value
48
+ end
49
+ end
50
+
51
+ %w(to cc bcc reply_to).each do |field|
52
+ if self[field]
53
+ value = self[field].value
54
+ hash[postmark_key(field)] = value.respond_to?(:join) ? value.join(', ') : value
55
+ end
56
+ end
57
+
58
+ hash['TextBody'] = body_text
59
+ hash['HtmlBody'] = body_html
60
+ hash['Headers'] = postmark_headers
61
+ hash['Attachments'] = postmark_attachments
62
+ hash['Tag'] = tags.first
63
+
64
+ normalize(hash)
65
+ end
66
+
67
+ private
68
+
69
+ def postmark_key(string)
70
+ string.downcase.split(/[_-]/).map(&:capitalize).join
71
+ end
72
+ end
73
+ end
74
+ end
@@ -16,7 +16,8 @@ module MultiMail
16
16
  cc = params['CcFull'].map{|hash| transform_address(hash)}
17
17
 
18
18
  message = Mail.new do
19
- headers headers
19
+ headers headers
20
+ message_id params['MessageID']
20
21
 
21
22
  from from
22
23
  to to
@@ -1,42 +1,72 @@
1
- begin
2
- require 'postmark'
3
- rescue LoadError
4
- raise 'The postmark gem is not available. In order to use the Postmark sender, you must: gem install postmark'
5
- end
6
-
7
1
  module MultiMail
8
2
  module Sender
9
3
  # Postmark's outgoing mail sender.
10
4
  class Postmark
11
5
  include MultiMail::Sender::Base
12
6
 
13
- # @see https://github.com/wildbit/postmark-gem#communicating-with-the-api
14
7
  requires :api_key
15
8
 
9
+ attr_reader :api_key
10
+
11
+ # Initializes a Postmark outgoing email sender.
12
+ #
13
+ # @param [Hash] options required and optional arguments
14
+ # @option options [String] :api_key a Postmark API key
15
+ # @see http://developer.postmarkapp.com/developer-build.html#authentication-headers
16
+ def initialize(options = {})
17
+ super
18
+ @api_key = settings.delete(:api_key)
19
+ end
20
+
16
21
  # Delivers a message via the Postmark API.
17
22
  #
18
23
  # @param [Mail::Message] mail a message
19
- # @see https://github.com/wildbit/postmark-gem#using-postmark-with-the-mail-library
24
+ # @see http://developer.postmarkapp.com/developer-build.html
25
+ # @see http://developer.postmarkapp.com/developer-build.html#http-response-codes
26
+ # @see http://developer.postmarkapp.com/developer-build.html#api-error-codes
20
27
  def deliver!(mail)
21
- mail.delivery_method Mail::Postmark, settings
28
+ parameters = settings.dup
29
+ parameters.delete(:return_response)
30
+ message = MultiMail::Message::Postmark.new(mail).to_postmark_hash.merge(parameters)
22
31
 
23
- if settings[:return_response]
24
- mail.deliver!
25
- else
26
- mail.deliver
32
+ response = Faraday.post do |request|
33
+ request.url 'https://api.postmarkapp.com/email'
34
+ request.headers['Accept'] = 'application/json'
35
+ request.headers['Content-Type'] = 'application/json'
36
+ request.headers['X-Postmark-Server-Token'] = @api_key
37
+ request.body = JSON.dump(message)
38
+ end
39
+
40
+ body = JSON.load(response.body)
41
+
42
+ unless response.status == 200
43
+ case body['ErrorCode']
44
+ when 0
45
+ raise InvalidAPIKey, body['Message']
46
+ when 300
47
+ case body['Message']
48
+ when "Header 'Content-Type' not allowed."
49
+ raise InvalidHeader, body['Message']
50
+ when "Header 'Date' not allowed."
51
+ raise InvalidHeader, body['Message']
52
+ when "Invalid 'From' value."
53
+ raise MissingSender, body['Message']
54
+ when 'Zero recipients specified'
55
+ raise MissingRecipients, body['Message']
56
+ when 'Provide either email TextBody or HtmlBody or both.'
57
+ raise MissingBody, body['Message']
58
+ else
59
+ raise InvalidMessage, body['Message']
60
+ end
61
+ else
62
+ raise InvalidRequest, body['Message']
63
+ end
27
64
  end
28
- rescue ::Postmark::InvalidApiKeyError => e
29
- raise InvalidAPIKey, e.message
30
- rescue ::Postmark::InvalidMessageError => e
31
- case e.message
32
- when "Invalid 'From' value."
33
- raise MissingSender, e.message
34
- when 'Zero recipients specified'
35
- raise MissingRecipients, e.message
36
- when 'Provide either email TextBody or HtmlBody or both.'
37
- raise MissingBody, e.message
65
+
66
+ if settings[:return_response]
67
+ body
38
68
  else
39
- raise InvalidMessage, e.message
69
+ self
40
70
  end
41
71
  end
42
72
  end
@@ -6,15 +6,15 @@ module MultiMail
6
6
  #
7
7
  # @return [Hash] the message headers in SendGrid format
8
8
  def sendgrid_headers
9
- headers = {}
9
+ hash = {}
10
10
  header_fields.each do |field|
11
11
  key = field.name.downcase
12
- unless %w(to subject from bcc reply-to date message-id).include?(key)
12
+ unless %w(to subject from bcc reply-to date).include?(key)
13
13
  # The JSON must not contain integers.
14
- headers[field.name] = field.value.to_s
14
+ hash[field.name] = field.value.to_s
15
15
  end
16
16
  end
17
- headers
17
+ hash
18
18
  end
19
19
 
20
20
  # Returns the message's attachments in SendGrid format.
@@ -1,5 +1,3 @@
1
- require 'multi_mail/sendgrid/message'
2
-
3
1
  module MultiMail
4
2
  module Sender
5
3
  # SendGrid's outgoing mail sender.
@@ -14,6 +12,8 @@ module MultiMail
14
12
  # @param [Hash] options required and optional arguments
15
13
  # @option options [String] :api_user a SendGrid API user
16
14
  # @option options [String] :api_key a SendGrid API key
15
+ # @option options [Hash,String] the X-SMTPAPI SendGrid header
16
+ # @see http://sendgrid.com/docs/API_Reference/SMTP_API/index.html
17
17
  def initialize(options = {})
18
18
  super
19
19
  if Hash === settings[:'x-smtpapi']
@@ -1,8 +1,33 @@
1
1
  module MultiMail
2
2
  module Receiver
3
+ # A simple incoming email receiver.
3
4
  class Simple
4
5
  include MultiMail::Receiver::Base
5
6
 
7
+ recognizes :secret
8
+
9
+ # Initializes a simple incoming email receiver.
10
+ #
11
+ # @param [Hash] options required and optional arguments
12
+ # @option options [String] :secret a secret key
13
+ def initialize(options = {})
14
+ super
15
+ @secret = options[:secret]
16
+ end
17
+
18
+ # Returns whether a request is authentic.
19
+ #
20
+ # @param [Hash] params the content of the webhook
21
+ # @return [Boolean] whether the request is authentic
22
+ # @raise [IndexError] if the request is missing parameters
23
+ def valid?(params)
24
+ if @secret
25
+ params.fetch('signature') == signature(params)
26
+ else
27
+ super
28
+ end
29
+ end
30
+
6
31
  # Expects a raw email message parsable by the Mail gem.
7
32
  #
8
33
  # @param [Hash] params the content of the webhook
@@ -10,6 +35,11 @@ module MultiMail
10
35
  def transform(params)
11
36
  [Mail.new(params)]
12
37
  end
38
+
39
+ def signature(params)
40
+ data = "#{params.fetch('timestamp')}#{params.fetch('token')}"
41
+ OpenSSL::HMAC.hexdigest('sha256', @secret, data)
42
+ end
13
43
  end
14
44
  end
15
- end
45
+ end
@@ -1,3 +1,3 @@
1
1
  module MultiMail
2
- VERSION = "0.1.0"
2
+ VERSION = "0.1.1"
3
3
  end
data/multi_mail.gemspec CHANGED
@@ -9,6 +9,7 @@ Gem::Specification.new do |s|
9
9
  s.email = ["info@opennorth.ca"]
10
10
  s.homepage = "http://github.com/opennorth/multi_mail"
11
11
  s.summary = %q{Easily switch between email APIs}
12
+ s.license = 'MIT'
12
13
 
13
14
  s.files = `git ls-files`.split("\n")
14
15
  s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
@@ -25,7 +26,7 @@ Gem::Specification.new do |s|
25
26
  s.add_development_dependency 'json', '~> 1.7.7' # to silence coveralls warning
26
27
  s.add_development_dependency 'rake'
27
28
  s.add_development_dependency 'rspec', '~> 2.10'
28
- s.add_development_dependency 'vcr', '~> 2.0'
29
+ s.add_development_dependency 'vcr', '~> 2.4.0'
29
30
 
30
31
  # For Rake tasks
31
32
  s.add_development_dependency 'mandrill-api', '~> 1.0.35'
@@ -2,108 +2,113 @@ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
2
  require 'multi_mail/cloudmailin/receiver'
3
3
 
4
4
  describe MultiMail::Receiver::Cloudmailin do
5
- context 'after initialization' do
6
- context 'with invalid HTTP POST format' do
7
- let :service do
8
- MultiMail::Receiver.new({
9
- :provider => :cloudmailin,
10
- :http_post_format => 'invalid',
11
- })
12
- end
5
+ context 'with invalid HTTP POST format' do
6
+ let :service do
7
+ MultiMail::Receiver.new({
8
+ :provider => :cloudmailin,
9
+ :http_post_format => 'invalid',
10
+ })
11
+ end
13
12
 
14
- describe '#transform' do
15
- it 'should raise an error if :http_post_format is invalid' do
16
- expect{ service.transform({}) }.to raise_error(ArgumentError)
17
- end
13
+ describe '#transform' do
14
+ it 'should raise an error if :http_post_format is invalid' do
15
+ expect{ service.transform({}) }.to raise_error(ArgumentError)
18
16
  end
19
17
  end
18
+ end
20
19
 
21
- [false, true].each do |action_dispatch|
22
- let :action_dispatch do
23
- action_dispatch
24
- end
20
+ [false, true].each do |action_dispatch|
21
+ let :action_dispatch do
22
+ action_dispatch
23
+ end
25
24
 
26
- ['raw', 'json', 'multipart', '', nil].each do |http_post_format|
27
- context "with #{http_post_format.inspect} format and #{action_dispatch ? 'ActionDispatch' : 'Rack'}" do
28
- let :http_post_format do
29
- http_post_format
30
- end
25
+ ['raw', 'json', 'multipart', '', nil].each do |http_post_format|
26
+ context "with #{http_post_format.inspect} format and #{action_dispatch ? 'ActionDispatch' : 'Rack'}" do
27
+ let :actual_http_post_format do
28
+ http_post_format.to_s.empty? ? 'raw' : http_post_format
29
+ end
31
30
 
32
- let :actual_http_post_format do
33
- http_post_format.to_s.empty? ? 'raw' : http_post_format
31
+ let :service do
32
+ MultiMail::Receiver.new({
33
+ :provider => :cloudmailin,
34
+ :http_post_format => http_post_format,
35
+ })
36
+ end
37
+
38
+ def params(fixture)
39
+ MultiMail::Receiver::Cloudmailin.parse(response("cloudmailin/#{actual_http_post_format}", fixture, action_dispatch))
40
+ end
41
+
42
+ describe '#transform' do
43
+ it 'should return a mail message' do
44
+ helper(service.transform(params('valid')))
34
45
  end
35
46
 
36
- let :service do
37
- MultiMail::Receiver.new({
47
+ it 'should return a mail message with URL attachments' do
48
+ helper(MultiMail::Receiver.new({
38
49
  :provider => :cloudmailin,
39
50
  :http_post_format => http_post_format,
40
- })
51
+ :attachment_store => true,
52
+ }).transform(params('attachment_store')), true)
41
53
  end
42
54
 
43
- def params(fixture)
44
- MultiMail::Receiver::Cloudmailin.parse(response("cloudmailin/#{actual_http_post_format}", fixture, action_dispatch))
45
- end
55
+ def helper(messages, attachment_store = false)
56
+ messages.size.should == 1
57
+ message = messages[0]
58
+
59
+ # Headers
60
+ message.date.should == DateTime.parse('Mon, 15 Apr 2013 20:20:12 -04:00')
61
+ message.from.should == ['james@opennorth.ca']
62
+ message.to.should == ['5dae6f85cd65d30d384a@cloudmailin.net']
63
+ message.subject.should == 'Test'
46
64
 
47
- describe '#transform' do
48
- it 'should return a mail message' do
49
- messages = service.transform(params('valid'))
50
- messages.size.should == 1
51
- message = messages[0]
52
-
53
- # Headers
54
- message.date.should == DateTime.parse('Mon, 15 Apr 2013 20:20:12 -04:00')
55
- message.from.should == ['james@opennorth.ca']
56
- message.to.should == ['5dae6f85cd65d30d384a@cloudmailin.net']
57
- message.subject.should == 'Test'
58
-
59
- # Body
60
- message.multipart?.should == true
61
- message.parts.size.should == 4
62
- text_part = message.parts.find{|part| part.content_type == 'text/plain'}
63
- html_part = message.parts.find{|part| part.content_type == 'text/html; charset=UTF-8'}
64
- text_part.body.decoded.should == "bold text\n\n\n\nsome more bold text\n\n\n\nsome italic text\n\n> multiline\n> quoted\n> text\n\n\n--\nSignature block"
65
-
66
- # @note Due to a Cloudmailin bug, the HTML part is missing content
67
- # unless you use the "raw" HTTP POST format.
68
- if actual_http_post_format == 'raw'
69
- html_part.body.decoded.should == %(<html><head></head><body style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space; "><b>bold text</b><div><br></div><div></div></body></html><html><body style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space; "><head></head><br><div></div><div><br></div><div><b>some more bold text</b></div><div><b><br></b></div><div><b></b></div></body></html><html><head></head><body style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space; "><br><div><b></b></div><div><b><span class="Apple-style-span" style="font-weight: normal; "><br></span></b></div><div><b><span class="Apple-style-span" style="font-weight: normal; "><i>some italic text</i></span></b></div><div><b><span class="Apple-style-span" style="font-weight: normal; "><br></span></b></div><div><blockquote type="cite">multiline</blockquote><blockquote type="cite">quoted</blockquote><blockquote type="cite">text</blockquote></div><div><br></div><div>--</div><div>Signature block</div></body></html>)
70
- else
71
- html_part.body.decoded.should == %(<html><head></head><body style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space; "><b>bold text</b><div><br></div><div></div></body></html>)
72
- end
73
-
74
- # Attachments
75
- attachment0 = message.attachments.find{|attachment| attachment.filename == 'foo.txt'}
76
- attachment1 = message.attachments.find{|attachment| attachment.filename == 'bar.txt'}
77
- # @note Cloudmailin removes the newline at the end of the file,
78
- # unless you use the "raw" HTTP POST format.
79
- if actual_http_post_format == 'raw'
80
- attachment0.read.should == "Lorem ipsum dolor sit amet, consectetur adipiscing elit.\n"
81
- attachment1.read.should == "Nam accumsan euismod eros et rhoncus.\n"
82
- else
83
- attachment0.read.should == "Lorem ipsum dolor sit amet, consectetur adipiscing elit."
84
- attachment1.read.should == "Nam accumsan euismod eros et rhoncus."
85
- end
86
-
87
- # Extra Cloudmailin parameters
88
- if actual_http_post_format == 'raw'
89
- message['reply_plain'].should be_nil
90
- else
91
- message['reply_plain'].value.should == "bold text\n\n\n\nsome more bold text\n\n\n\nsome italic text\n"
92
- end
93
- message['spf-result'].value.should == 'pass'
65
+ # Body
66
+ message.multipart?.should == true
67
+ message.parts.size.should == 4
68
+ text_part = message.parts.find{|part| part.content_type == 'text/plain'}
69
+ html_part = message.parts.find{|part| part.content_type == 'text/html; charset=UTF-8'}
70
+ text_part.body.decoded.should == "bold text\n\n\n\nsome more bold text\n\n\n\nsome italic text\n\n> multiline\n> quoted\n> text\n\n\n--\nSignature block"
71
+
72
+ # @note Due to a Cloudmailin bug, the HTML part is missing content
73
+ # unless you use the "raw" HTTP POST format or URL attachments.
74
+ if actual_http_post_format == 'raw' || attachment_store
75
+ html_part.body.decoded.should == %(<html><head></head><body style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space; "><b>bold text</b><div><br></div><div></div></body></html><html><body style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space; "><head></head><br><div></div><div><br></div><div><b>some more bold text</b></div><div><b><br></b></div><div><b></b></div></body></html><html><head></head><body style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space; "><br><div><b></b></div><div><b><span class="Apple-style-span" style="font-weight: normal; "><br></span></b></div><div><b><span class="Apple-style-span" style="font-weight: normal; "><i>some italic text</i></span></b></div><div><b><span class="Apple-style-span" style="font-weight: normal; "><br></span></b></div><div><blockquote type="cite">multiline</blockquote><blockquote type="cite">quoted</blockquote><blockquote type="cite">text</blockquote></div><div><br></div><div>--</div><div>Signature block</div></body></html>)
76
+ else
77
+ html_part.body.decoded.should == %(<html><head></head><body style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space; "><b>bold text</b><div><br></div><div></div></body></html>)
94
78
  end
95
- end
96
79
 
97
- describe '#spam?' do
98
- it 'should return true if the response is spam' do
99
- message = service.transform(params('spam'))[0]
100
- service.spam?(message).should == true
80
+ # Attachments
81
+ attachment0 = message.attachments.find{|attachment| attachment.filename == 'foo.txt'}
82
+ attachment1 = message.attachments.find{|attachment| attachment.filename == 'bar.txt'}
83
+ # @note Cloudmailin removes the newline at the end of the file,
84
+ # unless you use the "raw" HTTP POST format or URL attachments.
85
+ if actual_http_post_format == 'raw' || attachment_store
86
+ attachment0.read.should == "Lorem ipsum dolor sit amet, consectetur adipiscing elit.\n"
87
+ attachment1.read.should == "Nam accumsan euismod eros et rhoncus.\n"
88
+ else
89
+ attachment0.read.should == "Lorem ipsum dolor sit amet, consectetur adipiscing elit."
90
+ attachment1.read.should == "Nam accumsan euismod eros et rhoncus."
101
91
  end
102
92
 
103
- it 'should return false if the response is ham' do
104
- message = service.transform(params('valid'))[0]
105
- service.spam?(message).should == false
93
+ # Extra Cloudmailin parameters
94
+ if actual_http_post_format == 'raw'
95
+ message['reply_plain'].should be_nil
96
+ else
97
+ message['reply_plain'].value.should == "bold text\n\n\n\nsome more bold text\n\n\n\nsome italic text\n"
106
98
  end
99
+ message['spf-result'].value.should == 'pass'
100
+ end
101
+ end
102
+
103
+ describe '#spam?' do
104
+ it 'should return true if the response is spam' do
105
+ message = service.transform(params('spam'))[0]
106
+ service.spam?(message).should == true
107
+ end
108
+
109
+ it 'should return false if the response is ham' do
110
+ message = service.transform(params('valid'))[0]
111
+ service.spam?(message).should == false
107
112
  end
108
113
  end
109
114
  end