signature_detection_spec.rb 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194
  1. # Copyright (C) 2012-2024 Zammad Foundation, https://zammad-foundation.org/
  2. require 'rails_helper'
  3. RSpec.describe SignatureDetection do
  4. describe '.find_signature' do
  5. context 'when given an array of hashes' do
  6. let(:messages) do
  7. raw_message_files.map do |f|
  8. { content: File.read(f), content_type: content_type }
  9. end
  10. end
  11. context 'with plain text messages in their :content keys (sample input 1)' do
  12. let(:content_type) { 'text/plain' }
  13. let(:raw_message_files) do
  14. [
  15. Rails.root.join('test/data/email_signature_detection/client_a_1.txt'),
  16. Rails.root.join('test/data/email_signature_detection/client_a_2.txt'),
  17. Rails.root.join('test/data/email_signature_detection/client_a_3.txt')
  18. ]
  19. end
  20. it 'returns the first 5–10-line substring they share in common' do
  21. expect(described_class.find_signature(messages)).to eq(<<~SIG.chomp)
  22. Mit freundlichen Grüßen
  23. Bob Smith
  24. Berechtigungen und dez. Department
  25. ________________________________
  26. Musik AG
  27. Berechtigungen und dez. Department (ITPBM)
  28. Kastanien 2
  29. SIG
  30. end
  31. end
  32. context 'with plain text messages in their :content keys (sample input 2)' do
  33. let(:content_type) { 'text/plain' }
  34. let(:raw_message_files) do
  35. [
  36. Rails.root.join('test/data/email_signature_detection/client_b_1.txt'),
  37. Rails.root.join('test/data/email_signature_detection/client_b_2.txt'),
  38. Rails.root.join('test/data/email_signature_detection/client_b_3.txt')
  39. ]
  40. end
  41. it 'returns the first 5–10-line substring they share in common' do
  42. expect(described_class.find_signature(messages)).to eq(<<~SIG.chomp)
  43. Freundliche Grüße
  44. Günter Lässig
  45. Lokale Daten
  46. Music GmbH
  47. Baustraße 123, 12345 Max City
  48. Telefon 0123 5432114
  49. Telefax 0123 5432139
  50. SIG
  51. end
  52. end
  53. context 'with HTML messages in their :content keys' do
  54. let(:content_type) { 'text/html' }
  55. let(:raw_message_files) do
  56. [
  57. Rails.root.join('test/data/email_signature_detection/client_c_1.html'),
  58. Rails.root.join('test/data/email_signature_detection/client_c_2.html'),
  59. Rails.root.join('test/data/email_signature_detection/client_c_3.html')
  60. ]
  61. end
  62. it 'converts messages (via #html2text) then returns the first 5–10-line substring they share in common' do
  63. expect(described_class.find_signature(messages)).to eq(<<~SIG.chomp)
  64. ChristianSmith
  65. Technik
  66. Tel: +49 12 34 56 78 441
  67. Fax: +49 12 34 56 78 499
  68. Email: Christian.Smith@example.com
  69. Web: www.example.com
  70. ABC KFZ- und Flugzeug B.V. & Co. KG
  71. Hauptverwaltung
  72. SIG
  73. end
  74. end
  75. end
  76. context 'when input messages do not share 5-line common substrings' do
  77. let(:messages) do
  78. Array.new(2) { { content: <<~RAW, content_type: 'text/plain' } }
  79. Lorem ipsum dolor sit amet, consectetur adipiscing elit.
  80. Ut ut tincidunt nunc. Sed mattis aliquam tellus sit amet lacinia.
  81. Mauris fermentum dictum aliquet.
  82. Nam ex risus, gravida et ornare ut, mollis non sapien.
  83. RAW
  84. end
  85. it 'doesn’t break' do
  86. expect { described_class.find_signature(messages) }.not_to raise_error
  87. end
  88. end
  89. context 'when message contains exchange warning (#3571)' do
  90. let(:content_type) { 'text/html' }
  91. let(:messages) do
  92. [
  93. { content: '<div><span style="color:#9c6500;">CAUTION:</span> This email originated from outside of the organization. Do not click links or open attachments unless you recognize the sender and know the content is safe.</div><br><div><p>actual content</p><div><p>actual content 2</p></div><p>&nbsp;</p><div><p>actual quote</p></div><div><blockquote><p>actual quote</p></blockquote></div><div><p>&nbsp;</p></div><p>&nbsp;</p></div></div>', content_type: 'text/html' },
  94. { content: '<div><span style="color:#9c6500;">CAUTION:</span> This email originated from outside of the organization. Do not click links or open attachments unless you recognize the sender and know the content is safe.</div><br><div><p>333 content</p><div><p>4452134123 content 2</p></div><p>&nbsp;</p><div><p>123124123 quote</p></div><div><blockquote><p>123141 144234</p></blockquote></div><div><p>&nbsp;</p></div><p>&nbsp;</p></div></div>', content_type: 'text/html' },
  95. { content: '<div><span style="color:#9c6500;">CAUTION:</span> This email originated from outside of the organization. Do not click links or open attachments unless you recognize the sender and know the content is safe.</div><br><div><p>333 content</p><div><p>4452134123 content 2</p></div><p>&nbsp;</p><div><p>9999 quote</p></div><div><blockquote><p>999 144234</p></blockquote></div><div><p>&nbsp;</p></div><p>&nbsp;</p></div></div>', content_type: 'text/html' },
  96. ]
  97. end
  98. it 'does not detect the warning information as signature' do
  99. expect(described_class.find_signature(messages)).to be_nil
  100. end
  101. end
  102. end
  103. describe '.find_signature_line' do
  104. context 'when given a plain text message' do
  105. let(:content_type) { 'text/plain' }
  106. let(:content) { Rails.root.join('test/data/email_signature_detection/client_a_1.txt').read }
  107. context 'and a substring it contains' do
  108. let(:signature) { <<~SIG.chomp }
  109. Mit freundlichen Grüßen
  110. Bob Smith
  111. Berechtigungen und dez. Department
  112. ________________________________
  113. Musik AG
  114. Berechtigungen und dez. Department (ITPBM)
  115. Kastanien 2
  116. SIG
  117. it 'returns the line of the message where the signature begins' do
  118. expect(described_class.find_signature_line(signature, content, content_type)).to eq(10)
  119. end
  120. end
  121. end
  122. context 'when given an HTML message' do
  123. let(:content_type) { 'text/html' }
  124. let(:content) { Rails.root.join('test/data/email_signature_detection/example1.html').read }
  125. context 'and a substring it contains' do
  126. let(:signature) { <<~SIG.chomp }
  127. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  128. Bob Smith
  129. ABC Organisation
  130. EXAMPLE IT-Service GmbH
  131. Dorten 5 F&E
  132. 12345 Da / Germany
  133. Phone: +49 (0) 1234 567 890 / +49 (0) 1234 567 891
  134. Fax:     +49 (0) 1234 567 892
  135. SIG
  136. it 'converts messages (via #html2text) then returns the line of the message where the signature begins' do
  137. expect(described_class.find_signature_line(signature, content, content_type)).to eq(11)
  138. end
  139. end
  140. end
  141. end
  142. describe '.rebuild_all_articles' do
  143. context 'when a user exists with a recorded signature' do
  144. let!(:customer) { create(:customer, preferences: { signature_detection: "\nbar" }) }
  145. context 'and multiple articles exist for that customer' do
  146. let!(:articles) do
  147. [create(:ticket_article, created_by_id: customer.id, body: "foo\nfoo\nbar"),
  148. create(:ticket_article, created_by_id: customer.id, body: "foo\nbar")]
  149. end
  150. it 'updates the signature-line data of all articles' do
  151. expect { described_class.rebuild_all_articles }
  152. .to change { articles.first.reload.preferences[:signature_detection] }.to(3)
  153. .and change { articles.second.reload.preferences[:signature_detection] }.to(2)
  154. end
  155. end
  156. end
  157. end
  158. end