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.

test_alert.py 10KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262
  1. import mock
  2. import os
  3. import warnings
  4. from django_webtest import WebTest
  5. from django.contrib.auth.models import AnonymousUser
  6. from django.core.urlresolvers import reverse
  7. from django.core import mail
  8. from django.test import TestCase
  9. from oscar.utils.deprecation import RemovedInOscar20Warning
  10. from oscar.apps.customer.alerts.utils import (send_alert_confirmation,
  11. send_product_alerts)
  12. from oscar.apps.customer.forms import ProductAlertForm
  13. from oscar.apps.customer.models import ProductAlert
  14. from oscar.test.factories import (
  15. create_product, create_stockrecord, ProductAlertFactory, UserFactory)
  16. class TestAUser(WebTest):
  17. def test_can_create_a_stock_alert(self):
  18. user = UserFactory()
  19. product = create_product(num_in_stock=0)
  20. product_page = self.app.get(product.get_absolute_url(), user=user)
  21. form = product_page.forms['alert_form']
  22. form.submit()
  23. alerts = ProductAlert.objects.filter(user=user)
  24. self.assertEqual(1, len(alerts))
  25. alert = alerts[0]
  26. self.assertEqual(ProductAlert.ACTIVE, alert.status)
  27. self.assertEqual(alert.product, product)
  28. class TestAUserWithAnActiveStockAlert(WebTest):
  29. def setUp(self):
  30. self.user = UserFactory()
  31. self.product = create_product()
  32. self.stockrecord = create_stockrecord(self.product, num_in_stock=0)
  33. product_page = self.app.get(self.product.get_absolute_url(),
  34. user=self.user)
  35. form = product_page.forms['alert_form']
  36. form.submit()
  37. def test_can_cancel_it(self):
  38. alerts = ProductAlert.objects.filter(user=self.user)
  39. self.assertEqual(1, len(alerts))
  40. alert = alerts[0]
  41. self.assertFalse(alert.is_cancelled)
  42. self.app.get(
  43. reverse('customer:alerts-cancel-by-pk', kwargs={'pk': alert.pk}),
  44. user=self.user)
  45. alerts = ProductAlert.objects.filter(user=self.user)
  46. self.assertEqual(1, len(alerts))
  47. alert = alerts[0]
  48. self.assertTrue(alert.is_cancelled)
  49. def test_gets_notified_when_it_is_back_in_stock(self):
  50. self.stockrecord.num_in_stock = 10
  51. self.stockrecord.save()
  52. self.assertEqual(1, self.user.notifications.all().count())
  53. def test_gets_emailed_when_it_is_back_in_stock(self):
  54. self.stockrecord.num_in_stock = 10
  55. self.stockrecord.save()
  56. self.assertEqual(1, len(mail.outbox))
  57. def test_does_not_get_emailed_when_it_is_saved_but_still_zero_stock(self):
  58. self.stockrecord.num_in_stock = 0
  59. self.stockrecord.save()
  60. self.assertEqual(0, len(mail.outbox))
  61. class TestAnAnonymousUser(WebTest):
  62. def test_can_create_a_stock_alert(self):
  63. product = create_product(num_in_stock=0)
  64. product_page = self.app.get(product.get_absolute_url())
  65. form = product_page.forms['alert_form']
  66. form['email'] = 'john@smith.com'
  67. form.submit()
  68. alerts = ProductAlert.objects.filter(email='john@smith.com')
  69. self.assertEqual(1, len(alerts))
  70. alert = alerts[0]
  71. self.assertEqual(ProductAlert.UNCONFIRMED, alert.status)
  72. self.assertEqual(alert.product, product)
  73. def test_can_cancel_unconfirmed_stock_alert(self):
  74. alert = ProductAlertFactory(
  75. user=None, email='john@smith.com', status=ProductAlert.UNCONFIRMED)
  76. self.app.get(
  77. reverse('customer:alerts-cancel-by-key', kwargs={'key': alert.key}))
  78. alert.refresh_from_db()
  79. self.assertTrue(alert.is_cancelled)
  80. def test_cannot_create_multiple_unconfirmed_alerts(self):
  81. # Create an unconfirmed alert
  82. alert = ProductAlertFactory(
  83. user=None, email='john@smith.com', status=ProductAlert.UNCONFIRMED)
  84. # Alert form should not allow creation of additional alerts.
  85. form = ProductAlertForm(
  86. user=AnonymousUser(),
  87. product=create_product(num_in_stock=0),
  88. data={'email': 'john@smith.com'},
  89. )
  90. self.assertFalse(form.is_valid())
  91. self.assertIn(
  92. "john@smith.com has been sent a confirmation email for another "
  93. "product alert on this site.", form.errors['__all__'][0])
  94. class TestHurryMode(TestCase):
  95. def setUp(self):
  96. self.user = UserFactory()
  97. self.product = create_product()
  98. def test_hurry_mode_not_set_when_stock_high(self):
  99. # One alert, 5 items in stock. No need to hurry.
  100. create_stockrecord(self.product, num_in_stock=5)
  101. ProductAlert.objects.create(user=self.user, product=self.product)
  102. send_product_alerts(self.product)
  103. self.assertEqual(1, len(mail.outbox))
  104. self.assertNotIn('Beware that the amount of items in stock is limited',
  105. mail.outbox[0].body)
  106. def test_hurry_mode_set_when_stock_low(self):
  107. # Two alerts, 1 item in stock. Hurry mode should be set.
  108. create_stockrecord(self.product, num_in_stock=1)
  109. ProductAlert.objects.create(user=self.user, product=self.product)
  110. ProductAlert.objects.create(user=UserFactory(), product=self.product)
  111. send_product_alerts(self.product)
  112. self.assertEqual(2, len(mail.outbox))
  113. self.assertIn('Beware that the amount of items in stock is limited',
  114. mail.outbox[0].body)
  115. def test_hurry_mode_not_set_multiple_stockrecords(self):
  116. # Two stockrecords, 5 items in stock for one. No need to hurry.
  117. create_stockrecord(self.product, num_in_stock=1)
  118. create_stockrecord(self.product, num_in_stock=5)
  119. ProductAlert.objects.create(user=self.user, product=self.product)
  120. send_product_alerts(self.product)
  121. self.assertNotIn('Beware that the amount of items in stock is limited',
  122. mail.outbox[0].body)
  123. def test_hurry_mode_set_multiple_stockrecords(self):
  124. # Two stockrecords, low stock on both. Hurry mode should be set.
  125. create_stockrecord(self.product, num_in_stock=1)
  126. create_stockrecord(self.product, num_in_stock=1)
  127. ProductAlert.objects.create(user=self.user, product=self.product)
  128. ProductAlert.objects.create(user=UserFactory(), product=self.product)
  129. send_product_alerts(self.product)
  130. self.assertIn('Beware that the amount of items in stock is limited',
  131. mail.outbox[0].body)
  132. class TestAlertMessageSending(TestCase):
  133. def setUp(self):
  134. self.user = UserFactory()
  135. self.product = create_product()
  136. create_stockrecord(self.product, num_in_stock=1)
  137. @mock.patch('oscar.apps.customer.utils.Dispatcher.dispatch_direct_messages')
  138. def test_alert_confirmation_uses_dispatcher(self, mock_dispatch):
  139. alert = ProductAlert.objects.create(
  140. email='test@example.com',
  141. key='dummykey',
  142. status=ProductAlert.UNCONFIRMED,
  143. product=self.product
  144. )
  145. send_alert_confirmation(alert)
  146. self.assertEqual(mock_dispatch.call_count, 1)
  147. self.assertEqual(mock_dispatch.call_args[0][0], 'test@example.com')
  148. @mock.patch('oscar.apps.customer.utils.Dispatcher.dispatch_user_messages')
  149. def test_alert_uses_dispatcher(self, mock_dispatch):
  150. ProductAlert.objects.create(user=self.user, product=self.product)
  151. send_product_alerts(self.product)
  152. self.assertEqual(mock_dispatch.call_count, 1)
  153. self.assertEqual(mock_dispatch.call_args[0][0], self.user)
  154. def test_alert_creates_email_obj(self):
  155. ProductAlert.objects.create(user=self.user, product=self.product)
  156. send_product_alerts(self.product)
  157. self.assertEqual(self.user.emails.count(), 1)
  158. @staticmethod
  159. def _load_deprecated_template(path):
  160. from django.template.loader import select_template
  161. # Replace the old template paths with new ones, thereby mocking
  162. # the presence of the deprecated templates.
  163. if path.startswith('customer/alerts/emails/'):
  164. old_path = path.replace('customer/alerts/emails/', '')
  165. path_map = {
  166. 'confirmation_body.txt': 'confirmation_body.txt',
  167. 'confirmation_subject.txt': 'confirmation_subject.txt',
  168. 'alert_body.txt': 'body.txt',
  169. 'alert_subject.txt': 'subject.txt',
  170. }
  171. new_path = 'customer/emails/commtype_product_alert_{}'.format(
  172. path_map[old_path]
  173. )
  174. # We use 'select_template' because 'load_template' is mocked...
  175. return select_template([new_path])
  176. return select_template([path])
  177. @mock.patch('oscar.apps.customer.alerts.utils.loader.get_template')
  178. def test_deprecated_notification_templates_work(self, mock_loader):
  179. """
  180. This test can be removed when support for the deprecated templates is
  181. dropped.
  182. """
  183. mock_loader.side_effect = self._load_deprecated_template
  184. with warnings.catch_warnings(record=True) as warning_list:
  185. warnings.simplefilter("always")
  186. alert = ProductAlert.objects.create(
  187. email='test@example.com',
  188. key='dummykey',
  189. status=ProductAlert.UNCONFIRMED,
  190. product=self.product
  191. )
  192. send_alert_confirmation(alert)
  193. # Check that warnings were raised
  194. self.assertTrue(any(item.category == RemovedInOscar20Warning
  195. for item in warning_list))
  196. # Check that alerts were still sent
  197. self.assertEqual(len(mail.outbox), 1)
  198. @mock.patch('oscar.apps.customer.alerts.utils.loader.get_template')
  199. def test_deprecated_alert_templates_work(self, mock_loader):
  200. """
  201. This test can be removed when support for the deprecated templates is
  202. dropped.
  203. """
  204. mock_loader.side_effect = self._load_deprecated_template
  205. with warnings.catch_warnings(record=True) as warning_list:
  206. warnings.simplefilter("always")
  207. ProductAlert.objects.create(user=self.user, product=self.product)
  208. send_product_alerts(self.product)
  209. # Check that warnings were raised
  210. self.assertTrue(any(item.category == RemovedInOscar20Warning
  211. for item in warning_list))
  212. # Check that alerts were still sent
  213. self.assertEqual(len(mail.outbox), 1)