From b41dd5213b93aee74f160ac8dfa239f15e5dbf7e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Romain=20S=C3=A9bille?= <20045330+rsebille@users.noreply.github.com> Date: Fri, 31 Oct 2025 14:52:16 +0100 Subject: [PATCH 1/3] fundraising.tests: Test the `download_donor_report` custom action --- fundraising/tests/test_admin.py | 78 +++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) create mode 100644 fundraising/tests/test_admin.py diff --git a/fundraising/tests/test_admin.py b/fundraising/tests/test_admin.py new file mode 100644 index 0000000000..496175a056 --- /dev/null +++ b/fundraising/tests/test_admin.py @@ -0,0 +1,78 @@ +from django.contrib.admin import helpers +from django.contrib.auth.models import User +from django.test import TestCase +from django.urls import reverse +from django.utils import timezone +from django.utils.crypto import get_random_string + +from fundraising.models import DjangoHero + + +class CustomViewTests(TestCase): + @classmethod + def setUpTestData(cls): + cls.super_user = User.objects.create_superuser(username="superuser") + + def setUp(self): + super().setUp() + self.client.force_login(self.super_user) + + def test_download_donor_report(self): + a_hero = DjangoHero.objects.create(approved=True, is_visible=True) + donation = a_hero.donation_set.create(interval="onetime") + donation.payment_set.create( + amount=42, + stripe_charge_id=get_random_string(length=12), + ) + response = self.client.post( + reverse("admin:fundraising_djangohero_changelist"), + { + "action": "download_donor_report", + helpers.ACTION_CHECKBOX_NAME: [a_hero.pk], + }, + follow=True, + ) + self.assertEqual(response.status_code, 200) + self.assertEqual( + response.content, + "\r\n".join( + [ + "name,email,alternate email,last gift date,gift amount (US$)," + "interval,recurring active?,location", + f",,,{timezone.now():%Y-%m-%d},42.00,One-time ,,", + "", # empty end line + ] + ).encode(), + ) + + def test_download_donor_report_get_latest_payment(self): + a_hero = DjangoHero.objects.create(approved=True, is_visible=True) + donation = a_hero.donation_set.create(interval="onetime") + donation.payment_set.create( + amount=42, + stripe_charge_id=get_random_string(length=12), + ) + donation.payment_set.create( + amount=21, + stripe_charge_id=get_random_string(length=12), + ) + response = self.client.post( + reverse("admin:fundraising_djangohero_changelist"), + { + "action": "download_donor_report", + helpers.ACTION_CHECKBOX_NAME: [a_hero.pk], + }, + follow=True, + ) + self.assertEqual(response.status_code, 200) + self.assertEqual( + response.content, + "\r\n".join( + [ + "name,email,alternate email,last gift date,gift amount (US$)," + "interval,recurring active?,location", + f",,,{timezone.now():%Y-%m-%d},21.00,One-time,,", + "", # empty end line + ] + ).encode(), + ) From b8005d5a5e8d24198c2142e5934eb42ca20dd8fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Romain=20S=C3=A9bille?= <20045330+rsebille@users.noreply.github.com> Date: Fri, 31 Oct 2025 14:57:00 +0100 Subject: [PATCH 2/3] fundraising.admin.download_donor_report: Remove extra whitespace in `interval` column --- fundraising/admin_views.py | 2 +- fundraising/tests/test_admin.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/fundraising/admin_views.py b/fundraising/admin_views.py index 48aab99934..7677ee158e 100644 --- a/fundraising/admin_views.py +++ b/fundraising/admin_views.py @@ -44,7 +44,7 @@ def download_donor_report(modeladmin, request, queryset): alternate_email, last_gift_date, last_gift_amount, - last_gift.get_interval_display().replace("donation", ""), + last_gift.get_interval_display().replace("donation", "").strip(), "Yes" if last_gift.stripe_subscription_id else "", donor.location, ] diff --git a/fundraising/tests/test_admin.py b/fundraising/tests/test_admin.py index 496175a056..76db4fabd7 100644 --- a/fundraising/tests/test_admin.py +++ b/fundraising/tests/test_admin.py @@ -39,7 +39,7 @@ def test_download_donor_report(self): [ "name,email,alternate email,last gift date,gift amount (US$)," "interval,recurring active?,location", - f",,,{timezone.now():%Y-%m-%d},42.00,One-time ,,", + f",,,{timezone.now():%Y-%m-%d},42.00,One-time,,", "", # empty end line ] ).encode(), From ddf49663b6dc316d7885f1fc69673fec90223009 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Romain=20S=C3=A9bille?= <20045330+rsebille@users.noreply.github.com> Date: Fri, 31 Oct 2025 15:08:52 +0100 Subject: [PATCH 3/3] fundraising.tests.admin: Count queries for `download_donor_report()` --- fundraising/tests/test_admin.py | 39 ++++++++++++++++++++++----------- 1 file changed, 26 insertions(+), 13 deletions(-) diff --git a/fundraising/tests/test_admin.py b/fundraising/tests/test_admin.py index 76db4fabd7..a24094dffe 100644 --- a/fundraising/tests/test_admin.py +++ b/fundraising/tests/test_admin.py @@ -18,20 +18,31 @@ def setUp(self): self.client.force_login(self.super_user) def test_download_donor_report(self): - a_hero = DjangoHero.objects.create(approved=True, is_visible=True) - donation = a_hero.donation_set.create(interval="onetime") - donation.payment_set.create( - amount=42, - stripe_charge_id=get_random_string(length=12), - ) - response = self.client.post( - reverse("admin:fundraising_djangohero_changelist"), - { - "action": "download_donor_report", - helpers.ACTION_CHECKBOX_NAME: [a_hero.pk], - }, - follow=True, + heroes = [] + for _ in range(3): + a_hero = DjangoHero.objects.create(approved=True, is_visible=True) + donation = a_hero.donation_set.create(interval="onetime") + donation.payment_set.create( + amount=42, + stripe_charge_id=get_random_string(length=12), + ) + heroes.append(a_hero) + num_queries = ( + 1 # SELECT "django_session" + + 1 # SELECT "auth_user" + + 2 # COUNT("fundraising_djangohero") + + 1 # SELECT "fundraising_djangohero" + + 3 # SELECT "fundraising_payment". FIXME: N+1 queries ) + with self.assertNumQueries(num_queries): + response = self.client.post( + reverse("admin:fundraising_djangohero_changelist"), + { + "action": "download_donor_report", + helpers.ACTION_CHECKBOX_NAME: [h.pk for h in heroes], + }, + follow=True, + ) self.assertEqual(response.status_code, 200) self.assertEqual( response.content, @@ -40,6 +51,8 @@ def test_download_donor_report(self): "name,email,alternate email,last gift date,gift amount (US$)," "interval,recurring active?,location", f",,,{timezone.now():%Y-%m-%d},42.00,One-time,,", + f",,,{timezone.now():%Y-%m-%d},42.00,One-time,,", + f",,,{timezone.now():%Y-%m-%d},42.00,One-time,,", "", # empty end line ] ).encode(),