Files
Kikomiko/kikomiko/admin/test_catalog_roundtrip.py
T

116 lines
6.1 KiB
Python

"""Run: python3 -m unittest -v test_catalog_roundtrip.py. Uses disposable DBs only."""
import copy
import json
import tempfile
import unittest
from pathlib import Path
import server
from import_catalog import import_categories
class CatalogTests(unittest.TestCase):
def setUp(self):
self.tmp = tempfile.TemporaryDirectory()
server.DATA_DIR = Path(self.tmp.name)
server.DB_PATH = server.DATA_DIR / 'test.sqlite'
server.DIST_CATALOG = server.DATA_DIR / 'public.json'
self.admin = server.ensure_admin_user
server.ensure_admin_user = lambda: None
server.init_db()
self.payload = {'metadata':{'eurToTomanRate':302700}, 'images':[{'id':'photo','original_url':'/i/photo','local_path':'media/photo.webp'}],
'categories':[{'slug':'lip-gloss','name':'Lip Gloss','nameFa':'لیپ گلاس','parentSlug':'lips'}],
'products':[{'id':'parent','slug':'lip-gloss','name':'Official English Name','nameFa':'لیپ گلاس کیکو',
'description':'توضیحات فارسی محصول', 'seoTitle':'عنوان سئو', 'seoDescription':'متای فارسی',
'details':[{'key':'finish','label':'فینیش','value':'براق'}],
'blocks':[{'type':'SPECIFICATIONS','content':{'custom':'preserved'}}],
'variants':[{'id':'shade01','slug':'lip-gloss-01','name':'Official English Name 01','colorCode':'01','colorName':'Rose',
'description':'توضیحات رنگ اول','seoTitle':'سئو رنگ اول','seoDescription':'متای رنگ اول',
'details':[{'key':'finish','label':'فینیش','value':'مات'}],
'price':123456,'stock':3,'images':['/media/a.webp','/media/b.webp'],'heroImage':'/media/a.webp'},
{'id':'shade02','slug':'lip-gloss-02','name':'Official English Name 02','shade':'02 Red',
'description':'توضیحات رنگ دوم','details':{'coverage':'full'},'price':234567,'stock':0}]}]}
def tearDown(self):
server.ensure_admin_user = self.admin
self.tmp.cleanup()
def read(self):
with server.db() as conn:
return server.product_dict(conn.execute('SELECT * FROM products').fetchone())
def test_roundtrip_and_public_export(self):
import_categories(self.payload)
result = server.import_products_payload(self.payload)
self.assertEqual(result['created'],1)
self.assertEqual(result['errors'],[])
first = self.read()
self.assertEqual(len(first['colorVariants']),2)
self.assertEqual(first['stock'],3)
for key in ('description','seoTitle','seoDescription','details','blocks'):
self.assertEqual(first[key],self.payload['products'][0][key])
for actual,expected in zip(first['colorVariants'],self.payload['products'][0]['variants']):
for key,value in expected.items(): self.assertEqual(actual[key],value)
server.export_catalog()
public=json.loads(server.DIST_CATALOG.read_text())
self.assertEqual(public['products'][0],first)
dest=server.DATA_DIR/'backup.json'
server.export_import_bundle(dest)
bundle=json.loads(dest.read_text())
server.DB_PATH=server.DATA_DIR/'second.sqlite'
server.init_db()
import_categories(bundle)
server.import_products_payload(bundle)
self.assertEqual(self.read(),first)
with server.db() as conn:
category=conn.execute('SELECT * FROM categories').fetchone()
self.assertEqual(category['slug'],'lip-gloss')
self.assertEqual(category['name_fa'],'لیپ گلاس')
def test_reimport_idempotent_and_partial_parent(self):
server.import_products_payload(self.payload)
self.assertEqual(server.import_products_payload(self.payload)['updated'],1)
server.import_products_payload({'products':[{'id':'parent','name':'Updated','slug':'lip-gloss'}]})
self.assertEqual(self.read()['description'],'توضیحات فارسی محصول')
self.assertEqual(len(self.read()['colorVariants']),2)
with server.db() as conn:self.assertEqual(conn.execute('SELECT COUNT(*) FROM products').fetchone()[0],1)
def test_invalid_variants_do_not_replace_existing(self):
server.import_products_payload(self.payload)
before=self.read()
bad=copy.deepcopy(self.payload)
bad['products'][0]['variants'][1]['id']='shade01'
self.assertEqual(server.import_products_payload(bad)['skipped'],1)
self.assertEqual(self.read(),before)
bad['products'][0]['variants'][1]['id']='shade02'
bad['products'][0]['variants'][0]['price']=float('nan')
self.assertEqual(server.import_products_payload(bad)['skipped'],1)
self.assertEqual(self.read(),before)
def test_inactive_retained_in_backup_not_public(self):
self.payload['products'][0]['active']=False
server.import_products_payload(self.payload)
server.export_catalog()
self.assertEqual(json.loads(server.DIST_CATALOG.read_text())['products'],[])
dest=server.DATA_DIR/'backup.json'
server.export_import_bundle(dest)
self.assertEqual(len(json.loads(dest.read_text())['products']),1)
def test_old_database_migration(self):
# Recreate the original product table by dropping only the newly added column.
with server.db() as conn:conn.execute('ALTER TABLE products DROP COLUMN content_json')
server.init_db()
server.init_db()
self.assertEqual(server.import_products_payload(self.payload)['created'],1)
def test_image_batches_merge(self):
server.import_products_payload(self.payload)
server.import_products_payload({'metadata':{'newKey':'x'},'images':[{'id':'another','original_url':'/i/another'}],'products':[]})
dest=server.DATA_DIR/'backup.json'
server.export_import_bundle(dest)
data=json.loads(dest.read_text())
self.assertEqual(len(data['images']),2)
self.assertEqual(data['metadata']['eurToTomanRate'],302700)
if __name__=='__main__': unittest.main()