Repository: fidals/shopelectro

Issue:
500 ошибка в админке
https://www.shopelectro.ru/admin/shopelectro/productpage/?has_category=no


Discussion:
found this lines in failed admin traceback from prod:
```
se-python                |   File "/usr/app/src/shopelectro/admin.py", line 163, in model_id
se-python                |     return obj.model.vendor_code
```

Right now we have 65 not active product pages with no binded products. Seems it's because of them.

I suppose, that they appeared from `update_db` command. We can't test it now with TDD
Analyzed bad pages set. They are old, but already bad copies of existing and active products:
```
In [1]:  pp = ProductPage.objects.filter(shopelectro_product=None).first()

In [2]: p = Product.objects.get(name=pp.name)

In [3]: pp.id, p.page.id
Out[3]: (8344, 8379)

In [4]: pp.is_active, p.page.is_active
Out[4]: (False, True)

In [5]: pp.date_published, p.page.date_published
Out[6]: (datetime.date(2018, 7, 31), datetime.date(2018, 7, 31))
```

I'll just rm all such bad pages from prod db. But the problem is to define where this pages came from.
I'll write test that update product with commands update_db mech. I'll try to catch bad pages copies creating

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
@@ -236,6 +236,11 @@ def report(recipients=None, message=None):
 
 @transaction.atomic
 def delete(data: Dict[UUID, Data]):
+    """
+    Deactivate stale pages.
+
+    Deactivate all pages that are still in db, but already not in `data`.
+    """
     uuids = list(data)
     pages_to_deactivate = ProductPage.objects.exclude(
         shopelectro_product__uuid__in=uuids)


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
@@ -19,7 +19,7 @@
 from shopelectro.management.commands._update_catalog import (
     update_products, update_tags
 )
-from shopelectro.models import Category, Product, Tag, TagGroup
+from shopelectro.models import Category, Product, ProductPage, Tag, TagGroup
 
 """
 @todo #179 Раздели тесты класса UpdateProducts на интеграционные и модульные.
@@ -37,6 +37,39 @@ def get_tag_as_dict(group: str, tag: str):
     }
 
 
+@tag('fast')
+class UpdateProductsUnit(TestCase):
+    """Unit tests, but not integration."""
+
+    fixtures = ['dump.json']
+
+    def test_product_delete(self):
+        """Function called "delete" should not delete, but should deactivate product page."""
+        product = Product.objects.first()
+        # - delete all products. Our product is among them
+        update_products.delete(data={})
+        self.assertFalse(product.page.is_active)
+
+    # @todo #656:30m Fix pages consistency in update_db command.
+    #  See test below for details.
+    @unittest.expectedFailure
+    def test_product_pages_consistency(self):
+        """Full import cycle with delete/update/create should keep db pages consistency."""
+        # - take some existing prod
+        product = Product.objects.first()
+        product_data = {str(product.uuid): {'name': product.name}}
+        # - delete all products. Our product is among them
+        update_products.delete(data={})
+        # - set our product to db again
+        updated_products = update_products.update(product_data)
+        update_products.create(product_data, updated_products)
+        # - assert if product's page is unique by name
+        self.assertEqual(ProductPage.objects.filter(name=product.name).count(), 1)
+        old_named_pages = ProductPage.objects.filter(name=product.name)
+        # - and this unique page should be active
+        self.assertTrue(old_named_pages.first().is_active)
+
+
 # @todo #603:30m Resurrect update_catalog tests.
 #  Now we have problems with files downloading.
 @tag('slow')
@@ -86,7 +119,8 @@ def test_update_products(self):
         self.assertEqual(len(updated_products), update_products_count)
         self.assertTrue(all(
             product.price == new_data['price']
-            for product in updated_products))
+            for product in updated_products
+        ))
 
     def test_create_products(self):
         create_count = 10