You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

284 lines
12 KiB

  1. import odoo
  2. from odoo.tests import TransactionCase
  3. from email.message import EmailMessage
  4. from ..models.mail import encode_msg_id
  5. from ..models.mail import encode_msg_id_legacy
  6. from ..models.mail import MESSAGE_PREFIX
  7. import mock
  8. from odoo import SUPERUSER_ID
  9. from odoo.addons.base.models.ir_mail_server import IrMailServer
  10. from ..models.mail import random_string
  11. @odoo.tests.tagged('post_install', '-at_install')
  12. class TestEmail(TransactionCase):
  13. def setUp(self):
  14. super(TestEmail, self).setUp()
  15. self.partner = self.env['res.partner'].create({'name': 'Test Dude'})
  16. self.partner2 = self.env['res.partner'].create({'name': 'Dudette'})
  17. self.demo_user = self.env.ref('base.user_demo')
  18. self.subtype_comment = self.env.ref('mail.mt_comment')
  19. self.subtype_note = self.env.ref('mail.mt_note')
  20. self.MailMessage = self.env['mail.message']
  21. self.ConfigParam = self.env['ir.config_parameter']
  22. # Create server configuration
  23. self.outgoing_server = self.env['ir.mail_server'].create({
  24. 'name': 'Outgoing SMTP Server for Unit Tests',
  25. 'sequence': 1,
  26. 'smtp_host': 'localhost',
  27. 'smtp_port': '9999',
  28. 'smtp_encryption': 'none',
  29. 'smtp_user': 'doesnt',
  30. 'smtp_pass': 'exist',
  31. 'reply_to_method': 'msg_id',
  32. 'force_email_reply_to_domain': 'example.com',
  33. 'force_email_from': 'odoo@example.com',
  34. })
  35. @staticmethod
  36. def create_email_message():
  37. message = EmailMessage()
  38. message['Content-Type'] = 'multipart/mixed; boundary="===============2590914155756834027=="'
  39. message['MIME-Version'] = '1.0'
  40. message['Message-Id'] = '<CAD-eYi=a264_3DcrYSDU5yc_fwYoHonZ3H+{}@mail.gmail.com>'.format(random_string(6))
  41. message['Subject'] = '1'
  42. message['From'] = 'Miku Laitinen <miku@avoin.systems>'
  43. message['Reply-To'] = 'YourCompany Eurooppa <sales@avoin.onmicrosoft.com>'
  44. message['To'] = '"Erik N. French" <ErikNFrench@armyspy.com>'
  45. message['Date'] = 'Mon, 06 May 2019 14:16:38 -0000'
  46. return message
  47. def test_reply_to_method_msg_id(self):
  48. # Make administrator follow the partner
  49. self.partner.message_subscribe([self.env.user.partner_id.id])
  50. # Send a message to the followers of the partner
  51. thread_msg = self.partner.with_user(self.demo_user).message_post(
  52. body='dummy message.',
  53. message_type='comment',
  54. subtype='mail.mt_comment'
  55. )
  56. # Make sure the message headers look right.. or not
  57. # mail_msg = thread_msg.notification_ids[0]
  58. # Get the encoded message address
  59. encoded_msg_id = encode_msg_id(thread_msg.id, self.env)
  60. # Try to read an incoming email
  61. message = self.create_email_message()
  62. del message['To']
  63. message['To'] = '"Erik N. French" <ErikNFrench+{}{}@armyspy.com>'.format(MESSAGE_PREFIX, encoded_msg_id)
  64. thread_id = self.env['mail.thread'].message_process(model=False, message=message.as_string())
  65. self.assertEqual(thread_msg.res_id, thread_id,
  66. "The incoming email wasn't connected to the correct thread")
  67. # Make sure the message is a comment
  68. incoming_msg1 = self.MailMessage.search([('message_id', '=', message['Message-Id'])])
  69. self.assertEqual(
  70. incoming_msg1.message_type,
  71. 'email',
  72. "The incoming message was created as a type {} instead of a email.".format(incoming_msg1.message_type)
  73. )
  74. self.assertEqual(
  75. incoming_msg1.subtype_id,
  76. self.subtype_comment,
  77. "The incoming message was created as a subtype {} instead of a comment.".format(incoming_msg1.subtype_id)
  78. )
  79. # Try to read another incoming email
  80. message = self.create_email_message()
  81. del message['To']
  82. message['To'] = '"Erik N. French" <ErikNFrench+{}HURDURLUR@armyspy.com>'.format(MESSAGE_PREFIX)
  83. message['In-Reply-To'] = thread_msg.message_id
  84. thread_id = self.env['mail.thread'].message_process(model=False, message=message.as_string())
  85. self.assertEqual(thread_msg.res_id, thread_id,
  86. "The incoming email wasn't connected to the correct thread")
  87. # Make sure the message is a comment
  88. incoming_msg2 = self.MailMessage.search([('message_id', '=', message['Message-Id'])])
  89. self.assertEqual(
  90. incoming_msg2.message_type,
  91. 'email',
  92. "The incoming message was created as a type {} instead of a email.".format(incoming_msg2.message_type)
  93. )
  94. self.assertEqual(
  95. incoming_msg2.subtype_id,
  96. self.subtype_comment,
  97. "The incoming message was created as a subtype {} instead of a comment.".format(incoming_msg2.subtype_id)
  98. )
  99. def test_reply_to_method_msg_id_priority(self):
  100. """
  101. In this test we will inject the wrong Message-Id to the incoming
  102. email messages References-header and see if Odoo will prioritize
  103. the custom Reply-To address over the References-header. It should.
  104. :return:
  105. """
  106. # Make administrator follow the partner
  107. self.partner.message_subscribe([self.env.user.partner_id.id])
  108. # Send a message to the followers of the partner
  109. thread_msg = self.partner.with_user(self.demo_user).message_post(
  110. body='dummy message X.',
  111. message_type='comment',
  112. subtype='mail.mt_comment'
  113. )
  114. # Get the encoded message address
  115. encoded_msg_id = encode_msg_id(thread_msg.id, self.env)
  116. # Send another message to the followers of the partner
  117. thread_msg2 = self.partner2.with_user(self.demo_user).message_post(
  118. body='dummy message X.',
  119. message_type='comment',
  120. subtype='mail.mt_comment'
  121. )
  122. # Try to read an incoming email
  123. message = self.create_email_message()
  124. del message['To']
  125. del message['References']
  126. message['To'] = '"Erik N. French" <ErikNFrench+{}{}@armyspy.com>'.format(MESSAGE_PREFIX, encoded_msg_id)
  127. # Inject the wrong References
  128. message['References'] = thread_msg2.message_id
  129. thread_id = self.env['mail.thread'].message_process(model=False, message=message.as_string())
  130. self.assertEqual(thread_msg.res_id, thread_id,
  131. "The incoming email wasn't connected to the correct thread")
  132. def test_reply_to_method_msg_id_notification(self):
  133. # Make administrator follow the partner
  134. self.partner2.message_subscribe([self.env.user.partner_id.id])
  135. # Send a message to the followers of the partner
  136. thread_msg = self.partner2.with_user(self.demo_user).message_post(
  137. body='dummy message 2.',
  138. message_type='comment',
  139. subtype='mail.mt_note'
  140. )
  141. # Get the encoded message address
  142. encoded_msg_id = encode_msg_id(thread_msg.id, self.env)
  143. # Try to read an incoming email
  144. message = self.create_email_message()
  145. del message['To']
  146. message['To'] = '"Erik N. French" <ErikNFrench+{}{}@armyspy.com>'.format(MESSAGE_PREFIX, encoded_msg_id)
  147. thread_id = self.env['mail.thread'].message_process(model=False, message=message.as_string())
  148. self.assertEqual(thread_msg.res_id, thread_id,
  149. "The incoming email wasn't connected to the correct thread")
  150. # Make sure the message is a note
  151. incoming_msg1 = self.MailMessage.search([('message_id', '=', message['Message-Id'])])
  152. self.assertEqual(
  153. incoming_msg1.message_type,
  154. 'email',
  155. "The incoming message was created as a type {} instead of a email.".format(incoming_msg1.message_type)
  156. )
  157. self.assertEqual(
  158. incoming_msg1.subtype_id,
  159. self.subtype_note,
  160. "The incoming message was created as a subtype {} instead of a note.".format(incoming_msg1.subtype_id)
  161. )
  162. def test_reply_to_method_msg_id_legacy(self):
  163. # REMOVE this test when porting to Odoo 14
  164. # Make administrator follow the partner
  165. self.partner2.message_subscribe([self.env.user.partner_id.id])
  166. # Send a message to the followers of the partner
  167. thread_msg = self.partner2.with_user(self.demo_user).message_post(
  168. body='dummy message 2.',
  169. message_type='comment',
  170. subtype='mail.mt_note'
  171. )
  172. # Get the encoded message address
  173. encoded_msg_id = encode_msg_id_legacy(thread_msg.id, self.env)
  174. # Try to read an incoming email
  175. message = self.create_email_message()
  176. del message['To']
  177. message['To'] = '"Erik N. French" <ErikNFrench+{}{}@armyspy.com>'.format(MESSAGE_PREFIX, encoded_msg_id)
  178. thread_id = self.env['mail.thread'].message_process(model=False, message=message.as_string())
  179. self.assertEqual(thread_msg.res_id, thread_id,
  180. "The incoming email wasn't connected to the correct thread")
  181. def test_reply_to_method_msg_id_lowercase(self):
  182. # Make administrator follow the partner
  183. self.partner2.message_subscribe([self.env.user.partner_id.id])
  184. # Send a message to the followers of the partner
  185. thread_msg = self.partner2.with_user(self.demo_user).message_post(
  186. body='dummy message 2.',
  187. message_type='comment',
  188. subtype='mail.mt_note'
  189. )
  190. # Get the encoded message address
  191. encoded_msg_id = encode_msg_id(thread_msg.id, self.env).lower()
  192. # Try to read an incoming email
  193. message = self.create_email_message()
  194. del message['To']
  195. message['To'] = '"Erik N. French" <ErikNFrench+{}{}@armyspy.com>'.format(MESSAGE_PREFIX, encoded_msg_id)
  196. thread_id = self.env['mail.thread'].message_process(model=False, message=message.as_string())
  197. self.assertEqual(thread_msg.res_id, thread_id,
  198. "The incoming email wasn't connected to the correct thread")
  199. def test_outgoing_msg_id(self):
  200. # Make administrator follow the partner
  201. self.partner2.message_subscribe([SUPERUSER_ID])
  202. with mock.patch.object(IrMailServer, 'send_email') as send_email:
  203. # Send a message to the followers of the partner
  204. thread_msg = self.partner2.with_user(self.demo_user).message_post(
  205. body='dummy message 3.',
  206. message_type='comment',
  207. subtype='mail.mt_comment'
  208. )
  209. # Get the encoded message address
  210. encoded_msg_id = encode_msg_id(thread_msg.id, self.env)
  211. self.assertTrue(
  212. send_email.called,
  213. "IrMailServer.send_email wasn't called when sending outgoing email"
  214. )
  215. message = send_email.call_args[0][0]
  216. reply_to_address = '{}{}@{}'.format(
  217. MESSAGE_PREFIX,
  218. encoded_msg_id,
  219. self.outgoing_server.force_email_reply_to_domain
  220. )
  221. # Make sure the subaddress is correct in the Reply-To field
  222. self.assertIn(reply_to_address,
  223. message['Reply-To'],
  224. "Reply-To address didn't contain the correct subaddress")
  225. # Make sure the author name is in the Reply-To field
  226. self.assertIn(thread_msg.author_id.name,
  227. message['Reply-To'],
  228. "Reply-To address didn't contain the author name")
  229. self.assertIn(self.outgoing_server.force_email_from,
  230. message['From'],
  231. "From address didn't contain the configure From-address")