From 81f5e7445d649ead05b4d3d0a15b742444cd8b06 Mon Sep 17 00:00:00 2001 From: Radek Czajka Date: Fri, 29 Jul 2022 11:51:34 +0200 Subject: [PATCH 1/1] Tests. --- Makefile | 8 +- src/alerts/views.py | 3 - .../management/commands/refresh_covers.py | 43 ---- src/cover/models.py | 2 +- src/cover/tests.py | 223 +++++++++++++++++- src/cover/tests/angelus-novus.jpeg | Bin 0 -> 23365 bytes src/cover/tests/book.xml | 16 ++ src/cover/utils.py | 12 - src/cover/views.py | 24 +- src/documents/management/commands/fixdc.py | 52 ---- src/redakcja/settings/__init__.py | 3 + src/redakcja/settings/test.py | 13 + src/redakcja/settings/test_full.py | 5 + 13 files changed, 270 insertions(+), 134 deletions(-) delete mode 100644 src/alerts/views.py delete mode 100644 src/cover/management/commands/refresh_covers.py create mode 100644 src/cover/tests/angelus-novus.jpeg create mode 100644 src/cover/tests/book.xml delete mode 100644 src/documents/management/commands/fixdc.py create mode 100644 src/redakcja/settings/test_full.py diff --git a/Makefile b/Makefile index 304f5db7..34f331ad 100644 --- a/Makefile +++ b/Makefile @@ -10,7 +10,13 @@ deploy: src/redakcja/localsettings.py .ONESHELL: test: cd src - coverage run --branch --source='.' ./manage.py test --settings=redakcja.settings.test; true + ./manage.py test --settings=redakcja.settings.test + + +.ONESHELL: +test_full: + cd src + coverage run --branch --source='.' ./manage.py test --settings=redakcja.settings.test_full; true coverage html -d ../htmlcov.new rm -rf ../htmlcov mv ../htmlcov.new ../htmlcov diff --git a/src/alerts/views.py b/src/alerts/views.py deleted file mode 100644 index 91ea44a2..00000000 --- a/src/alerts/views.py +++ /dev/null @@ -1,3 +0,0 @@ -from django.shortcuts import render - -# Create your views here. diff --git a/src/cover/management/commands/refresh_covers.py b/src/cover/management/commands/refresh_covers.py deleted file mode 100644 index 57325633..00000000 --- a/src/cover/management/commands/refresh_covers.py +++ /dev/null @@ -1,43 +0,0 @@ -# This file is part of FNP-Redakcja, licensed under GNU Affero GPLv3 or later. -# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information. -# -import urllib2 as urllib -from django.core.files.base import ContentFile -from django.core.management import BaseCommand - -from cover.models import Image -from cover.utils import get_flickr_data, URLOpener, FlickrError - - -class Command(BaseCommand): - def add_arguments(self, parser): - parser.add_argument('--from', dest='from_id', type=int, default=1) - - def handle(self, *args, **options): - from_id = options.get('from_id', 1) - images = Image.objects.filter(id__gte=from_id).exclude(book=None).order_by('id') - images = images.filter(source_url__contains='flickr.com').exclude(download_url__endswith='_o.jpg') - for image in images: - print(image.id) - try: - flickr_data = get_flickr_data(image.source_url) - print(flickr_data) - except FlickrError as e: - print('Flickr analysis failed: %s' % e) - else: - flickr_url = flickr_data['download_url'] - if flickr_url != image.download_url: - same_url = Image.objects.filter(download_url=flickr_url) - if same_url: - print('Download url already present in image %s' % same_url.get().id) - continue - try: - t = URLOpener().open(flickr_url).read() - except urllib.URLError: - print('Broken download url') - except IOError: - print('Connection failed') - else: - image.download_url = flickr_url - image.file.save(image.file.name, ContentFile(t)) - image.save() diff --git a/src/cover/models.py b/src/cover/models.py index 04c2a6bc..87ac0369 100644 --- a/src/cover/models.py +++ b/src/cover/models.py @@ -73,7 +73,7 @@ class Image(models.Model): img, save=False ) - super().save(**kwargs) + super().save(update_fields=['use_file']) def get_absolute_url(self): return reverse('cover_image', args=[self.id]) diff --git a/src/cover/tests.py b/src/cover/tests.py index a852f25b..4755991e 100644 --- a/src/cover/tests.py +++ b/src/cover/tests.py @@ -1,17 +1,224 @@ # This file is part of FNP-Redakcja, licensed under GNU Affero GPLv3 or later. # Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information. # -from django.test import TestCase +import re +from django.core.files.uploadedfile import SimpleUploadedFile +from django.conf import settings +from django.contrib.auth.models import User +from django.test import TestCase, override_settings +from unittest import skipUnless +from unittest.mock import patch +from documents.models import Book from cover.forms import ImportForm +from cover.models import Image +IMAGE_PATH = __file__.rsplit('/', 1)[0] + '/tests/angelus-novus.jpeg' + +with open(__file__.rsplit('/', 1)[0] + '/tests/book.xml') as f: + SAMPLE_XML = f.read() + + +@skipUnless(settings.TEST_INTEGRATION, 'Skip integration tests') class FlickrTests(TestCase): + def assertEqualWithRe(self, dict1, dict2): + self.assertEqual(len(dict1), len(dict2)) + for k, v in dict2.items(): + if isinstance(v, re.Pattern): + self.assertRegex(dict1[k], v) + else: + self.assertEqual(dict1[k], v) + def test_flickr(self): - form = ImportForm({"source_url": "https://www.flickr.com/photos/rczajka/6941928577/in/photostream"}) + form = ImportForm({ + "source_url": "https://www.flickr.com/photos/rczajka/6941928577/in/photostream" + }) + self.assertTrue(form.is_valid()) + self.assertEqualWithRe( + form.cleaned_data, + { + 'source_url': "https://www.flickr.com/photos/rczajka/6941928577/", + 'author': "Radek Czajka@Flickr", + 'title': "Pirate Stańczyk", + 'license_name': "CC BY 2.0", + 'license_url': "https://creativecommons.org/licenses/by/2.0/", + 'download_url': re.compile(r'\.staticflickr\.com'), + } + ) + + def test_wikimedia_fal(self): + form = ImportForm({ + "source_url": "https://commons.wikimedia.org/wiki/File:Valdai_IverskyMon_asv2018_img47.jpg" + }) + self.assertTrue(form.is_valid()) + self.assertEqual( + form.cleaned_data, + { + 'title': 'Valdai IverskyMon asv2018 img47', + 'author': 'A.Savin', + 'source_url': 'https://commons.wikimedia.org/wiki/File:Valdai_IverskyMon_asv2018_img47.jpg', + 'download_url': 'https://upload.wikimedia.org/wikipedia/commons/4/43/Valdai_IverskyMon_asv2018_img47.jpg', + 'license_url': 'http://artlibre.org/licence/lal/en', + 'license_name': 'FAL' + } + ) + + def test_wikimedia_public_domain(self): + form = ImportForm({ + "source_url": 'https://commons.wikimedia.org/wiki/File:Pymonenko_A_boy_in_a_straw_hat.jpg' + }) self.assertTrue(form.is_valid()) - self.assertEqual(form.cleaned_data['source_url'], "https://www.flickr.com/photos/rczajka/6941928577/") - self.assertEqual(form.cleaned_data['author'], "Radek Czajka@Flickr") - self.assertEqual(form.cleaned_data['title'], u"Pirate Stańczyk") - self.assertEqual(form.cleaned_data['license_name'], "CC BY 2.0") - self.assertEqual(form.cleaned_data['license_url'], "https://creativecommons.org/licenses/by/2.0/") - self.assertTrue('.staticflickr.com' in form.cleaned_data['download_url']) + self.assertEqual( + form.cleaned_data, + { + 'title': 'Chłopiec w słomkowym kapeluszu', + 'author': 'Mykola Pymonenko', + 'source_url': 'https://commons.wikimedia.org/wiki/File:Pymonenko_A_boy_in_a_straw_hat.jpg', + 'download_url': 'https://upload.wikimedia.org/wikipedia/commons/9/9b/Pymonenko_A_boy_in_a_straw_hat.jpg', + 'license_url': 'https://pl.wikipedia.org/wiki/Domena_publiczna', + 'license_name': 'domena publiczna' + } + ) + + def test_mnw(self): + form = ImportForm({ + "source_url": 'https://cyfrowe.mnw.art.pl/pl/katalog/511078' + }) + self.assertTrue(form.is_valid()) + self.assertEqualWithRe( + form.cleaned_data, + { + 'title': 'Chłopka (Baba ukraińska)', + 'author': 'Krzyżanowski, Konrad (1872-1922)', + 'source_url': 'https://cyfrowe.mnw.art.pl/pl/katalog/511078', + 'download_url': re.compile(r'https://cyfrowe-cdn\.mnw\.art\.pl/.*\.jpg'), + 'license_url': 'https://pl.wikipedia.org/wiki/Domena_publiczna', + 'license_name': 'domena publiczna' + } + ) + + def test_quick_import(self): + user = User.objects.create(username='test', is_superuser=True) + self.client.force_login(user) + + book = Book.create(slug='test', text=SAMPLE_XML, creator=user) + + self.client.post( + '/cover/quick-import/1/', + { + 'url': 'https://cyfrowe.mnw.art.pl/pl/katalog/511078' + } + ) + + self.assertEqual(Image.objects.all().count(), 1) + self.assertEqual(book[0].history().count(), 2) + self.assertIn( + 'Chłopka (Baba ukraińska), Krzyżanowski, Konrad (1872-1922), domena publiczna', + book.materialize() + ) + + +class CoverPreviewTest(TestCase): + @classmethod + def setUpTestData(cls): + cls.book = Book.create(slug='test', text='', creator=None) + + def test_preview_from_bad_xml(self): + response = self.client.post('/cover/preview/', data={"xml": ''}) + self.assertEqual(response.status_code, 200) + self.assertEqual(response.content, b'/media/static/img/sample_cover.png') + + @patch('cover.views.make_cover') + def test_preview_from_minimal_xml(self, make_cover): + response = self.client.post('/cover/preview/', data={"xml": SAMPLE_XML}) + self.assertEqual(response.status_code, 200) + self.assertIn(b'/media/dynamic/cover/preview/', response.content) + + def test_bad_book(self): + response = self.client.get('/cover/preview/test/') + self.assertEqual(response.status_code, 302) + + @patch('cover.views.make_cover') + def test_good_book(self, make_cover): + self.book[0].commit(text=SAMPLE_XML) + + response = self.client.get('/cover/preview/test/1/3/') + self.assertEqual(response.status_code, 404) + + response = self.client.get('/cover/preview/test/1/') + self.assertEqual(response.status_code, 200) + self.assertNotIn('Content-Disposition', response) + + response = self.client.get('/cover/preview/test/1/2/?download&width=100') + self.assertEqual(response.status_code, 200) + self.assertIn('attachment', response['Content-Disposition']) + + +class TestAddCover(TestCase): + @classmethod + def setUpTestData(cls): + cls.user = User.objects.create(username='test', is_superuser=True) + + def test_add_image(self): + self.client.force_login(self.user) + + response = self.client.get('/cover/add_image/') + self.assertEqual(response.status_code, 200) + + with open(IMAGE_PATH, 'rb') as image_file: + response = self.client.post( + '/cover/add_image/', + data={ + 'title': 'Angelus Novus', + 'author': 'Paul Klee', + 'license_name': 'domena publiczna', + 'license_url': '', + 'file': image_file, + } + ) + self.assertEqual(Image.objects.all().count(), 1) + +class TestCover(TestCase): + @classmethod + def setUpTestData(cls): + cls.user = User.objects.create(username='test', is_superuser=True) + with open(IMAGE_PATH, 'rb') as f: + cls.img = Image.objects.create( + title='Angelus Novus', + author='Paul Klee', + license_name='domena publiczna', + license_url='', + file=SimpleUploadedFile( + 'angelus-novus.jpg', + f.read(), + content_type='image/jpeg' + ) + ) + + def test_image_list(self): + response = self.client.get('/cover/image/') + self.assertEqual(len(response.context['object_list']), 1) + + def test_image(self): + response = self.client.get('/cover/image/1/') + self.assertEqual(response.context['object'].title, 'Angelus Novus') + + def test_edit_image(self): + self.client.force_login(self.user) + response = self.client.post('/cover/image/1/', { + 'author': 'author', + 'title': 'changed title', + 'license_name': 'domena', + 'cut_top': 1, + 'cut_bottom': 1, + 'cut_left': 1, + 'cut_right': 1, + }) + + response = self.client.get('/cover/image/1/') + self.assertEqual(response.context['object'].title, 'changed title') + + + def test_image_file(self): + response = self.client.get('/cover/image/1/file/') + self.assertRegex(response['Location'], r'^/media/dynamic/cover/image/') diff --git a/src/cover/tests/angelus-novus.jpeg b/src/cover/tests/angelus-novus.jpeg new file mode 100644 index 0000000000000000000000000000000000000000..fd0394fb342880b9fef27f1ad6d2055b06061ae2 GIT binary patch literal 23365 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 literal 0 HcmV?d00001 diff --git a/src/cover/tests/book.xml b/src/cover/tests/book.xml new file mode 100644 index 00000000..acdba432 --- /dev/null +++ b/src/cover/tests/book.xml @@ -0,0 +1,16 @@ + + + + Tytuł utworu + Utworu, Autor + 2022-07-26 + Wolne Lektury + pol + slug + domena publiczna + + + + diff --git a/src/cover/utils.py b/src/cover/utils.py index fcda91fd..56fb24a7 100644 --- a/src/cover/utils.py +++ b/src/cover/utils.py @@ -76,14 +76,6 @@ def get_flickr_data(url): def get_wikimedia_data(url): - """ - >>> get_wikimedia_data('https://commons.wikimedia.org/wiki/File:Valdai_IverskyMon_asv2018_img47.jpg') - {'title': 'Valdai IverskyMon asv2018 img47', 'author': 'A.Savin', 'source_url': 'https://commons.wikimedia.org/wiki/File:Valdai_IverskyMon_asv2018_img47.jpg', 'download_url': 'https://upload.wikimedia.org/wikipedia/commons/4/43/Valdai_IverskyMon_asv2018_img47.jpg', 'license_url': 'http://artlibre.org/licence/lal/en', 'license_name': 'FAL'} - - >>> get_wikimedia_data('https://commons.wikimedia.org/wiki/File:Pymonenko_A_boy_in_a_straw_hat.jpg') - {'title': 'Chłopiec w słomkowym kapeluszu', 'author': 'Mykola Pymonenko', 'source_url': 'https://commons.wikimedia.org/wiki/File:Pymonenko_A_boy_in_a_straw_hat.jpg', 'download_url': 'https://upload.wikimedia.org/wikipedia/commons/9/9b/Pymonenko_A_boy_in_a_straw_hat.jpg', 'license_url': 'https://pl.wikipedia.org/wiki/Domena_publiczna', 'license_name': 'domena publiczna'} - - """ file_name = url.rsplit('/', 1)[-1].rsplit(':', 1)[-1] d = json.loads(URLOpener().open('https://commons.wikimedia.org/w/api.php?action=query&titles=File:{}&prop=imageinfo&iiprop=url|user|extmetadata&iimetadataversion=latest&format=json'.format(file_name)).read().decode('utf-8')) @@ -118,10 +110,6 @@ def get_wikimedia_data(url): def get_mnw_data(url): - """ - >>> get_mnw_data('https://cyfrowe.mnw.art.pl/pl/katalog/511078') - {'title': 'Chłopka (Baba ukraińska)', 'author': 'Krzyżanowski, Konrad (1872-1922)', 'source_url': 'https://cyfrowe.mnw.art.pl/pl/katalog/511078', 'download_url': 'https://cyfrowe-cdn.mnw.art.pl/upload/multimedia/a0/68/a0681c60f203d907d9c45050d245c921.jpg', 'license_url': 'https://pl.wikipedia.org/wiki/Domena_publiczna', 'license_name': 'domena publiczna'} - """ nr = url.rsplit('/', 1)[-1] d = list( csv.DictReader( diff --git a/src/cover/views.py b/src/cover/views.py index 660909d5..638f1c06 100644 --- a/src/cover/views.py +++ b/src/cover/views.py @@ -1,7 +1,10 @@ # This file is part of FNP-Redakcja, licensed under GNU Affero GPLv3 or later. # Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information. # +from hashlib import sha1 +from os import makedirs import os.path +import PIL.Image from django.conf import settings from django.contrib.auth.decorators import permission_required from django.http import HttpResponse, HttpResponseRedirect, Http404 @@ -10,6 +13,8 @@ from django.views.decorators.csrf import csrf_exempt from django.views.decorators.http import require_POST from lxml import etree from librarian import RDFNS, DCNS +from librarian.cover import make_cover +from librarian.dcparser import BookInfo from documents.helpers import active_tab from documents.models import Book, Chunk from cover.models import Image @@ -26,10 +31,6 @@ def preview(request, book, chunk=None, rev=None): If chunk and rev number are given, use version from given revision. If rev is not given, use publishable version. """ - from PIL import Image - from librarian.cover import make_cover - from librarian.dcparser import BookInfo - chunk = Chunk.get(book, chunk) if rev is not None: try: @@ -44,7 +45,8 @@ def preview(request, book, chunk=None, rev=None): try: info = BookInfo.from_bytes(xml) - except: + except Exception as e: + print(e) return HttpResponseRedirect(os.path.join(settings.STATIC_URL, "img/sample_cover.png")) width = request.GET.get('width') width = int(width) if width else None @@ -70,17 +72,11 @@ def preview(request, book, chunk=None, rev=None): @csrf_exempt @require_POST def preview_from_xml(request): - from hashlib import sha1 - from PIL import Image - from os import makedirs - from lxml import etree - from librarian.cover import make_cover - from librarian.dcparser import BookInfo - xml = request.POST['xml'] try: info = BookInfo.from_bytes(xml.encode('utf-8')) - except: + except Exception as e: + print(e) return HttpResponse(os.path.join(settings.STATIC_URL, "img/sample_cover.png")) coverid = sha1(etree.tostring(info.to_etree())).hexdigest() cover = make_cover(info) @@ -91,7 +87,7 @@ def preview_from_xml(request): except OSError: pass fname = os.path.join(cover_dir, "%s.%s" % (coverid, cover.ext())) - img = cover.image().resize(PREVIEW_SIZE, Image.ANTIALIAS) + img = cover.image().resize(PREVIEW_SIZE, PIL.Image.ANTIALIAS) img.save(os.path.join(settings.MEDIA_ROOT, fname)) return HttpResponse(os.path.join(settings.MEDIA_URL, fname)) diff --git a/src/documents/management/commands/fixdc.py b/src/documents/management/commands/fixdc.py deleted file mode 100644 index 3f4a848e..00000000 --- a/src/documents/management/commands/fixdc.py +++ /dev/null @@ -1,52 +0,0 @@ -# This file is part of FNP-Redakcja, licensed under GNU Affero GPLv3 or later. -# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information. -# -from librarian import RDFNS, WLURI, ValidationError -from librarian.dcparser import BookInfo -from documents.management import XmlUpdater -from documents.management.commands import XmlUpdaterCommand - - -class FixDC(XmlUpdater): - commit_desc = "auto-fixing DC" - retain_publishable = True - only_first_chunk = True - - def fix_wluri(elem, change, verbose): - try: - WLURI.strict(elem.text) - except ValidationError: - correct_field = str(WLURI.from_slug( - WLURI(elem.text.strip()).slug)) - try: - WLURI.strict(correct_field) - except ValidationError: - # Can't make a valid WLURI out of it, leave as is. - return False - if verbose: - print("Changing %s from %s to %s" % ( - elem.tag, elem.text, correct_field - )) - elem.text = correct_field - return True - for field in BookInfo.FIELDS: - if field.validator == WLURI: - XmlUpdater.fixes_elements('.//' + field.uri)(fix_wluri) - - @XmlUpdater.fixes_elements(".//" + RDFNS("Description")) - def fix_rdfabout(elem, change, verbose): - correct_about = change.tree.book.correct_about() - attr_name = RDFNS("about") - current_about = elem.get(attr_name) - if current_about != correct_about: - if verbose: - print("Changing rdf:about from %s to %s" % ( - current_about, correct_about - )) - elem.set(attr_name, correct_about) - return True - - -class Command(XmlUpdaterCommand): - updater = FixDC - help = 'Fixes obvious errors in DC: rdf:about and WLURI format.' diff --git a/src/redakcja/settings/__init__.py b/src/redakcja/settings/__init__.py index 1325743e..3286f073 100644 --- a/src/redakcja/settings/__init__.py +++ b/src/redakcja/settings/__init__.py @@ -290,6 +290,9 @@ REST_FRAMEWORK = { } +TEST_INTEGRATION = False + + try: SENTRY_DSN except NameError: diff --git a/src/redakcja/settings/test.py b/src/redakcja/settings/test.py index 4a80142f..dba64b6b 100644 --- a/src/redakcja/settings/test.py +++ b/src/redakcja/settings/test.py @@ -26,3 +26,16 @@ SECRET_KEY = "not-so-secret" LITERARY_DIRECTOR_USERNAME = 'Kaowiec' + +MIN_COVER_SIZE = (1, 1) + + +class DisableMigrations(object): + + def __contains__(self, item): + return True + + def __getitem__(self, item): + return None + +MIGRATION_MODULES = DisableMigrations() diff --git a/src/redakcja/settings/test_full.py b/src/redakcja/settings/test_full.py new file mode 100644 index 00000000..90d243d3 --- /dev/null +++ b/src/redakcja/settings/test_full.py @@ -0,0 +1,5 @@ +from .test import * + +TEST_INTEGRATION = True + +MIGRATION_MODULES = {} -- 2.20.1