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.

utils.py 5.7KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174
  1. import os
  2. import queue
  3. import shutil
  4. import threading
  5. from datetime import date
  6. from django.conf import settings
  7. from django.contrib.auth.models import AnonymousUser
  8. from django.contrib.messages.storage.fallback import FallbackStorage
  9. from django.contrib.sessions.backends.db import SessionStore
  10. from django.core import mail
  11. from django.core.signing import Signer
  12. from django.db import connection
  13. from django.test import RequestFactory as BaseRequestFactory
  14. from sorl.thumbnail.conf import settings as sorl_settings
  15. from oscar.core.loading import get_class, get_model
  16. from oscar.core.thumbnails import get_thumbnailer
  17. from oscar.test.factories import ProductImageFactory, UserFactory
  18. OSCAR_IMAGE_FOLDER_FORMATTED = 'images/products/{0}/{1:02d}/'.format(date.today().year, date.today().month)
  19. FULL_PATH_TO_IMAGES_FOLDER = os.path.join(settings.MEDIA_ROOT, OSCAR_IMAGE_FOLDER_FORMATTED)
  20. FULL_PATH_TO_SORL_THUMBNAILS_FOLDER = os.path.join(settings.MEDIA_ROOT, sorl_settings.THUMBNAIL_PREFIX)
  21. EASY_THUMBNAIL_BASEDIR = 'thumbnails'
  22. FULL_PATH_TO_EASY_THUMBNAILS_FOLDER = os.path.join(
  23. settings.MEDIA_ROOT, EASY_THUMBNAIL_BASEDIR, OSCAR_IMAGE_FOLDER_FORMATTED)
  24. def remove_image_folders():
  25. if os.path.exists(FULL_PATH_TO_IMAGES_FOLDER):
  26. shutil.rmtree(FULL_PATH_TO_IMAGES_FOLDER)
  27. if os.path.exists(FULL_PATH_TO_SORL_THUMBNAILS_FOLDER):
  28. shutil.rmtree(FULL_PATH_TO_SORL_THUMBNAILS_FOLDER)
  29. if os.path.exists(FULL_PATH_TO_EASY_THUMBNAILS_FOLDER):
  30. shutil.rmtree(FULL_PATH_TO_EASY_THUMBNAILS_FOLDER)
  31. def get_thumbnail_full_path(thumbnail_url):
  32. # Thumbnail URL looks like "/media/...........jpg". To be able to
  33. # create full path we need to remove the first "/" from it.
  34. return os.path.join(settings.PUBLIC_ROOT, thumbnail_url[1:])
  35. class ThumbnailMixin:
  36. @classmethod
  37. def setUpClass(cls):
  38. super().setUpClass()
  39. # Remove images folders - they could be created in the previous tests.
  40. remove_image_folders()
  41. def setUp(self):
  42. super().setUp()
  43. self.thumbnail_options = {
  44. 'size': 'x50',
  45. 'upscale': False,
  46. }
  47. def tearDown(self):
  48. super().tearDown()
  49. # Remove created images folders after each test.
  50. remove_image_folders()
  51. def create_product_images(self, qty=2, product=None):
  52. self._test_images_folder_is_empty()
  53. kwargs = {}
  54. if product is not None:
  55. kwargs['product'] = product
  56. self.images = ProductImageFactory.create_batch(qty, **kwargs)
  57. file_names = os.listdir(FULL_PATH_TO_IMAGES_FOLDER)
  58. assert len(file_names) == qty # New images added.
  59. def create_thumbnails(self):
  60. thumbnailer = get_thumbnailer()
  61. thumbnails_full_paths = []
  62. for image in self.images:
  63. thumbnail = thumbnailer.generate_thumbnail(image.original, **self.thumbnail_options)
  64. thumbnails_full_paths.append(get_thumbnail_full_path(thumbnail.url))
  65. # Check thumbnails exist in the file system.
  66. for path in thumbnails_full_paths:
  67. assert os.path.isfile(path) # `isfile` returns `True` if path is an existing regular file.
  68. return thumbnails_full_paths
  69. def _test_images_folder_is_empty(self):
  70. if not os.path.exists(FULL_PATH_TO_IMAGES_FOLDER):
  71. os.makedirs(FULL_PATH_TO_IMAGES_FOLDER)
  72. file_names = os.listdir(FULL_PATH_TO_IMAGES_FOLDER)
  73. assert len(file_names) == 0
  74. def _test_thumbnails_not_exist(self, thumbnails_full_paths):
  75. for path in thumbnails_full_paths:
  76. assert not os.path.isfile(path)
  77. class EmailsMixin:
  78. def setUp(self):
  79. super().setUp()
  80. self.user = UserFactory()
  81. def _test_send_plain_text_and_html(self, outboxed_email):
  82. email = outboxed_email
  83. assert '</p>' not in email.body # Plain text body (because w/o </p> tags)
  84. html_content = email.alternatives[0][0]
  85. assert '</p>' in html_content
  86. mimetype = email.alternatives[0][1]
  87. assert mimetype == 'text/html'
  88. def _test_common_part(self):
  89. assert len(mail.outbox) == 1
  90. assert mail.outbox[0].to == [self.user.email]
  91. self._test_send_plain_text_and_html(mail.outbox[0])
  92. class RequestFactory(BaseRequestFactory):
  93. Basket = get_model('basket', 'basket')
  94. selector = get_class('partner.strategy', 'Selector')()
  95. def request(self, user=None, basket=None, **request):
  96. request = super().request(**request)
  97. request.user = user or AnonymousUser()
  98. request.session = SessionStore()
  99. request._messages = FallbackStorage(request)
  100. # Mimic basket middleware
  101. request.strategy = self.selector.strategy(
  102. request=request, user=request.user)
  103. request.basket = basket or self.Basket()
  104. request.basket.strategy = request.strategy
  105. request.basket_hash = Signer().sign(basket.pk) if basket else None
  106. request.cookies_to_delete = []
  107. return request
  108. def run_concurrently(fn, kwargs=None, num_threads=5):
  109. exceptions = queue.Queue()
  110. def worker(**kwargs):
  111. try:
  112. fn(**kwargs)
  113. except Exception as exc:
  114. exceptions.put(exc)
  115. else:
  116. exceptions.put(None)
  117. finally:
  118. connection.close()
  119. kwargs = kwargs if kwargs is not None else {}
  120. # Run them
  121. threads = [
  122. threading.Thread(target=worker, name='thread-%d' % i, kwargs=kwargs)
  123. for i in range(num_threads)
  124. ]
  125. try:
  126. for thread in threads:
  127. thread.start()
  128. finally:
  129. for thread in threads:
  130. thread.join()
  131. # Retrieve exceptions
  132. exceptions = [exceptions.get(block=False) for i in range(num_threads)]
  133. return [exc for exc in exceptions if exc is not None]