From ff2fd01dba5de6b0d641e86df9bba64f4770e8f9 Mon Sep 17 00:00:00 2001 From: Baptiste Mispelon Date: Fri, 13 Dec 2024 14:16:05 +0100 Subject: [PATCH] Fixed issue with uploaded file handling in fundraising tests The tests currently fail on docker. Using the TemporaryMediaRootMixin originally developped for the views tests should fix that. --- fundraising/tests/test_models.py | 25 ++++-------------- fundraising/tests/test_views.py | 44 +++++--------------------------- fundraising/tests/utils.py | 41 +++++++++++++++++++++++++++++ 3 files changed, 52 insertions(+), 58 deletions(-) create mode 100644 fundraising/tests/utils.py diff --git a/fundraising/tests/test_models.py b/fundraising/tests/test_models.py index 48a848efd7..3f443b6ff7 100644 --- a/fundraising/tests/test_models.py +++ b/fundraising/tests/test_models.py @@ -1,14 +1,12 @@ -import os from datetime import date -from django.conf import settings from django.test import TestCase -from PIL import Image from ..models import DjangoHero, Donation, InKindDonor +from .utils import ImageFileFactory, TemporaryMediaRootMixin -class TestDjangoHero(TestCase): +class TestDjangoHero(TemporaryMediaRootMixin, TestCase): def setUp(self): kwargs = { "approved": True, @@ -26,26 +24,13 @@ def setUp(self): self.today = date.today() def test_thumbnail(self): - try: - os.makedirs(os.path.join(settings.MEDIA_ROOT, "fundraising/logos/")) - except OSError: # directory may already exist - pass - image_path = os.path.join( - settings.MEDIA_ROOT, "fundraising/logos/test_logo.jpg" - ) - image = Image.new("L", (500, 500)) - image.save(image_path) - self.h1.logo = image_path + self.h1.logo = ImageFileFactory(name="logo.jpeg") self.h1.save() thumbnail = self.h1.thumbnail self.assertEqual(thumbnail.x, 170) self.assertEqual(thumbnail.y, 170) - os.remove(image_path) - self.assertTrue( - os.path.exists( - thumbnail.url.replace(settings.MEDIA_URL, f"{settings.MEDIA_ROOT}/") - ) - ) + self.h1.logo.delete() + self.assertTrue(thumbnail.exists()) def test_thumbnail_no_logo(self): self.assertIsNone(self.h2.thumbnail) diff --git a/fundraising/tests/test_views.py b/fundraising/tests/test_views.py index a8a96ccd96..37b2790996 100644 --- a/fundraising/tests/test_views.py +++ b/fundraising/tests/test_views.py @@ -1,55 +1,21 @@ import json -import shutil -import tempfile from datetime import date, datetime -from io import BytesIO from operator import attrgetter from unittest.mock import patch import stripe from django.conf import settings from django.core import mail -from django.core.files.images import ImageFile from django.template.defaultfilters import date as date_filter from django.test import TestCase from django.urls import reverse from django_hosts.resolvers import reverse as django_hosts_reverse from django_recaptcha.client import RecaptchaResponse -from PIL import Image from members.models import CorporateMember, Invoice from ..models import DjangoHero, Donation - - -def _make_image(name, width=1, height=1, color=(12, 75, 51)): - img = Image.new("RGB", (width, height), color=color) - out = BytesIO() - img.save(out, format=name.split(".")[-1]) - return ImageFile(out, name=name) - - -class TemporaryMediaRootMixin: - """ - A TestCase mixin that overrides settings.MEDIA_ROOT for every test on the - class to point to a temporary directory that is destroyed when the tests - finished. - The content of the directory persists between different tests on the class. - """ - - @classmethod - def setUpClass(cls): - super().setUpClass() - cls.tmpdir = tempfile.mkdtemp(prefix="djangoprojectcom_") - - @classmethod - def tearDownClass(cls): - shutil.rmtree(cls.tmpdir, ignore_errors=True) - super().tearDownClass() - - def run(self, result=None): - with self.settings(MEDIA_ROOT=self.tmpdir): - return super().run(result) +from .utils import ImageFileFactory, TemporaryMediaRootMixin class TestIndex(TestCase): @@ -77,7 +43,9 @@ def test_corporate_member_without_logo(self): def test_corporate_member_with_logo(self): member = CorporateMember.objects.create( - display_name="Test Member", membership_level=1, logo=_make_image("logo.png") + display_name="Test Member", + membership_level=1, + logo=ImageFileFactory("logo.png"), ) Invoice.objects.create(amount=100, expiration_date=date.today(), member=member) response = self.client.get(self.index_url) @@ -96,7 +64,7 @@ def test_corporate_member_with_logo(self): ) def test_corporate_member_with_non_square_logo(self): - logo = _make_image("wide.png", width=10) + logo = ImageFileFactory("wide.png", width=10) member = CorporateMember.objects.create( display_name="Test Member", membership_level=1, logo=logo ) @@ -130,7 +98,7 @@ def test_anonymous_donor_with_logo(self): is_visible=True, approved=True, hero_type="individual", - logo=_make_image("anonymous.png"), + logo=ImageFileFactory("anonymous.png"), ) donation = hero.donation_set.create(subscription_amount="5") donation.payment_set.create(amount="5") diff --git a/fundraising/tests/utils.py b/fundraising/tests/utils.py new file mode 100644 index 0000000000..29189676e2 --- /dev/null +++ b/fundraising/tests/utils.py @@ -0,0 +1,41 @@ +import shutil +import tempfile +from io import BytesIO + +from django.core.files.images import ImageFile +from PIL import Image + + +def ImageFileFactory(name, width=1, height=1, color=(12, 75, 51)): + """ + Return an ImageFile instance with the given name. + The image will be of the given size, and of solid color. The format will + be inferred from the filename. + """ + img = Image.new("RGB", (width, height), color=color) + out = BytesIO() + img.save(out, format=name.split(".")[-1]) + return ImageFile(out, name=name) + + +class TemporaryMediaRootMixin: + """ + A TestCase mixin that overrides settings.MEDIA_ROOT for every test on the + class to point to a temporary directory that is destroyed when the tests + finished. + The content of the directory persists between different tests on the class. + """ + + @classmethod + def setUpClass(cls): + super().setUpClass() + cls.tmpdir = tempfile.mkdtemp(prefix="djangoprojectcom_") + + @classmethod + def tearDownClass(cls): + shutil.rmtree(cls.tmpdir, ignore_errors=True) + super().tearDownClass() + + def run(self, result=None): + with self.settings(MEDIA_ROOT=self.tmpdir): + return super().run(result)