from django.conf import settings from django.contrib.admin.sites import AdminSite from django.contrib.contenttypes.models import ContentType from django.http import HttpRequest from django.test import TestCase, Client, override_settings from django.urls import reverse from galerie.models import Galerie, Soubor from soustredeni.models import Soustredeni import django.contrib.auth.models as auth import personalni.models as pers import tvorba.models as tv import galerie.admin as g_adm from contextlib import ExitStack from pathlib import Path import shutil import tempfile # NOTE: Existují metody, jak si pořídit kontext: https://docs.python.org/3/library/unittest.html#unittest.TestCase.enterClassContext # Nepoužíváme je, protože jsem nepřišlo na to, jak je pak použít v `@override_settings(MEDIA_ROOT=media_dir)` # Ref: https://stackoverflow.com/a/2269295 tempdir = tempfile.TemporaryDirectory(prefix='mamweb_test') media_dir = Path(tempdir.name) / 'media' class GalerieTests(TestCase): @classmethod def setUpTestData(cls): org_perm = auth.Permission.objects.get(content_type=ContentType.objects.get_for_model(auth.User), codename='org') resitel_perm = auth.Permission.objects.get(content_type=ContentType.objects.get_for_model(auth.User), codename='resitel') r = tv.Rocnik.objects.create(rocnik=42, prvni_rok=1970) cls.sous_a = Soustredeni.objects.create(rocnik=r) cls.sous_b = Soustredeni.objects.create(rocnik=r) cls.neucastnik = pers.Resitel.objects.create(osoba=pers.Osoba.objects.create(user=auth.User.objects.create_user(username='ne'))) cls.neucastnik.osoba.user.user_permissions.add(resitel_perm) cls.ucastnik_a = pers.Resitel.objects.create(osoba=pers.Osoba.objects.create(user=auth.User.objects.create_user(username='ua'))) cls.ucastnik_a.osoba.user.user_permissions.add(resitel_perm) cls.sous_a.ucastnici.add(cls.ucastnik_a) cls.ucastnik_b = pers.Resitel.objects.create(osoba=pers.Osoba.objects.create(user=auth.User.objects.create_user(username='ub'))) cls.ucastnik_b.osoba.user.user_permissions.add(resitel_perm) cls.sous_b.ucastnici.add(cls.ucastnik_b) cls.org = pers.Organizator.objects.create(osoba=pers.Osoba.objects.create(user=auth.User.objects.create_user(username='o', is_staff=True))) cls.org.osoba.user.user_permissions.add(org_perm) def setUp(self): media_dir.mkdir() def tearDown(self): shutil.rmtree(media_dir) @classmethod def tearDownClass(cls): super().tearDownClass() tempdir.cleanup() # TODO: tohle není moc unit test. Správnější by bylo mít polopřipravené # věci (sous s galerií, sous bez galerie, sous se špatně uspořádanými # galeriemi ap.) a dedikované testy na jednotlivé úkony. Ale teď jsem to # začalo psát takhle a chci to nejdřív dopsat a commitnout, takže pokud to # zapomenu rozstřelit na menší, tak to takhle zůstane… @override_settings(MEDIA_ROOT=media_dir) def test_vyrobeni_galerii_k_sousu(self): """Vyrobí strukturu galerií odpovídající sousové běžné struktuře pomocí příslušných views""" org_client = Client() org_client.force_login(self.org.osoba.user) resp = org_client.get(reverse('galerie_nova', kwargs={'soustredeni': self.sous_a.id, 'galerie': 0})) self.assertEqual(resp.status_code, 200) tf = Path(settings.BASE_DIR) / 'galerie' / 'testfiles' filenames_pa = [ tf / 'kod_emoji.jpg', tf / 'metal.jpg', tf / 'ohno_small.jpg', ] filenames_so = [ tf / 'pf2020_small.jpg', ] resp = org_client.post( reverse('galerie_nova', kwargs={'soustredeni': self.sous_a.id, 'galerie': 0}), data={'nazev': 'Top galerie k sousu A'}, follow=True, ) self.assertEqual(resp.status_code, 200) # Teď je kind-of těžké kliknout na „přidat novou podgalerii“, protože ten odkaz je někde v HTML vyrenderovaný templatem. # Ale můžeme vytáhnout pk galerie z kontextu top_gal = resp.context['galerie'] self.assertIsInstance(top_gal, Galerie) # Záměrně vyrobíme nejdřív sobotní a pak páteční galerii, ať je pak můžeme prohodit with ExitStack() as stack: files = [stack.enter_context(open(fn, 'rb')) for fn in filenames_so] resp = org_client.post( reverse('galerie_nova', kwargs={'soustredeni': self.sous_a.id, 'galerie': top_gal.pk}), data={'nazev': 'Sobota', 'obr': files}, follow=True, ) self.assertEqual(resp.status_code, 200) so_gal = resp.context['galerie'] self.assertIsInstance(so_gal, Galerie) with ExitStack() as stack: files = [stack.enter_context(open(fn, 'rb')) for fn in filenames_pa] resp = org_client.post( reverse('galerie_nova', kwargs={'soustredeni': self.sous_a.id, 'galerie': top_gal.pk}), data={'nazev': 'Pátek', 'obr': files}, follow=True, ) self.assertEqual(resp.status_code, 200) pa_gal = resp.context['galerie'] self.assertIsInstance(pa_gal, Galerie) self.assertGreater(pa_gal.poradi, so_gal.poradi) # Dokonce i tušíme, jaká ta pořadí jsou # NOTE: pokud tohle začne failovat, tak je asi safe to smazat. self.assertEqual(pa_gal.poradi, 2) self.assertEqual(so_gal.poradi, 1) # Teď je prohodíme: # NOTE: použití metody PATCH by tady prošlo, protože nekontrolujeme, # jaká metoda se používá. Ale dokud simulujeme orga na webu s odkazy # (``), tak použijeme GET. resp = org_client.get( reverse('galerie_doleva', kwargs={'soustredeni': self.sous_a.id, 'galerie': top_gal.pk, 'subgalerie': pa_gal.pk}), follow=True, ) self.assertEqual(resp.status_code, 200) resp = org_client.get( reverse('galerie_doprava', kwargs={'soustredeni': self.sous_a.id, 'galerie': top_gal.pk, 'subgalerie': so_gal.pk}), follow=True, ) self.assertEqual(resp.status_code, 200) # Změnil se stav pa_gal.refresh_from_db() so_gal.refresh_from_db() self.assertLess(pa_gal.poradi, so_gal.poradi) self.assertEqual(pa_gal.poradi, 1) self.assertEqual(so_gal.poradi, 2) # Ověříme, že v galeriích jsou obrázky self.assertEqual(top_gal.soubor_set.count(), 0) # FIXME: related_name self.assertEqual(top_gal.podgalerie.count(), 2) self.assertEqual(pa_gal.podgalerie.count(), 0) self.assertEqual(pa_gal.soubor_set.count(), 3) self.assertEqual(so_gal.podgalerie.count(), 0) self.assertEqual(so_gal.soubor_set.count(), 1) # Zveřejníme galerii (už ne pomocí org_clienta, bo se to dělá v Adminu, ale tu admin action použít chceme umět) # Otestujeme práva, že ještě veřejná není self.assertEqual(top_gal.zobrazit, Galerie.Viditelnost.ORG) self.assertEqual(pa_gal.zobrazit, Galerie.Viditelnost.ORG) self.assertEqual(so_gal.zobrazit, Galerie.Viditelnost.ORG) # (použití Admina zkopírováno z personalni.tests, což odkazuje na https://www.argpar.se/posts/programming/testing-django-admin/ adm_site = AdminSite() galerieadmin = g_adm.GalerieAdmin(Galerie, adm_site) # Sobotní galerie budiž jen pro účastníky so_gal.zobrazit = Galerie.Viditelnost.UCASTNIK so_gal.save() # FIXME: queryset z literálu g_adm.zverejnit_fotogalerii(galerieadmin, HttpRequest(), Galerie.objects.filter(pk=top_gal.pk)) top_gal.refresh_from_db() pa_gal.refresh_from_db() so_gal.refresh_from_db() # Otestujeme práva self.assertEqual(top_gal.zobrazit, Galerie.Viditelnost.VZDY) self.assertEqual(pa_gal.zobrazit, Galerie.Viditelnost.VZDY) self.assertEqual(so_gal.zobrazit, Galerie.Viditelnost.UCASTNIK) # otestujeme i že jde dělat requesty url_top = reverse('galerie_galerie', kwargs={'soustredeni': self.sous_a.id, 'galerie': top_gal.pk}) url_pa = reverse('galerie_galerie', kwargs={'soustredeni': self.sous_a.id, 'galerie': pa_gal.pk}) url_so = reverse('galerie_galerie', kwargs={'soustredeni': self.sous_a.id, 'galerie': so_gal.pk}) url_pa_soub = [reverse('galerie_soubor', kwargs={'soustredeni': self.sous_a.id, 'galerie': pa_gal.pk, 'soubor': s.pk}) for s in pa_gal.soubor_set.all()] url_so_soub = [reverse('galerie_soubor', kwargs={'soustredeni': self.sous_a.id, 'galerie': so_gal.pk, 'soubor': s.pk}) for s in so_gal.soubor_set.all()] verejne_url = [url_top, url_pa] + url_pa_soub ucastnik_url = [url_so] + url_so_soub # TODO: mít org-only galerii? uc_client = Client() uc_client.force_login(self.ucastnik_a.osoba.user) ucb_client = Client() ucb_client.force_login(self.ucastnik_b.osoba.user) neuc_client = Client() neuc_client.force_login(self.neucastnik.osoba.user) verej_client = Client() NESMI_ZOBRAZIT = 404 for url in ucastnik_url: for cl, status in ( (org_client, 200), (uc_client, 200), (ucb_client, NESMI_ZOBRAZIT), (neuc_client, NESMI_ZOBRAZIT), (verej_client, NESMI_ZOBRAZIT), ): resp = cl.get(url) self.assertEqual(resp.status_code, status) for url in verejne_url: for cl in (org_client, uc_client, ucb_client, neuc_client, verej_client): resp = cl.get(url) self.assertEqual(resp.status_code, 200)