test_pagure_lib_notify_email.py 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524
  1. # -*- coding: utf-8 -*-
  2. """
  3. (c) 2016 - Copyright Red Hat Inc
  4. Authors:
  5. Adam Williamson <awilliam@redhat.com>
  6. """
  7. from __future__ import unicode_literals, absolute_import
  8. import unittest
  9. import sys
  10. import os
  11. import mock
  12. import munch
  13. import six
  14. sys.path.insert(
  15. 0, os.path.join(os.path.dirname(os.path.abspath(__file__)), "..")
  16. )
  17. import pagure.lib.query # pylint: disable=wrong-import-position
  18. import pagure.lib.model # pylint: disable=wrong-import-position
  19. import pagure.lib.notify # pylint: disable=wrong-import-position
  20. import tests # pylint: disable=wrong-import-position
  21. class PagureLibNotifyEmailtests(tests.Modeltests):
  22. """Some tests for the various email construction functions. In
  23. their own class so they can have some shared fixtures.
  24. """
  25. def setUp(self):
  26. """Override setUp to add more fixtures used for many tests."""
  27. super(PagureLibNotifyEmailtests, self).setUp()
  28. tests.create_projects(self.session)
  29. # we don't want to send any mails while setting up
  30. patcher = mock.patch("pagure.lib.notify.send_email")
  31. patcher.start()
  32. self.user1 = pagure.lib.query.get_user(self.session, "pingou")
  33. self.user2 = pagure.lib.query.get_user(self.session, "foo")
  34. self.project1 = pagure.lib.query._get_project(self.session, "test")
  35. self.project2 = pagure.lib.query._get_project(self.session, "test2")
  36. self.project3 = pagure.lib.query._get_project(
  37. self.session, "test3", namespace="somenamespace"
  38. )
  39. # Create a forked repo, should be project #4
  40. # Not using fork_project as it tries to do a git clone
  41. item = pagure.lib.model.Project(
  42. user_id=2, # foo
  43. name="test",
  44. description="test project #1",
  45. is_fork=True,
  46. parent_id=1,
  47. hook_token="aaabbbyyy",
  48. )
  49. self.session.add(item)
  50. self.session.commit()
  51. self.forkedproject = pagure.lib.query._get_project(
  52. self.session, "test", user="foo"
  53. )
  54. # Report an issue on project #1
  55. self.issue1 = pagure.lib.query.new_issue(
  56. session=self.session,
  57. repo=self.project1,
  58. title="issue",
  59. content="a bug report",
  60. user="pingou",
  61. )
  62. # Add a comment on the issue
  63. pagure.lib.query.add_issue_comment(
  64. self.session, self.issue1, comment="Test comment", user="pingou"
  65. )
  66. self.comment1 = pagure.lib.query.get_issue_comment(
  67. self.session, self.issue1.uid, 1
  68. )
  69. # Report an issue on project #3 (namespaced)
  70. self.issue2 = pagure.lib.query.new_issue(
  71. session=self.session,
  72. repo=self.project3,
  73. title="namespaced project issue",
  74. content="a bug report on a namespaced project",
  75. user="pingou",
  76. )
  77. # report an issue on foo's fork of project #1
  78. self.issue3 = pagure.lib.query.new_issue(
  79. session=self.session,
  80. repo=self.forkedproject,
  81. title="forked project issue",
  82. content="a bug report on a forked project",
  83. user="pingou",
  84. )
  85. patcher.stop()
  86. @mock.patch("pagure.lib.notify.send_email")
  87. def test_notify_new_comment(self, fakemail):
  88. """Simple test for notification about new comment."""
  89. exptext = """
  90. pingou added a new comment to an issue you are following:
  91. ``
  92. Test comment
  93. ``
  94. To reply, visit the link below
  95. http://localhost.localdomain/test/issue/1
  96. """
  97. pagure.lib.notify.notify_new_comment(self.comment1)
  98. (_, args, kwargs) = fakemail.mock_calls[0]
  99. # Mail text should be as expected.
  100. self.assertEqual(args[0], exptext)
  101. self.assertTrue(isinstance(args[0], six.text_type))
  102. # Mail subject should be as expected.
  103. self.assertEqual(args[1], "Issue #1: issue")
  104. # Mail should be sent to user #1.
  105. self.assertEqual(args[2], self.user1.default_email)
  106. # Mail ID should be comment #1's mail ID...
  107. self.assertEqual(kwargs["mail_id"], self.comment1.mail_id)
  108. # In reply to issue #1's mail ID.
  109. self.assertEqual(kwargs["in_reply_to"], self.issue1.mail_id)
  110. # Project name should be...project (full) name.
  111. self.assertEqual(kwargs["project_name"], self.project1.fullname)
  112. # Mail should be from user1 (who wrote the comment).
  113. self.assertEqual(kwargs["user_from"], self.user1.fullname)
  114. @mock.patch("pagure.lib.notify.send_email")
  115. def test_user_notified_new_comment(self, fakemail):
  116. """Check that users that are @mentionned are notified."""
  117. self.comment1.comment = "Hey @foo. Let's do it!"
  118. g = munch.Munch()
  119. g.session = self.session
  120. with mock.patch("flask.g", g):
  121. pagure.lib.notify.notify_new_comment(self.comment1)
  122. (_, args, kwargs) = fakemail.mock_calls[0]
  123. # Mail should be sent to both users
  124. self.assertIn(
  125. args[2],
  126. ["bar@pingou.com,foo@bar.com", "foo@bar.com,bar@pingou.com"],
  127. )
  128. # Mail ID should be comment #1's mail ID...
  129. self.assertEqual(kwargs["mail_id"], self.comment1.mail_id)
  130. # In reply to issue #1's mail ID.
  131. self.assertEqual(kwargs["in_reply_to"], self.issue1.mail_id)
  132. # Project name should be...project (full) name.
  133. self.assertEqual(kwargs["project_name"], self.project1.fullname)
  134. # Mail should be from user1 (who wrote the comment).
  135. self.assertEqual(kwargs["user_from"], self.user1.fullname)
  136. @mock.patch("pagure.lib.notify.send_email")
  137. def test_user_notified_new_comment_start_row(self, fakemail):
  138. """Check that users that are @mentionned are notified."""
  139. self.comment1.comment = "@foo, okidoki"
  140. g = munch.Munch()
  141. g.session = self.session
  142. with mock.patch("flask.g", g):
  143. pagure.lib.notify.notify_new_comment(self.comment1)
  144. (_, args, kwargs) = fakemail.mock_calls[0]
  145. # Mail should be sent to both users
  146. self.assertIn(
  147. args[2],
  148. ["bar@pingou.com,foo@bar.com", "foo@bar.com,bar@pingou.com"],
  149. )
  150. # Mail ID should be comment #1's mail ID...
  151. self.assertEqual(kwargs["mail_id"], self.comment1.mail_id)
  152. # In reply to issue #1's mail ID.
  153. self.assertEqual(kwargs["in_reply_to"], self.issue1.mail_id)
  154. # Project name should be...project (full) name.
  155. self.assertEqual(kwargs["project_name"], self.project1.fullname)
  156. # Mail should be from user1 (who wrote the comment).
  157. self.assertEqual(kwargs["user_from"], self.user1.fullname)
  158. @mock.patch("pagure.lib.notify.send_email")
  159. def test_user_notified_new_comment_with_email(self, fakemail):
  160. """Ensures that @mention doesn't over-reach."""
  161. self.comment1.comment = "So apparently bar@foo.com exists"
  162. g = munch.Munch()
  163. g.fas_user = tests.FakeUser(username="pingou")
  164. g.authenticated = True
  165. g.session = self.session
  166. with mock.patch("flask.g", g):
  167. pagure.lib.notify.notify_new_comment(self.comment1)
  168. (_, args, kwargs) = fakemail.mock_calls[0]
  169. # Mail should be sent to both users
  170. self.assertEqual(args[2], "bar@pingou.com")
  171. # Mail ID should be comment #1's mail ID...
  172. self.assertEqual(kwargs["mail_id"], self.comment1.mail_id)
  173. # In reply to issue #1's mail ID.
  174. self.assertEqual(kwargs["in_reply_to"], self.issue1.mail_id)
  175. # Project name should be...project (full) name.
  176. self.assertEqual(kwargs["project_name"], self.project1.fullname)
  177. # Mail should be from user1 (who wrote the comment).
  178. self.assertEqual(kwargs["user_from"], self.user1.fullname)
  179. @mock.patch("pagure.lib.notify.send_email")
  180. def test_user_notified_new_comment_with_email_with_number(self, fakemail):
  181. """Ensures that @mention doesn't over-reach."""
  182. self.comment1.comment = "So apparently bar123@foo.com exists"
  183. g = munch.Munch()
  184. g.fas_user = tests.FakeUser(username="pingou")
  185. g.authenticated = True
  186. g.session = self.session
  187. with mock.patch("flask.g", g):
  188. pagure.lib.notify.notify_new_comment(self.comment1)
  189. (_, args, kwargs) = fakemail.mock_calls[0]
  190. # Mail should be sent to both users
  191. self.assertEqual(args[2], "bar@pingou.com")
  192. # Mail ID should be comment #1's mail ID...
  193. self.assertEqual(kwargs["mail_id"], self.comment1.mail_id)
  194. # In reply to issue #1's mail ID.
  195. self.assertEqual(kwargs["in_reply_to"], self.issue1.mail_id)
  196. # Project name should be...project (full) name.
  197. self.assertEqual(kwargs["project_name"], self.project1.fullname)
  198. # Mail should be from user1 (who wrote the comment).
  199. self.assertEqual(kwargs["user_from"], self.user1.fullname)
  200. @mock.patch("pagure.lib.notify.send_email")
  201. def test_user_notified_new_comment_not_in_code_block_incline(
  202. self, fakemail
  203. ):
  204. """Ensures that @mention doesn't over-reach in code-blocks."""
  205. self.comment1.comment = (
  206. "So apparently they said ``@foo.com is awesome`` :)"
  207. )
  208. g = munch.Munch()
  209. g.fas_user = tests.FakeUser(username="pingou")
  210. g.authenticated = True
  211. g.session = self.session
  212. with mock.patch("flask.g", g):
  213. pagure.lib.notify.notify_new_comment(self.comment1)
  214. (_, args, kwargs) = fakemail.mock_calls[0]
  215. # Mail should be sent to both users
  216. self.assertEqual(args[2], "bar@pingou.com")
  217. # Mail ID should be comment #1's mail ID...
  218. self.assertEqual(kwargs["mail_id"], self.comment1.mail_id)
  219. # In reply to issue #1's mail ID.
  220. self.assertEqual(kwargs["in_reply_to"], self.issue1.mail_id)
  221. # Project name should be...project (full) name.
  222. self.assertEqual(kwargs["project_name"], self.project1.fullname)
  223. # Mail should be from user1 (who wrote the comment).
  224. self.assertEqual(kwargs["user_from"], self.user1.fullname)
  225. @mock.patch("pagure.lib.notify.send_email")
  226. def test_user_notified_new_comment_not_in_code_block_4_backtick(
  227. self, fakemail
  228. ):
  229. """Ensures that @mention doesn't over-reach in code-blocks."""
  230. self.comment1.comment = """
  231. So apparently they said
  232. ````
  233. @foo.com is awesome and @foo is great
  234. We all love @foo !
  235. ````
  236. :)
  237. """
  238. g = munch.Munch()
  239. g.fas_user = tests.FakeUser(username="pingou")
  240. g.authenticated = True
  241. g.session = self.session
  242. with mock.patch("flask.g", g):
  243. pagure.lib.notify.notify_new_comment(self.comment1)
  244. (_, args, kwargs) = fakemail.mock_calls[0]
  245. # Mail should be sent to both users
  246. self.assertEqual(args[2], "bar@pingou.com")
  247. # Mail ID should be comment #1's mail ID...
  248. self.assertEqual(kwargs["mail_id"], self.comment1.mail_id)
  249. # In reply to issue #1's mail ID.
  250. self.assertEqual(kwargs["in_reply_to"], self.issue1.mail_id)
  251. # Project name should be...project (full) name.
  252. self.assertEqual(kwargs["project_name"], self.project1.fullname)
  253. # Mail should be from user1 (who wrote the comment).
  254. self.assertEqual(kwargs["user_from"], self.user1.fullname)
  255. @mock.patch("pagure.lib.notify.send_email")
  256. def test_user_notified_new_comment_not_in_code_block_3_backticks(
  257. self, fakemail
  258. ):
  259. """Ensures that @mention doesn't over-reach in code-blocks."""
  260. self.comment1.comment = """
  261. So apparently they said
  262. ```
  263. @foo.com is awesome and @foo is great
  264. We all love @foo !
  265. ```
  266. :)
  267. """
  268. g = munch.Munch()
  269. g.fas_user = tests.FakeUser(username="pingou")
  270. g.authenticated = True
  271. g.session = self.session
  272. with mock.patch("flask.g", g):
  273. pagure.lib.notify.notify_new_comment(self.comment1)
  274. (_, args, kwargs) = fakemail.mock_calls[0]
  275. # Mail should be sent to both users
  276. self.assertEqual(args[2], "bar@pingou.com")
  277. # Mail ID should be comment #1's mail ID...
  278. self.assertEqual(kwargs["mail_id"], self.comment1.mail_id)
  279. # In reply to issue #1's mail ID.
  280. self.assertEqual(kwargs["in_reply_to"], self.issue1.mail_id)
  281. # Project name should be...project (full) name.
  282. self.assertEqual(kwargs["project_name"], self.project1.fullname)
  283. # Mail should be from user1 (who wrote the comment).
  284. self.assertEqual(kwargs["user_from"], self.user1.fullname)
  285. @mock.patch("pagure.lib.notify.send_email")
  286. def test_user_notified_new_comment_not_in_code_block_tilde(self, fakemail):
  287. """Ensures that @mention doesn't over-reach in code-blocks."""
  288. self.comment1.comment = """
  289. So apparently they said
  290. ~~~~
  291. @foo.com is awesome and @foo is great
  292. We all love @foo !
  293. ~~~~
  294. :)
  295. """
  296. g = munch.Munch()
  297. g.fas_user = tests.FakeUser(username="pingou")
  298. g.authenticated = True
  299. g.session = self.session
  300. with mock.patch("flask.g", g):
  301. pagure.lib.notify.notify_new_comment(self.comment1)
  302. (_, args, kwargs) = fakemail.mock_calls[0]
  303. # Mail should be sent to both users
  304. self.assertEqual(args[2], "bar@pingou.com")
  305. # Mail ID should be comment #1's mail ID...
  306. self.assertEqual(kwargs["mail_id"], self.comment1.mail_id)
  307. # In reply to issue #1's mail ID.
  308. self.assertEqual(kwargs["in_reply_to"], self.issue1.mail_id)
  309. # Project name should be...project (full) name.
  310. self.assertEqual(kwargs["project_name"], self.project1.fullname)
  311. # Mail should be from user1 (who wrote the comment).
  312. self.assertEqual(kwargs["user_from"], self.user1.fullname)
  313. @mock.patch("pagure.lib.notify.send_email")
  314. def test_notify_new_issue_namespaced(
  315. self, fakemail
  316. ): # pylint: disable=invalid-name
  317. """Test for notifying of a new issue, namespaced project."""
  318. exptext = """
  319. pingou reported a new issue against the project: `test3` that you are following:
  320. ``
  321. a bug report on a namespaced project
  322. ``
  323. To reply, visit the link below
  324. http://localhost.localdomain/somenamespace/test3/issue/1
  325. """
  326. pagure.lib.notify.notify_new_issue(self.issue2)
  327. (_, args, kwargs) = fakemail.mock_calls[0]
  328. # Mail text should be as expected.
  329. self.assertEqual(args[0], exptext)
  330. self.assertTrue(isinstance(args[0], six.text_type))
  331. # Mail subject should be as expected.
  332. self.assertEqual(args[1], "Issue #1: namespaced project issue")
  333. # Mail should be sent to user #1.
  334. self.assertEqual(args[2], self.user1.default_email)
  335. # Mail ID should be issue's mail ID.
  336. self.assertEqual(kwargs["mail_id"], self.issue2.mail_id)
  337. # Project name should be...project (full) name.
  338. self.assertEqual(kwargs["project_name"], self.project3.fullname)
  339. # Mail should be from user1 (who submitted the issue).
  340. self.assertEqual(kwargs["user_from"], self.user1.fullname)
  341. @mock.patch("pagure.lib.notify.send_email")
  342. def test_notify_assigned_issue_forked(
  343. self, fakemail
  344. ): # pylint: disable=invalid-name
  345. """Test for notifying re-assignment of issue on forked project.
  346. 'foo' reassigns issue on his fork of 'test' to 'pingou'.
  347. """
  348. exptext = """
  349. The issue: `forked project issue` of project: `test` has been assigned to `pingou` by foo.
  350. http://localhost.localdomain/fork/foo/test/issue/1
  351. """
  352. pagure.lib.notify.notify_assigned_issue(
  353. self.issue3, self.user1, self.user2
  354. )
  355. (_, args, kwargs) = fakemail.mock_calls[0]
  356. # Mail text should be as expected.
  357. self.assertEqual(args[0], exptext)
  358. self.assertTrue(isinstance(args[0], six.text_type))
  359. # Mail subject should be as expected.
  360. self.assertEqual(args[1], "Issue #1: forked project issue")
  361. # Mail should be sent to user #1.
  362. # NOTE: Not sent to user #2...
  363. self.assertEqual(args[2], self.user1.default_email)
  364. # Mail ID should contain issue's mail ID and '/assigned/'
  365. self.assertIn(
  366. "{0}/assigned/".format(self.issue3.mail_id), kwargs["mail_id"]
  367. )
  368. # Project name should be...project (full) name.
  369. self.assertEqual(kwargs["project_name"], self.forkedproject.fullname)
  370. # Mail should be from user1 (who submitted the issue).
  371. self.assertEqual(kwargs["user_from"], self.user2.fullname)
  372. @mock.patch("pagure.lib.notify.send_email")
  373. # for non-ASCII testing, we mock these return values
  374. @mock.patch("pagure.lib.git.get_author", return_value="Cecil Cõmmîttër")
  375. @mock.patch(
  376. "pagure.lib.git.get_commit_subject", return_value="We love Motörhead"
  377. )
  378. def test_notify_new_commits(
  379. self, _, __, fakemail
  380. ): # pylint: disable=invalid-name
  381. """Test for notification on new commits, especially when
  382. non-ASCII text is involved.
  383. """
  384. exptext = """
  385. The following commits were pushed to the repo test on branch
  386. master, which you are following:
  387. abcdefg Cecil Cõmmîttër We love Motörhead
  388. To view more about the commits, visit:
  389. http://localhost.localdomain/test/commits/master
  390. """
  391. # first arg (abspath) doesn't matter and we can use a commit
  392. # ID that doesn't actually exist, as we are mocking
  393. # the get_author and get_commit_subject calls anyway
  394. pagure.lib.notify.notify_new_commits(
  395. "/", self.project1, "master", ["abcdefg"]
  396. )
  397. (_, args, kwargs) = fakemail.mock_calls[0]
  398. # Mail text should be as expected.
  399. self.assertEqual(args[0], exptext)
  400. self.assertTrue(isinstance(args[0], six.text_type))
  401. # Mail subject should be as expected.
  402. self.assertEqual(args[1], 'New Commits To "test" (master)')
  403. # Mail doesn't actually get sent to anyone by default
  404. self.assertEqual(args[2], "")
  405. # Project name should be...project (full) name.
  406. self.assertEqual(kwargs["project_name"], self.project1.fullname)
  407. # Add more tests to verify that correct mails are sent to correct people here
  408. if __name__ == "__main__":
  409. unittest.main(verbosity=2)