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

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272
  1. from unittest import mock
  2. from django.contrib.auth.models import AnonymousUser
  3. from django.core import mail
  4. from django.test import TestCase
  5. from django.urls import reverse
  6. from django_webtest import WebTest
  7. from oscar.apps.customer.forms import ProductAlertForm
  8. from oscar.apps.customer.models import ProductAlert
  9. from oscar.core.loading import get_class
  10. from oscar.test.factories import (
  11. ProductAlertFactory,
  12. UserFactory,
  13. create_product,
  14. create_stockrecord,
  15. )
  16. CustomerDispatcher = get_class("customer.utils", "CustomerDispatcher")
  17. AlertsDispatcher = get_class("customer.alerts.utils", "AlertsDispatcher")
  18. class TestProductAlert(WebTest):
  19. def setUp(self):
  20. self.user = UserFactory()
  21. self.product = create_product(num_in_stock=0)
  22. def test_can_create_a_stock_alert(self):
  23. product_page = self.app.get(self.product.get_absolute_url(), user=self.user)
  24. form = product_page.forms["alert_form"]
  25. form.submit()
  26. alerts = ProductAlert.objects.filter(user=self.user)
  27. assert len(alerts) == 1
  28. alert = alerts[0]
  29. assert ProductAlert.ACTIVE == alert.status
  30. assert alert.product == self.product
  31. def test_cannot_create_multiple_alerts_for_one_product(self):
  32. ProductAlertFactory(
  33. user=self.user, product=self.product, status=ProductAlert.ACTIVE
  34. )
  35. # Alert form should not allow creation of additional alerts.
  36. form = ProductAlertForm(user=self.user, product=self.product, data={})
  37. assert not form.is_valid()
  38. assert (
  39. "You already have an active alert for this product"
  40. in form.errors["__all__"][0]
  41. )
  42. class TestAUserWithAnActiveStockAlert(WebTest):
  43. def setUp(self):
  44. self.user = UserFactory()
  45. self.product = create_product()
  46. self.stockrecord = create_stockrecord(self.product, num_in_stock=0)
  47. product_page = self.app.get(self.product.get_absolute_url(), user=self.user)
  48. form = product_page.forms["alert_form"]
  49. form.submit()
  50. def test_can_cancel_it(self):
  51. alerts = ProductAlert.objects.filter(user=self.user)
  52. assert len(alerts) == 1
  53. alert = alerts[0]
  54. assert not alert.is_cancelled
  55. self.app.get(
  56. reverse("customer:alerts-cancel-by-pk", kwargs={"pk": alert.pk}),
  57. user=self.user,
  58. )
  59. alerts = ProductAlert.objects.filter(user=self.user)
  60. assert len(alerts) == 1
  61. alert = alerts[0]
  62. assert alert.is_cancelled
  63. def test_gets_notified_when_it_is_back_in_stock(self):
  64. self.stockrecord.num_in_stock = 10
  65. self.stockrecord.save()
  66. assert self.user.notifications.all().count() == 1
  67. def test_gets_emailed_when_it_is_back_in_stock(self):
  68. self.stockrecord.num_in_stock = 10
  69. self.stockrecord.save()
  70. assert len(mail.outbox) == 1
  71. def test_does_not_get_emailed_when_it_is_saved_but_still_zero_stock(self):
  72. self.stockrecord.num_in_stock = 0
  73. self.stockrecord.save()
  74. assert len(mail.outbox) == 0
  75. @mock.patch("oscar.apps.communication.utils.Dispatcher.notify_user")
  76. def test_site_notification_sent(self, mock_notify):
  77. self.stockrecord.num_in_stock = 10
  78. self.stockrecord.save()
  79. mock_notify.assert_called_once_with(
  80. self.user,
  81. "{} is back in stock".format(self.product.title),
  82. body='<a href="{}">{}</a> is back in stock'.format(
  83. self.product.get_absolute_url(), self.product.title
  84. ),
  85. )
  86. @mock.patch("oscar.apps.communication.utils.Dispatcher.notify_user")
  87. def test_product_title_truncated_in_alert_notification_subject(self, mock_notify):
  88. self.product.title = (
  89. "Aut nihil dignissimos perspiciatis. Beatae sed consequatur odit incidunt. "
  90. "Quaerat labore perferendis quasi aut sunt maxime accusamus laborum. "
  91. "Ut quam repudiandae occaecati eligendi. Nihil rem vel eos."
  92. )
  93. self.product.save()
  94. self.stockrecord.num_in_stock = 10
  95. self.stockrecord.save()
  96. mock_notify.assert_called_once_with(
  97. self.user,
  98. "{} is back in stock".format(self.product.title[:200]),
  99. body='<a href="{}">{}</a> is back in stock'.format(
  100. self.product.get_absolute_url(), self.product.title
  101. ),
  102. )
  103. class TestAnAnonymousUser(WebTest):
  104. def test_can_create_a_stock_alert(self):
  105. product = create_product(num_in_stock=0)
  106. product_page = self.app.get(product.get_absolute_url())
  107. form = product_page.forms["alert_form"]
  108. form["email"] = "john@smith.com"
  109. form.submit()
  110. alerts = ProductAlert.objects.filter(email="john@smith.com")
  111. assert len(alerts) == 1
  112. alert = alerts[0]
  113. assert ProductAlert.UNCONFIRMED == alert.status
  114. assert alert.product == product
  115. def test_can_cancel_unconfirmed_stock_alert(self):
  116. alert = ProductAlertFactory(
  117. user=None, email="john@smith.com", status=ProductAlert.UNCONFIRMED
  118. )
  119. self.app.get(
  120. reverse("customer:alerts-cancel-by-key", kwargs={"key": alert.key})
  121. )
  122. alert.refresh_from_db()
  123. assert alert.is_cancelled
  124. def test_cannot_create_multiple_alerts_for_one_product(self):
  125. product = create_product(num_in_stock=0)
  126. alert = ProductAlertFactory(user=None, product=product, email="john@smith.com")
  127. alert.status = ProductAlert.ACTIVE
  128. alert.save()
  129. # Alert form should not allow creation of additional alerts.
  130. form = ProductAlertForm(
  131. user=AnonymousUser(), product=product, data={"email": "john@smith.com"}
  132. )
  133. assert not form.is_valid()
  134. assert (
  135. "There is already an active stock alert for john@smith.com"
  136. in form.errors["__all__"][0]
  137. )
  138. def test_cannot_create_multiple_unconfirmed_alerts(self):
  139. # Create an unconfirmed alert
  140. ProductAlertFactory(
  141. user=None, email="john@smith.com", status=ProductAlert.UNCONFIRMED
  142. )
  143. # Alert form should not allow creation of additional alerts.
  144. form = ProductAlertForm(
  145. user=AnonymousUser(),
  146. product=create_product(num_in_stock=0),
  147. data={"email": "john@smith.com"},
  148. )
  149. assert not form.is_valid()
  150. message = "john@smith.com has been sent a confirmation email for another product alert on this site."
  151. assert message in form.errors["__all__"][0]
  152. class TestHurryMode(TestCase):
  153. def setUp(self):
  154. self.user = UserFactory()
  155. self.product = create_product()
  156. self.dispatcher = AlertsDispatcher()
  157. def test_hurry_mode_not_set_when_stock_high(self):
  158. # One alert, 5 items in stock. No need to hurry.
  159. create_stockrecord(self.product, num_in_stock=5)
  160. ProductAlert.objects.create(user=self.user, product=self.product)
  161. self.dispatcher.send_product_alert_email_for_user(self.product)
  162. assert len(mail.outbox) == 1
  163. assert (
  164. "Beware that the amount of items in stock is limited"
  165. not in mail.outbox[0].body
  166. )
  167. def test_hurry_mode_set_when_stock_low(self):
  168. # Two alerts, 1 item in stock. Hurry mode should be set.
  169. create_stockrecord(self.product, num_in_stock=1)
  170. ProductAlert.objects.create(user=self.user, product=self.product)
  171. ProductAlert.objects.create(user=UserFactory(), product=self.product)
  172. self.dispatcher.send_product_alert_email_for_user(self.product)
  173. assert len(mail.outbox) == 2
  174. assert (
  175. "Beware that the amount of items in stock is limited" in mail.outbox[0].body
  176. )
  177. def test_hurry_mode_not_set_multiple_stockrecords(self):
  178. # Two stockrecords, 5 items in stock for one. No need to hurry.
  179. create_stockrecord(self.product, num_in_stock=1)
  180. create_stockrecord(self.product, num_in_stock=5)
  181. ProductAlert.objects.create(user=self.user, product=self.product)
  182. self.dispatcher.send_product_alert_email_for_user(self.product)
  183. assert (
  184. "Beware that the amount of items in stock is limited"
  185. not in mail.outbox[0].body
  186. )
  187. def test_hurry_mode_set_multiple_stockrecords(self):
  188. # Two stockrecords, low stock on both. Hurry mode should be set.
  189. create_stockrecord(self.product, num_in_stock=1)
  190. create_stockrecord(self.product, num_in_stock=1)
  191. ProductAlert.objects.create(user=self.user, product=self.product)
  192. ProductAlert.objects.create(user=UserFactory(), product=self.product)
  193. self.dispatcher.send_product_alert_email_for_user(self.product)
  194. assert (
  195. "Beware that the amount of items in stock is limited" in mail.outbox[0].body
  196. )
  197. class TestAlertMessageSending(TestCase):
  198. def setUp(self):
  199. self.user = UserFactory()
  200. self.product = create_product()
  201. create_stockrecord(self.product, num_in_stock=1)
  202. self.dispatcher = AlertsDispatcher()
  203. @mock.patch("oscar.apps.communication.utils.Dispatcher.dispatch_direct_messages")
  204. def test_alert_confirmation_uses_dispatcher(self, mock_dispatch):
  205. alert = ProductAlert.objects.create(
  206. email="test@example.com",
  207. key="dummykey",
  208. status=ProductAlert.UNCONFIRMED,
  209. product=self.product,
  210. )
  211. AlertsDispatcher().send_product_alert_confirmation_email_for_user(alert)
  212. assert mock_dispatch.call_count == 1
  213. assert mock_dispatch.call_args[0][0] == "test@example.com"
  214. @mock.patch("oscar.apps.communication.utils.Dispatcher.dispatch_user_messages")
  215. def test_alert_uses_dispatcher(self, mock_dispatch):
  216. ProductAlert.objects.create(user=self.user, product=self.product)
  217. self.dispatcher.send_product_alert_email_for_user(self.product)
  218. assert mock_dispatch.call_count == 1
  219. assert mock_dispatch.call_args[0][0] == self.user
  220. def test_alert_creates_email_obj(self):
  221. ProductAlert.objects.create(user=self.user, product=self.product)
  222. self.dispatcher.send_product_alert_email_for_user(self.product)
  223. assert self.user.emails.count() == 1