imap_folder_agent_spec.rb 8.6KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288
  1. require 'spec_helper'
  2. require 'time'
  3. describe Agents::ImapFolderAgent do
  4. describe 'checking IMAP' do
  5. before do
  6. @site = {
  7. 'expected_update_period_in_days' => 1,
  8. 'host' => 'mail.example.net',
  9. 'ssl' => true,
  10. 'username' => 'foo',
  11. 'password' => 'bar',
  12. 'folders' => ['INBOX'],
  13. 'conditions' => {
  14. }
  15. }
  16. @checker = Agents::ImapFolderAgent.new(:name => 'Example', :options => @site, :keep_events_for => 2.days)
  17. @checker.user = users(:bob)
  18. @checker.save!
  19. message_mixin = Module.new {
  20. def folder
  21. 'INBOX'
  22. end
  23. def uidvalidity
  24. 100
  25. end
  26. def has_attachment?
  27. false
  28. end
  29. def body_parts(mime_types = %[text/plain text/enriched text/html])
  30. mime_types.map { |type|
  31. all_parts.find { |part|
  32. part.mime_type == type
  33. }
  34. }.compact.map! { |part|
  35. part.extend(Agents::ImapFolderAgent::Message::Scrubbed)
  36. }
  37. end
  38. include Agents::ImapFolderAgent::Message::Scrubbed
  39. }
  40. @mails = [
  41. Mail.read(Rails.root.join('spec/data_fixtures/imap1.eml')).tap { |mail|
  42. mail.extend(message_mixin)
  43. stub(mail).uid.returns(1)
  44. },
  45. Mail.read(Rails.root.join('spec/data_fixtures/imap2.eml')).tap { |mail|
  46. mail.extend(message_mixin)
  47. stub(mail).uid.returns(2)
  48. stub(mail).has_attachment?.returns(true)
  49. },
  50. ]
  51. stub(@checker).each_unread_mail.returns { |yielder|
  52. seen = @checker.lastseen
  53. notified = @checker.notified
  54. @mails.each_with_object(notified) { |mail|
  55. yielder[mail, notified]
  56. seen[mail.uidvalidity] = mail.uid
  57. }
  58. @checker.lastseen = seen
  59. @checker.notified = notified
  60. nil
  61. }
  62. @payloads = [
  63. {
  64. 'folder' => 'INBOX',
  65. 'from' => 'nanashi.gombeh@example.jp',
  66. 'to' => ['jane.doe@example.com', 'john.doe@example.com'],
  67. 'cc' => [],
  68. 'date' => '2014-05-09T16:00:00+09:00',
  69. 'subject' => 'some subject',
  70. 'body' => "Some plain text\nSome second line\n",
  71. 'has_attachment' => false,
  72. 'matches' => {},
  73. 'mime_type' => 'text/plain',
  74. },
  75. {
  76. 'folder' => 'INBOX',
  77. 'from' => 'john.doe@example.com',
  78. 'to' => ['jane.doe@example.com', 'nanashi.gombeh@example.jp'],
  79. 'cc' => [],
  80. 'subject' => 'Re: some subject',
  81. 'body' => "Some reply\n",
  82. 'date' => '2014-05-09T17:00:00+09:00',
  83. 'has_attachment' => true,
  84. 'matches' => {},
  85. 'mime_type' => 'text/plain',
  86. }
  87. ]
  88. end
  89. describe 'validations' do
  90. before do
  91. expect(@checker).to be_valid
  92. end
  93. it 'should validate the integer fields' do
  94. @checker.options['expected_update_period_in_days'] = 'nonsense'
  95. expect(@checker).not_to be_valid
  96. @checker.options['expected_update_period_in_days'] = '2'
  97. expect(@checker).to be_valid
  98. @checker.options['port'] = -1
  99. expect(@checker).not_to be_valid
  100. @checker.options['port'] = 'imap'
  101. expect(@checker).not_to be_valid
  102. @checker.options['port'] = '143'
  103. expect(@checker).to be_valid
  104. @checker.options['port'] = 993
  105. expect(@checker).to be_valid
  106. end
  107. it 'should validate the boolean fields' do
  108. %w[ssl mark_as_read].each do |key|
  109. @checker.options[key] = 1
  110. expect(@checker).not_to be_valid
  111. @checker.options[key] = false
  112. expect(@checker).to be_valid
  113. @checker.options[key] = 'true'
  114. expect(@checker).to be_valid
  115. @checker.options[key] = ''
  116. expect(@checker).to be_valid
  117. end
  118. end
  119. it 'should validate regexp conditions' do
  120. @checker.options['conditions'] = {
  121. 'subject' => '(foo'
  122. }
  123. expect(@checker).not_to be_valid
  124. @checker.options['conditions'] = {
  125. 'body' => '***'
  126. }
  127. expect(@checker).not_to be_valid
  128. @checker.options['conditions'] = {
  129. 'subject' => '\ARe:',
  130. 'body' => '(?<foo>http://\S+)'
  131. }
  132. expect(@checker).to be_valid
  133. end
  134. end
  135. describe '#check' do
  136. it 'should check for mails and save memory' do
  137. expect { @checker.check }.to change { Event.count }.by(2)
  138. expect(@checker.notified.sort).to eq(@mails.map(&:message_id).sort)
  139. expect(@checker.lastseen).to eq(@mails.each_with_object(@checker.make_seen) { |mail, seen|
  140. seen[mail.uidvalidity] = mail.uid
  141. })
  142. Event.last(2).map(&:payload) == @payloads
  143. expect { @checker.check }.not_to change { Event.count }
  144. end
  145. it 'should narrow mails by To' do
  146. @checker.options['conditions']['to'] = 'John.Doe@*'
  147. expect { @checker.check }.to change { Event.count }.by(1)
  148. expect(@checker.notified.sort).to eq([@mails.first.message_id])
  149. expect(@checker.lastseen).to eq(@mails.each_with_object(@checker.make_seen) { |mail, seen|
  150. seen[mail.uidvalidity] = mail.uid
  151. })
  152. expect(Event.last.payload).to eq(@payloads.first)
  153. expect { @checker.check }.not_to change { Event.count }
  154. end
  155. it 'should perform regexp matching and save named captures' do
  156. @checker.options['conditions'].update(
  157. 'subject' => '\ARe: (?<a>.+)',
  158. 'body' => 'Some (?<b>.+) reply',
  159. )
  160. expect { @checker.check }.to change { Event.count }.by(1)
  161. expect(@checker.notified.sort).to eq([@mails.last.message_id])
  162. expect(@checker.lastseen).to eq(@mails.each_with_object(@checker.make_seen) { |mail, seen|
  163. seen[mail.uidvalidity] = mail.uid
  164. })
  165. expect(Event.last.payload).to eq(@payloads.last.update(
  166. 'body' => "<div dir=\"ltr\">Some HTML reply<br></div>\n",
  167. 'matches' => { 'a' => 'some subject', 'b' => 'HTML' },
  168. 'mime_type' => 'text/html',
  169. ))
  170. expect { @checker.check }.not_to change { Event.count }
  171. end
  172. it 'should narrow mails by has_attachment (true)' do
  173. @checker.options['conditions']['has_attachment'] = true
  174. expect { @checker.check }.to change { Event.count }.by(1)
  175. expect(Event.last.payload['subject']).to eq('Re: some subject')
  176. end
  177. it 'should narrow mails by has_attachment (false)' do
  178. @checker.options['conditions']['has_attachment'] = false
  179. expect { @checker.check }.to change { Event.count }.by(1)
  180. expect(Event.last.payload['subject']).to eq('some subject')
  181. end
  182. it 'should narrow mail parts by MIME types' do
  183. @checker.options['mime_types'] = %w[text/plain]
  184. @checker.options['conditions'].update(
  185. 'subject' => '\ARe: (?<a>.+)',
  186. 'body' => 'Some (?<b>.+) reply',
  187. )
  188. expect { @checker.check }.not_to change { Event.count }
  189. expect(@checker.notified.sort).to eq([])
  190. expect(@checker.lastseen).to eq(@mails.each_with_object(@checker.make_seen) { |mail, seen|
  191. seen[mail.uidvalidity] = mail.uid
  192. })
  193. end
  194. it 'should never mark mails as read unless mark_as_read is true' do
  195. @mails.each { |mail|
  196. stub(mail).mark_as_read.never
  197. }
  198. expect { @checker.check }.to change { Event.count }.by(2)
  199. end
  200. it 'should mark mails as read if mark_as_read is true' do
  201. @checker.options['mark_as_read'] = true
  202. @mails.each { |mail|
  203. stub(mail).mark_as_read.once
  204. }
  205. expect { @checker.check }.to change { Event.count }.by(2)
  206. end
  207. it 'should create just one event for multiple mails with the same Message-Id' do
  208. @mails.first.message_id = @mails.last.message_id
  209. @checker.options['mark_as_read'] = true
  210. @mails.each { |mail|
  211. stub(mail).mark_as_read.once
  212. }
  213. expect { @checker.check }.to change { Event.count }.by(1)
  214. end
  215. end
  216. end
  217. describe 'Agents::ImapFolderAgent::Message::Scrubbed' do
  218. before do
  219. @class = Class.new do
  220. def subject
  221. "broken\xB7subject\xB6"
  222. end
  223. def body
  224. "broken\xB7body\xB6"
  225. end
  226. include Agents::ImapFolderAgent::Message::Scrubbed
  227. end
  228. @object = @class.new
  229. end
  230. describe '#scrubbed' do
  231. it 'should return a scrubbed string' do
  232. expect(@object.scrubbed(:subject)).to eq("broken<b7>subject<b6>")
  233. expect(@object.scrubbed(:body)).to eq("broken<b7>body<b6>")
  234. end
  235. end
  236. end
  237. end