internlm/SWE-Fixer-Train-110K huggingface dataset:
Repository: fidals/shopelectro

Issue:
/robots.txt url returns 301. stb2
Check this for example:
https://www.shopelectro.ru/robots.txt


Discussion:

Resolving patch:
diff --git a/shopelectro/urls.py b/shopelectro/urls.py
--- a/shopelectro/urls.py
+++ b/shopelectro/urls.py
@@ -92,7 +92,7 @@ def cache_page(arg):  # Ignore PyFlakesBear
 
 custom_pages = [
     custom_page_url(r'^(?P<page>)$', cached_2h(views.IndexPage.as_view())),
-    custom_page_url(r'^(?P<page>robots\.txt)/$', RobotsView.as_view(in_db=True)),
+    custom_page_url(r'^(?P<page>robots\.txt)$', RobotsView.as_view(in_db=True)),
     custom_page_url(r'^(?P<page>search)/$', views.Search.as_view()),
     custom_page_url(r'^(?P<page>catalog)/$', cached_2h(views.CategoryTree.as_view())),
     custom_page_url(r'^shop/(?P<page>order)/$', views.OrderPage.as_view()),


Test patch:
Repository: fidals/shopelectro

Issue:
Wrong QuerySet in RobotsView stb2
Currently it looks so `CustomPage.objects.filter(slug='robots')` and should be `.filter(slug='robots.txt')`


Discussion:

Resolving patch:
diff --git a/shopelectro/urls.py b/shopelectro/urls.py
--- a/shopelectro/urls.py
+++ b/shopelectro/urls.py
@@ -6,6 +6,7 @@
 from django.contrib.sitemaps.views import sitemap
 from django.views.decorators.cache import cache_page
 
+from pages.models import CustomPage
 from pages.views import RobotsView, SitemapPage
 from pages.urls import custom_page_url
 
@@ -92,7 +93,10 @@ def cache_page(arg):  # Ignore PyFlakesBear
 
 custom_pages = [
     custom_page_url(r'^(?P<page>)$', cached_2h(views.IndexPage.as_view())),
-    custom_page_url(r'^(?P<page>robots\.txt)$', RobotsView.as_view(in_db=True)),
+    custom_page_url(
+        r'^(?P<page>robots\.txt)$',
+        RobotsView.as_view(in_db=True, objects=CustomPage.objects.filter(slug='robots.txt'))
+    ),
     custom_page_url(r'^(?P<page>search)/$', views.Search.as_view()),
     custom_page_url(r'^(?P<page>catalog)/$', cached_2h(views.CategoryTree.as_view())),
     custom_page_url(r'^shop/(?P<page>order)/$', views.OrderPage.as_view()),


Test patch:
Repository: fidals/shopelectro

Issue:
helpers.py:58-59: Move selenium timeout to env var. stb2...
The puzzle `371-6673a075` from #371 has to be resolved:

https://github.com/fidals/shopelectro/blob/7d319f81942692c16105a8f8703e483a7067f1ef/shopelectro/tests/helpers.py#L58-L59

The puzzle was created by duker33 on 19-Jun-18. 

Estimate: 15 minutes,     

If you have any technical questions, don't ask me, submit new tickets instead. The task will be "done" when the problem is fixed and the text of the puzzle is _removed_ from the source code. Here is more about [PDD](http://www.yegor256.com/2009/03/04/pdd.html) and [about me](http://www.yegor256.com/2017/04/05/pdd-in-action.html).


Discussion:
@artemiyds2 @duker33 please pay attention to this new issue.

Resolving patch:
diff --git a/shopelectro/settings/base.py b/shopelectro/settings/base.py
--- a/shopelectro/settings/base.py
+++ b/shopelectro/settings/base.py
@@ -217,6 +217,8 @@
 }
 
 SELENIUM_URL = os.environ.get('SELENIUM_URL', 'http://selenium:4444/wd/hub')
+SELENIUM_WAIT_SECONDS = int(os.environ.get('SELENIUM_WAIT_SECONDS', 60))
+SELENIUM_TIMEOUT_SECONDS = int(os.environ.get('SELENIUM_TIMEOUT_SECONDS', 30))
 
 SITE_CREATED = datetime(2013, 1, 1)
 


Test patch:
diff --git a/shopelectro/tests/helpers.py b/shopelectro/tests/helpers.py
--- a/shopelectro/tests/helpers.py
+++ b/shopelectro/tests/helpers.py
@@ -58,9 +58,9 @@ def setUpClass(cls):
         )
         # @todo #371:15m Move selenium timeout to env var. stb2
         #  To be able to change it from drone without touching code.
-        cls.wait = WebDriverWait(cls.browser, 60)
+        cls.wait = WebDriverWait(cls.browser, settings.SELENIUM_WAIT_SECONDS)
         cls.browser.implicitly_wait(30)
-        cls.browser.set_page_load_timeout(30)
+        cls.browser.set_page_load_timeout(settings.SELENIUM_TIMEOUT_SECONDS)
         # Fresh created browser failures on maximizing window.
         # This bug is won't fixed by selenium guys https://goo.gl/6Ttguf
         # Ohh, so selenium is so selenium ...
Repository: fidals/shopelectro

Issue:
Check update catalog for ftp connection timeouts
Now we get this error trying download files with ftp

```
==> PASV ... couldn't connect to 37.18.77.165 port 57034: Connection timed out
==> PASV ... couldn't connect to 37.18.77.165 port 51454: Connection timed out
```

It leads to errors in tests
https://ci.fidals.com/fidals/shopelectro/439/8
Check update catalog for ftp connection timeouts
Now we get this error trying download files with ftp

```
==> PASV ... couldn't connect to 37.18.77.165 port 57034: Connection timed out
==> PASV ... couldn't connect to 37.18.77.165 port 51454: Connection timed out
```

It leads to errors in tests
https://ci.fidals.com/fidals/shopelectro/439/8


Discussion:
just added tests skip for a while with custom exceptions. We can't prevent test errors in case of files not downloaded
just added tests skip for a while with custom exceptions. We can't prevent test errors in case of files not downloaded

Resolving patch:
diff --git a/shopelectro/exception.py b/shopelectro/exception.py
new file mode 100644
--- /dev/null
+++ b/shopelectro/exception.py
@@ -0,0 +1,16 @@
+class UpdateCatalogException(BaseException):
+    def __init__(self, message=''):
+        self.message = message
+
+    def __str__(self, *args, **kwargs):
+        return self.message
+
+
+class DownloadFilesError(UpdateCatalogException):
+    def __init__(self, message):
+        self.message = message
+
+    def __str__(self, *args, **kwargs):
+        return (
+            f'Config error. {self.message}.'
+        )
diff --git a/shopelectro/management/commands/_update_catalog/utils.py b/shopelectro/management/commands/_update_catalog/utils.py
--- a/shopelectro/management/commands/_update_catalog/utils.py
+++ b/shopelectro/management/commands/_update_catalog/utils.py
@@ -14,8 +14,10 @@
 import requests
 from django.conf import settings
 
+from shopelectro.exception import DownloadFilesError
 
 logger = logging.getLogger(__name__)
+DOWNLOAD_FILES_TIMEOUT = 5.0
 UUID_TYPE = str
 Data = Dict[str, Dict[str, dict]]
 NOT_SAVE_TEMPLATE = '{entity} with name="{name}" has no {field}. It\'ll not be' \
@@ -110,7 +112,11 @@ def download_catalog(destination):
         )
     )
 
-    subprocess.run(wget_command, shell=True)
+    try:
+        subprocess.run(wget_command, timeout=DOWNLOAD_FILES_TIMEOUT, shell=True)
+    except subprocess.TimeoutExpired as e:
+        raise DownloadFilesError(str(e))
+
     assert os.path.exists(os.path.join(
         destination, settings.FTP_IP)), 'Files do not downloaded...'
     logger.info('Download catalog - completed...')


Test patch:
diff --git a/shopelectro/tests/tests_commands.py b/shopelectro/tests/tests_commands.py
--- a/shopelectro/tests/tests_commands.py
+++ b/shopelectro/tests/tests_commands.py
@@ -6,6 +6,7 @@
 import glob
 import os
 import random
+import unittest
 import uuid
 from xml.etree import ElementTree
 
@@ -26,6 +27,7 @@
 """
 
 
+@unittest.skip('wait while 1C fixed')
 class UpdateProducts(TestCase):
 
     @classmethod
Repository: fidals/shopelectro

Issue:
Repeat stb's fix for front_build dir
https://github.com/fidals/stroyprombeton/pull/270


Discussion:

Resolving patch:
diff --git a/shopelectro/settings/base.py b/shopelectro/settings/base.py
--- a/shopelectro/settings/base.py
+++ b/shopelectro/settings/base.py
@@ -152,7 +152,7 @@
 STATICFILES_STORAGE = 'django.contrib.staticfiles.storage.ManifestStaticFilesStorage'
 
 STATICFILES_DIRS = [
-    os.path.join(BASE_DIR, 'front/build'),
+    os.path.join(BASE_DIR, 'front_build'),
     ASSETS_DIR,
 ]
 


Test patch:
diff --git a/shopelectro/tests/tests_selenium.py b/shopelectro/tests/tests_selenium.py
--- a/shopelectro/tests/tests_selenium.py
+++ b/shopelectro/tests/tests_selenium.py
@@ -1056,9 +1056,7 @@ def test_autocomplete_can_expand_and_collapse(self):
 
         # remove search term ...
         self.clear_input()
-        self.wait.until(EC.text_to_be_present_in_element(
-            (By.CLASS_NAME, 'js-search-input'), '')
-        )
+        self.wait.until(EC.text_to_be_present_in_element(self.INPUT_LOCATOR, ''))
         # ... and autocomplete collapse
         self.assertFalse(self.autocomplete.is_displayed())
Repository: fidals/shopelectro

Issue:
dc-volumes.yml:27-28: Repair npm container reusing at...
The puzzle `449-27d8c19e` from #449 has to be resolved:

https://github.com/fidals/shopelectro/blob/f8ce85246c00411fcb21d025b0fb42abab81c7d5/docker/dc-volumes.yml#L27-L28

The puzzle was created by duker33 on 28-Jul-18. 

Estimate: 60 minutes,     

If you have any technical questions, don't ask me, submit new tickets instead. The task will be "done" when the problem is fixed and the text of the puzzle is _removed_ from the source code. Here is more about [PDD](http://www.yegor256.com/2009/03/04/pdd.html) and [about me](http://www.yegor256.com/2017/04/05/pdd-in-action.html).
В Firefox не поставить оценку товару
Не нажимаются звездочки, когда хочешь оставить отзыв о товаре

Firefox 58.0.2, чистый профиль. В Chromium работают


Discussion:
@artemiyds2 @duker33 please pay attention to this new issue.


Resolving patch:
diff --git a/shopelectro/management/commands/_update_catalog/utils.py b/shopelectro/management/commands/_update_catalog/utils.py
--- a/shopelectro/management/commands/_update_catalog/utils.py
+++ b/shopelectro/management/commands/_update_catalog/utils.py
@@ -17,7 +17,7 @@
 from shopelectro.exception import DownloadFilesError
 
 logger = logging.getLogger(__name__)
-DOWNLOAD_FILES_TIMEOUT = 15.0
+DOWNLOAD_FILES_TIMEOUT = 40.0
 UUID_TYPE = str
 Data = Dict[str, Dict[str, dict]]
 NOT_SAVE_TEMPLATE = '{entity} with name="{name}" has no {field}. It\'ll not be' \


Test patch:
Repository: fidals/shopelectro

Issue:
Pin celery version to 4.1.1 and move SELENIUM_* variables to local.py/drone.py
See the [issue](https://github.com/celery/kombu/issues/870) for a detail about celery.

SELENIUM_* variables are not required for prod, so let's move them from base.py


Discussion:

Resolving patch:
diff --git a/shopelectro/settings/base.py b/shopelectro/settings/base.py
--- a/shopelectro/settings/base.py
+++ b/shopelectro/settings/base.py
@@ -216,10 +216,6 @@
     },
 }
 
-SELENIUM_URL = os.environ.get('SELENIUM_URL', 'http://selenium:4444/wd/hub')
-SELENIUM_WAIT_SECONDS = int(os.environ['SELENIUM_WAIT_SECONDS'])
-SELENIUM_TIMEOUT_SECONDS = int(os.environ['SELENIUM_TIMEOUT_SECONDS'])
-
 SITE_CREATED = datetime(2013, 1, 1)
 
 LOCALHOST = 'http://127.0.0.1:8000/'
diff --git a/shopelectro/settings/drone.py b/shopelectro/settings/drone.py
--- a/shopelectro/settings/drone.py
+++ b/shopelectro/settings/drone.py
@@ -12,4 +12,6 @@
 
 YANDEX_KASSA_LINK = 'https://demomoney.yandex.ru/eshop.xml'
 
-USE_CELERY = False
+SELENIUM_URL = os.environ.get('SELENIUM_URL', 'http://selenium:4444/wd/hub')
+SELENIUM_WAIT_SECONDS = int(os.environ['SELENIUM_WAIT_SECONDS'])
+SELENIUM_TIMEOUT_SECONDS = int(os.environ['SELENIUM_TIMEOUT_SECONDS'])


Test patch:
Repository: fidals/shopelectro

Issue:
Rm all TODO's
It's copy of https://github.com/fidals/refarm-site/issues/131

Required for pdd subtasks creating


Discussion:

Resolving patch:
diff --git a/shopelectro/management/commands/_update_catalog/update_products.py b/shopelectro/management/commands/_update_catalog/update_products.py
--- a/shopelectro/management/commands/_update_catalog/update_products.py
+++ b/shopelectro/management/commands/_update_catalog/update_products.py
@@ -323,8 +323,12 @@ def main(*args, **kwargs):
         if not any(parsed_files.values()):
             message = 'Files does not exist: {}'.format(parsed_files)
         else:
-            # TODO: happy debugging (:
-            message = 'The file structure has changed or it does not contain the required data.'
+            # file structure is unstable.
+            # You should adapt code for it if you got this error
+            message = (
+                'The file structure has changed'
+                ' or it does not contain the required data.'
+            )
 
         raise UpdateProductError(message)
 
diff --git a/shopelectro/templatetags/se_extras.py b/shopelectro/templatetags/se_extras.py
--- a/shopelectro/templatetags/se_extras.py
+++ b/shopelectro/templatetags/se_extras.py
@@ -16,14 +16,14 @@
 register = template.Library()
 
 
-# TODO - move it in catalog. Inspired by lp_electric
 @register.simple_tag
 def roots():
     return sorted(
         filter(
             lambda x: x.page.is_active,
-            Category.objects  # https://goo.gl/rFKiku
+            Category.objects
             .select_related('page')
+            # about get_cached_trees: https://goo.gl/rFKiku
             .get_cached_trees()
         ),
         key=lambda x: x.page.position,
@@ -35,7 +35,6 @@ def footer_links():
     return config.FOOTER_LINKS
 
 
-# TODO - move in pages. Inspired by LP electric
 @register.filter
 def class_name(model):
     """Return Model name."""
@@ -74,7 +73,6 @@ def is_closed(t):
             return time + call
 
 
-# TODO - move it in pages.
 @register.simple_tag
 def full_url(url_name, *args):
     return settings.BASE_URL + reverse(url_name, args=args)


Test patch:
Repository: fidals/shopelectro

Issue:
tracking.es6:30-33: Send `purchase` event to YA and GA...
The puzzle `504-30170dc3` from #504 has to be resolved:

https://github.com/fidals/shopelectro/blob/4e607fb856842b43192511b1b4eaff19d2052b9a/front/js/shared/tracking.es6#L30-L33

The puzzle was created by Artemiy on 17-Aug-18. 

Estimate: 60 minutes,     

If you have any technical questions, don't ask me, submit new tickets instead. The task will be "done" when the problem is fixed and the text of the puzzle is _removed_ from the source code. Here is more about [PDD](http://www.yegor256.com/2009/03/04/pdd.html) and [about me](http://www.yegor256.com/2017/04/05/pdd-in-action.html).


Discussion:
@artemiyds2 @duker33 please pay attention to this new issue.

Resolving patch:
diff --git a/shopelectro/views/ecommerce.py b/shopelectro/views/ecommerce.py
--- a/shopelectro/views/ecommerce.py
+++ b/shopelectro/views/ecommerce.py
@@ -1,4 +1,5 @@
 from django.conf import settings
+from django.core import serializers
 from django.http import HttpResponse, JsonResponse
 from django.shortcuts import get_object_or_404, render
 from django.views.decorators.http import require_POST
@@ -47,7 +48,20 @@ class FlushCart(ec_views.FlushCart):
 
 
 class OrderSuccess(ec_views.OrderSuccess):
-    order = Order
+    order = Order.objects.all().prefetch_related('positions')
+
+    def get_context_data(self, **kwargs):
+        context = super().get_context_data(**kwargs)
+        positions_json = serializers.serialize(
+            'json',
+            context['order'].positions.all(),
+            fields=['name', 'quantity', 'price'],
+        )
+
+        return {
+            **context,
+            'positions_json': positions_json,
+        }
 
 
 @require_POST


Test patch:
Repository: fidals/shopelectro

Issue:
Missed paid orders from Ya.Kassa
Text below is from PO

----
Мне se голову делает, что заказы оплаченные в яндексе кассе не приходят 

Вот этих я в почте не увидел 
http://prntscr.com/km0zp2

Хотя, я только что купил батарейку за 10 рублей. Я еще потестил. разные данные вбивал и с разными полями. У меня ок все.

Смотри как странно они идут в админке. видимо какой-то баг всн же есть http://prntscr.com/km124t



Discussion:
saw to admin - we have three selected orders there. Now looking to email log
log contains nothing suspicious. We should show order IDs and sums in admin.
And i'll think about email testing for a little
thought about email mech integration checks. It seems like overkill for your service.
Just improved logs and changed orders list at admin panel

Resolving patch:
diff --git a/shopelectro/admin.py b/shopelectro/admin.py
--- a/shopelectro/admin.py
+++ b/shopelectro/admin.py
@@ -238,10 +238,11 @@ class OrderAdmin(mixins.PermissionsControl):
 
     add = False
     inlines = [PositionInline]
-    list_display = ['name', 'email', 'phone', 'address', 'city', 'payment_type', 'paid']
+    list_display = ['id', 'name', 'email', 'phone', 'total_price', 'payment_type', 'paid']
     search_fields = ['name', 'email', 'phone']
     list_display_links = ['name']
 
+
 se_admin = SEAdminSite(name='se_admin')
 se_admin.register(CustomPage, models.CustomPageAdmin)
 se_admin.register(FlatPage, models.FlatPageAdmin)
diff --git a/shopelectro/views/service.py b/shopelectro/views/service.py
--- a/shopelectro/views/service.py
+++ b/shopelectro/views/service.py
@@ -1,3 +1,4 @@
+import logging
 from hashlib import md5
 
 from django.conf import settings
@@ -10,6 +11,9 @@
 
 from shopelectro.models import Order
 
+logger = logging.getLogger(__name__)
+
+
 YANDEX_REQUEST_PARAM = (
     'action', 'orderSumAmount', 'orderSumCurrencyPaycash', 'orderSumBankPaycash',
     'shopId', 'invoiceId', 'customerNumber'
@@ -88,6 +92,8 @@ def send_mail_to_customer(order):
             content_type='application/xhtml+xml'
         )
 
+    logger.info(f'received yandex aviso. Aviso data: {request.POST}')
+
     # maybe we can include django-annoying for such cases
     # https://github.com/skorokithakis/django-annoying#get_object_or_none-function
     try:


Test patch:
Repository: fidals/shopelectro

Issue:
Удали товары из фида
Из фида gm.yml (для мерчант центра) удаляй товары дешевле 200 рублей 
Условие работает только для этого фида. 


Discussion:

Resolving patch:
diff --git a/shopelectro/management/commands/price.py b/shopelectro/management/commands/price.py
--- a/shopelectro/management/commands/price.py
+++ b/shopelectro/management/commands/price.py
@@ -95,9 +95,10 @@ def filter_categories(utm):
 
             return result_categories
 
+        # @todo #533:30m Move `price.prepare_products` logic to `ProductQuerySet`
         def prepare_products(categories_, utm):
             """Filter product list and patch it for rendering."""
-            products_except_others = (
+            products_result_qs = (
                 Product.actives
                 .select_related('page')
                 .prefetch_related('category')
@@ -110,15 +111,21 @@ def prepare_products(categories_, utm):
                 Yandex Market feed requires items in some categories to have pictures
                 To simplify filtering we are excluding all products without pictures
                 """
-                products_except_others = (
-                    products_except_others
+                products_result_qs = (
+                    products_result_qs
                     .filter(page__images__isnull=False)
                     .distinct()
                 )
 
+            if utm == 'GM':
+                products_result_qs = (
+                    products_result_qs
+                    .filter(price__gt=settings.PRICE_GM_LOWER_BOUND)
+                )
+
             result_products = [
                 put_crumbs(put_utm(product))
-                for product in products_except_others
+                for product in products_result_qs
             ]
 
             return result_products
diff --git a/shopelectro/settings/base.py b/shopelectro/settings/base.py
--- a/shopelectro/settings/base.py
+++ b/shopelectro/settings/base.py
@@ -484,3 +484,6 @@ def get_robots_content():
 SLUG_HASH_SIZE = 5
 
 BRAND_TAG_GROUP_NAME = 'Производитель'
+
+# products with price lower this value should not be presented at gm.yml price
+PRICE_GM_LOWER_BOUND = 200.0


Test patch:
diff --git a/shopelectro/tests/tests_commands.py b/shopelectro/tests/tests_commands.py
--- a/shopelectro/tests/tests_commands.py
+++ b/shopelectro/tests/tests_commands.py
@@ -238,6 +238,15 @@ def test_products_in_price(self):
         products_in_price = self.get_price_offers_node('priceru.xml')
         self.assertEqual(len(products_in_price), Product.objects.count())
 
+    def test_products_in_gm_price_bounds(self):
+        """GM.yml should contain only offers with price > CONST."""
+        offers = self.get_price_offers_node('gm.yml').findall('offer')
+        prices_are_in_bounds = all(
+            float(offer.find('price').text) > settings.PRICE_GM_LOWER_BOUND
+            for offer in offers
+        )
+        self.assertTrue(prices_are_in_bounds)
+
     def test_products_in_yandex_price(self):
         products_in_yandex_price = self.get_price_offers_node('yandex.yml')
         self.assertEqual(
Repository: fidals/shopelectro

Issue:
models.py:122-123: Move Product.get_siblings method to...
The puzzle `388-509fa51e` from #388 has to be resolved:

https://github.com/fidals/shopelectro/blob/5d0c5e97622abce3a922eda62c1f38810d7da5f8/shopelectro/models.py#L122-L123

The puzzle was created by duker33 on 12-Aug-18. 

Estimate: 30 minutes,     

If you have any technical questions, don't ask me, submit new tickets instead. The task will be "done" when the problem is fixed and the text of the puzzle is _removed_ from the source code. Here is more about [PDD](http://www.yegor256.com/2009/03/04/pdd.html) and [about me](http://www.yegor256.com/2017/04/05/pdd-in-action.html).


Discussion:
@artemiyds2 @duker33 please pay attention to this new issue.

Resolving patch:
diff --git a/shopelectro/models.py b/shopelectro/models.py
--- a/shopelectro/models.py
+++ b/shopelectro/models.py
@@ -119,16 +119,6 @@ def feedback(self):
     def get_params(self):
         return Tag.objects.filter_by_products([self]).get_group_tags_pairs()
 
-    # @todo #388:30m Move Product.get_siblings method to refarm-site
-    #  And reuse it on STB.
-    def get_siblings(self, offset):
-        return (
-            self.__class__.actives
-            .filter(category=self.category)
-            .prefetch_related('category')
-            .select_related('page')[:offset]
-        )
-
     def get_brand_name(self) -> str:
         brand: Optional['Tag'] = Tag.objects.get_brands([self]).get(self)
         return brand.name if brand else ''


Test patch:
Repository: fidals/shopelectro

Issue:
Redundant tag slug postfix
At this link we can see tag slug `robiton_trabq`. But it should be `robiton`

https://www.shopelectro.ru/catalog/categories/dlia-svintsovykh-akkumuliatorov/tags/robiton_trabq/


Discussion:

Resolving patch:
diff --git a/shopelectro/views/catalog.py b/shopelectro/views/catalog.py
--- a/shopelectro/views/catalog.py
+++ b/shopelectro/views/catalog.py
@@ -242,6 +242,9 @@ def get_context_data(self, **kwargs):
             slugs = models.Tag.parse_url_tags(tags)
             tags = models.Tag.objects.filter(slug__in=slugs)
 
+            if not tags:
+                raise http.Http404('No such tag.')
+
             all_products = (
                 all_products
                 .filter(tags__in=tags)


Test patch:
diff --git a/shopelectro/tests/tests_views.py b/shopelectro/tests/tests_views.py
--- a/shopelectro/tests/tests_views.py
+++ b/shopelectro/tests/tests_views.py
@@ -211,6 +211,18 @@ def test_product_tag_linking(self):
         for link in property_links:
             self.assertContains(response, link)
 
+    def test_non_existing_tags_404(self):
+        """Product should contain links on CategoryTagPage for it's every tag."""
+        product = Product.objects.first()
+        self.assertGreater(product.tags.count(), 0)
+
+        bad_tag_url = reverse('category', kwargs={
+            'slug': product.category.page.slug,
+            'tags': 'non-existent-tag',
+        })
+        response = self.client.get(bad_tag_url)
+        self.assertEqual(response.status_code, 404)
+
 
 class CatalogPagination(BaseCatalogTestCase):