import base64 import datetime import json import os import shutil import sys import tempfile import pytest from django.contrib.auth.models import Group from django.core.files import File from django.core.management import call_command from django.utils.encoding import force_bytes, force_text from django.utils.six import BytesIO, StringIO from combo.apps.assets.models import Asset from combo.apps.gallery.models import Image, GalleryCell from combo.apps.maps.models import MapLayer, Map from combo.apps.pwa.models import PwaSettings, PwaNavigationEntry from combo.data.models import Page, TextCell from combo.data.utils import export_site, import_site, MissingGroups pytestmark = pytest.mark.django_db @pytest.fixture def some_data(): page = Page(title='One', slug='one') page.save() page = Page(title='Two', slug='two') page.save() page = Page(title='Three', slug='three') page.save() cell = TextCell(page=page, order=0, text='hello world', placeholder='content') cell.save() @pytest.fixture def some_map_layers(): MapLayer(label='Foo', slug='foo', geojson_url='http://example.net/foo/').save() MapLayer(label='Bar', slug='bar', geojson_url='http://example.net/bar/').save() @pytest.fixture def some_assets(): Asset(key='banner', asset=File(BytesIO(b'test'), 'test.png')).save() Asset(key='favicon', asset=File(BytesIO(b'test2'), 'test2.png')).save() def get_output_of_command(command, *args, **kwargs): old_stdout = sys.stdout output = sys.stdout = StringIO() call_command(command, *args, **kwargs) sys.stdout = old_stdout return output.getvalue() def test_import_export(app, some_data): output = get_output_of_command('export_site') assert len(json.loads(output)['pages']) == 3 import_site(data={}, clean=True) assert Page.objects.all().count() == 0 assert TextCell.objects.all().count() == 0 empty_output = get_output_of_command('export_site') assert len(json.loads(empty_output)['pages']) == 0 Page(title='test', slug='test').save() old_stdin = sys.stdin sys.stdin = StringIO(json.dumps({})) assert Page.objects.count() == 1 try: call_command('import_site', '-', clean=True) finally: sys.stdin = old_stdin assert Page.objects.count() == 0 with tempfile.NamedTemporaryFile() as f: f.write(force_bytes(output)) f.flush() call_command('import_site', f.name) assert Page.objects.count() == 3 assert TextCell.objects.all().count() == 1 import_site(data={}, if_empty=True) assert Page.objects.count() == 3 assert TextCell.objects.all().count() == 1 import_site(data=[], clean=True) tempdir = tempfile.mkdtemp('chrono-test') empty_output = get_output_of_command('export_site', output=os.path.join(tempdir, 't.json')) assert os.path.exists(os.path.join(tempdir, 't.json')) shutil.rmtree(tempdir) def test_backward_compatibility_import(app, some_data): old_export = Page.export_all_for_json() Page.objects.all().delete() import_site(data=old_export) assert Page.objects.count() == 3 def test_import_export_map_layers(app, some_map_layers): output = get_output_of_command('export_site') assert len(json.loads(output)['map-layers']) == 2 import_site(data={}, clean=True) assert MapLayer.objects.all().count() == 0 empty_output = get_output_of_command('export_site') assert len(json.loads(empty_output)['map-layers']) == 0 MapLayer(label='Baz', slug='baz', geojson_url='http://example.net/baz/').save() old_stdin = sys.stdin sys.stdin = StringIO(json.dumps({})) assert MapLayer.objects.count() == 1 try: call_command('import_site', '-', clean=True) finally: sys.stdin = old_stdin assert MapLayer.objects.count() == 0 with tempfile.NamedTemporaryFile() as f: f.write(force_bytes(output)) f.flush() call_command('import_site', f.name) assert MapLayer.objects.count() == 2 import_site(data={}, if_empty=True) assert MapLayer.objects.count() == 2 def test_import_export_map_cells(app, some_data, some_map_layers): page = Page.objects.get(slug='one') cell = Map(page=page, order=0, placeholder='content') cell.save() cell.layers.add(MapLayer.objects.get(slug='foo')) cell.save() site_export = get_output_of_command('export_site') import_site(data={}, clean=True) assert Map.objects.count() == 0 import_site(data=json.loads(site_export), clean=True) assert Map.objects.count() == 1 assert Map.objects.all()[0].layers.all()[0].slug == 'foo' def test_group_restrictions_import_export(app, some_data): group = Group(name='A Group') group.save() page = Page.objects.get(slug='one') page.groups = [group] page.save() cell = TextCell.objects.get(order=0) cell.groups = [group] cell.save() output = get_output_of_command('export_site') assert len(json.loads(output)['pages']) == 3 import_site(data={}, clean=True) assert Page.objects.all().count() == 0 assert TextCell.objects.all().count() == 0 Group.objects.all().delete() with pytest.raises(MissingGroups) as excinfo: import_site(json.loads(output), clean=True) assert excinfo.value.names == ['A Group'] group = Group(name='A Group') group.save() import_site(json.loads(output), clean=True) assert Page.objects.all().count() == 3 assert TextCell.objects.all().count() == 1 page = Page.objects.get(slug='one') assert [x.name for x in page.groups.all()] == ['A Group'] cell = TextCell.objects.get(order=0) assert [x.name for x in cell.groups.all()] == ['A Group'] def test_import_export_assets(app, some_assets): output = get_output_of_command('export_site') assert len(json.loads(output)['assets']) == 2 import_site(data={}, clean=True) assert Asset.objects.all().count() == 0 empty_output = get_output_of_command('export_site') assert len(json.loads(empty_output)['assets']) == 0 Asset(key='footer', asset=File(StringIO('test3'), 'test3.png')).save() old_stdin = sys.stdin sys.stdin = StringIO(json.dumps({})) assert Asset.objects.count() == 1 try: call_command('import_site', '-', clean=True) finally: sys.stdin = old_stdin assert Asset.objects.count() == 0 with tempfile.NamedTemporaryFile() as f: f.write(force_bytes(output)) f.flush() call_command('import_site', f.name) assert Asset.objects.count() == 2 import_site(data={}, if_empty=True) assert Asset.objects.count() == 2 def test_import_export_pwa_settings(app): output = get_output_of_command('export_site') pwa_settings = PwaSettings.singleton() pwa_settings.offline_text = 'Hello world' pwa_settings.offline_retry_button = False pwa_settings.save() output = get_output_of_command('export_site') import_site(data={}, clean=True) assert PwaSettings.objects.all().count() == 0 import_site(data=json.loads(output)) assert PwaSettings.singleton().offline_retry_button is False assert PwaSettings.singleton().offline_text == 'Hello world' def test_import_export_pwa_navigation(app, some_data): page = Page.objects.get(slug='one') entry1 = PwaNavigationEntry(label='a', url='/', order=0) entry2 = PwaNavigationEntry(link_page=page, order=1, icon=File(BytesIO(b'te\30st'), 'test.png')) entry1.save() entry2.save() output = get_output_of_command('export_site') import_site(data={}, clean=True) assert PwaNavigationEntry.objects.all().count() == 0 import_site(data=json.loads(output)) assert PwaNavigationEntry.objects.all().count() == 2 # check identical file was not touched assert os.path.basename(PwaNavigationEntry.objects.get(order=1).icon.file.name) == 'test.png' assert PwaNavigationEntry.objects.get(order=1).icon.read() == b'te\30st' # check a second import doesn't create additional entries import_site(data=json.loads(output)) assert PwaNavigationEntry.objects.all().count() == 2 # check with a change in icon file content data = json.loads(output) data['pwa']['navigation'][1]['icon:base64'] = force_text(base64.encodestring(b'TEST')) import_site(data=data) assert PwaNavigationEntry.objects.all().count() == 2 assert PwaNavigationEntry.objects.get(order=1).icon.read() == b'TEST' # check with a change in icon file name data = json.loads(output) data['pwa']['navigation'][1]['fields']['icon'] = 'pwa/test2.png' data['pwa']['navigation'][1]['icon:base64'] = force_text(base64.encodestring(b'TEST2')) import_site(data=data) assert PwaNavigationEntry.objects.all().count() == 2 assert os.path.basename(PwaNavigationEntry.objects.get(order=1).icon.file.name) == 'test2.png' assert PwaNavigationEntry.objects.get(order=1).icon.read() == b'TEST2' def test_import_export_gallery_images(app, some_data): page = Page.objects.get(slug='one') gallery = GalleryCell(page=page, order=2, placeholder='images') gallery.save() image1 = Image(gallery=gallery, image='path/foo.jpg', title='foo', order=1) image2 = Image(gallery=gallery, image='path/bar.jpg', title='bar', order=2) image1.save() image2.save() output = get_output_of_command('export_site') import_site(data={}, clean=True) assert Image.objects.all().count() == 0 import_site(data=json.loads(output)) assert Image.objects.all().count() == 2 assert image1.title == 'foo' assert image1.image == 'path/foo.jpg'