Closes #32. Keep an event upcoming until its end date (and end time, when set) has passed, and render the Past tab from past events.
708 lines
25 KiB
Python
708 lines
25 KiB
Python
import datetime
|
|
from unittest.mock import patch
|
|
|
|
from django.test import Client, TestCase, override_settings
|
|
from django.urls import reverse
|
|
|
|
from scha.settings.base import build_csrf_trusted_origins
|
|
|
|
from django.contrib.auth.models import User
|
|
from django.utils import timezone
|
|
|
|
from .admin import membership_payment_exists, person_payment_exists
|
|
from .forms import AddressForm, PeopleForm
|
|
from .models import (
|
|
AddressModel1,
|
|
CalendarEvent,
|
|
Membership,
|
|
MembershipPerson,
|
|
Payments,
|
|
SCHAOfficer,
|
|
UsefulLinks,
|
|
)
|
|
|
|
|
|
class CsrfTrustedOriginsTests(TestCase):
|
|
def test_derives_https_origins_from_public_hosts(self):
|
|
origins = build_csrf_trusted_origins(
|
|
["schawheaton.com", "www.schawheaton.com"]
|
|
)
|
|
|
|
self.assertEqual(
|
|
origins,
|
|
[
|
|
"https://schawheaton.com",
|
|
"https://www.schawheaton.com",
|
|
],
|
|
)
|
|
|
|
def test_derives_http_origins_for_local_hosts(self):
|
|
origins = build_csrf_trusted_origins(["localhost", "127.0.0.1"])
|
|
|
|
self.assertEqual(origins, ["http://localhost", "http://127.0.0.1"])
|
|
|
|
def test_explicit_origins_win(self):
|
|
origins = build_csrf_trusted_origins(
|
|
["schawheaton.com"],
|
|
["https://custom.example"],
|
|
)
|
|
|
|
self.assertEqual(origins, ["https://custom.example"])
|
|
|
|
|
|
class PublicPageTests(TestCase):
|
|
def setUp(self):
|
|
self.client = Client()
|
|
|
|
def test_index_renders(self):
|
|
response = self.client.get(reverse("index2"))
|
|
self.assertEqual(response.status_code, 200)
|
|
|
|
def test_about_us_renders(self):
|
|
response = self.client.get(reverse("about_us2"))
|
|
self.assertEqual(response.status_code, 200)
|
|
|
|
def test_calendar_renders(self):
|
|
response = self.client.get(reverse("calendar2"))
|
|
self.assertEqual(response.status_code, 200)
|
|
|
|
def test_newsletters_renders(self):
|
|
response = self.client.get(reverse("newsletters2"))
|
|
self.assertEqual(response.status_code, 200)
|
|
|
|
def test_dues_renders(self):
|
|
response = self.client.get(reverse("dues2"))
|
|
self.assertEqual(response.status_code, 200)
|
|
|
|
def test_scha_board_renders_officers(self):
|
|
SCHAOfficer.objects.create(
|
|
name="Jane President",
|
|
position="President",
|
|
email="jane@example.com",
|
|
)
|
|
|
|
response = self.client.get(reverse("scha_board2"))
|
|
|
|
self.assertEqual(response.status_code, 200)
|
|
self.assertContains(response, "Jane President")
|
|
|
|
def test_useful_links_renders_links(self):
|
|
UsefulLinks.objects.create(name="City of Wheaton", url="https://example.com")
|
|
|
|
response = self.client.get(reverse("useful_links2"))
|
|
|
|
self.assertEqual(response.status_code, 200)
|
|
self.assertContains(response, "City of Wheaton")
|
|
|
|
def test_public_pages_include_seo_metadata(self):
|
|
pages = [
|
|
("index2", "Home | Stonehedge"),
|
|
("about_us2", "About Us | Stonehedge"),
|
|
("calendar2", "Calendar | Stonehedge"),
|
|
("dues2", "Pay Dues | Stonehedge"),
|
|
("membership_form2", "Join Today | Stonehedge"),
|
|
("scha_board2", "SCHA Board | Stonehedge"),
|
|
("useful_links2", "Useful Links | Stonehedge"),
|
|
]
|
|
|
|
for name, title_fragment in pages:
|
|
with self.subTest(page=name):
|
|
response = self.client.get(reverse(name))
|
|
self.assertEqual(response.status_code, 200)
|
|
self.assertContains(response, f"<title>{title_fragment}")
|
|
self.assertContains(response, 'name="description"')
|
|
self.assertContains(response, 'property="og:title"')
|
|
self.assertContains(response, 'rel="canonical"')
|
|
self.assertContains(response, 'id="main-content"')
|
|
self.assertContains(response, "Skip to main content")
|
|
|
|
def test_robots_txt(self):
|
|
response = self.client.get(reverse("robots_txt"))
|
|
self.assertEqual(response.status_code, 200)
|
|
self.assertEqual(response["Content-Type"].split(";")[0], "text/plain")
|
|
self.assertContains(response, "Sitemap:")
|
|
self.assertContains(response, "Disallow: /admin/")
|
|
|
|
def test_sitemap_xml(self):
|
|
response = self.client.get(reverse("sitemap_xml"))
|
|
self.assertEqual(response.status_code, 200)
|
|
self.assertIn("xml", response["Content-Type"])
|
|
self.assertContains(response, reverse("index2"))
|
|
self.assertContains(response, reverse("dues2"))
|
|
|
|
def test_llms_txt(self):
|
|
response = self.client.get(reverse("llms_txt"))
|
|
self.assertEqual(response.status_code, 200)
|
|
self.assertEqual(response["Content-Type"].split(";")[0], "text/plain")
|
|
self.assertContains(response, "Stonehedge Community Homeowners Association")
|
|
self.assertContains(response, reverse("membership_form2").lstrip("/"))
|
|
|
|
|
|
@override_settings(
|
|
RECAPTCHA_PUBLIC_KEY="test-public-key",
|
|
RECAPTCHA_PRIVATE_KEY="test-private-key",
|
|
)
|
|
class MembershipFormViewTests(TestCase):
|
|
def setUp(self):
|
|
self.client = Client()
|
|
self.url = reverse("membership_form2")
|
|
|
|
def test_get_renders_form(self):
|
|
response = self.client.get(self.url)
|
|
|
|
self.assertEqual(response.status_code, 200)
|
|
self.assertIn("peopleForm1", response.context)
|
|
self.assertIn("addressForm", response.context)
|
|
self.assertIn("captchaForm", response.context)
|
|
|
|
@patch("schasite.views.CaptchaForm")
|
|
def test_post_success_creates_membership_and_redirects(self, mock_captcha_form):
|
|
mock_captcha_form.return_value.is_valid.return_value = True
|
|
|
|
payload = {
|
|
"streetAddress": "123 Main St",
|
|
"unit": "",
|
|
"city": "Wheaton",
|
|
"state": "IL",
|
|
"zipCode": "60189",
|
|
"firstName1": "Pat",
|
|
"lastName1": "Neighbor",
|
|
"email1": "pat@example.com",
|
|
"phone1": "6305551212",
|
|
"firstName2": "",
|
|
"lastName2": "",
|
|
"email2": "",
|
|
"phone2": "",
|
|
"captcha": "passed",
|
|
}
|
|
|
|
response = self.client.post(self.url, payload)
|
|
|
|
self.assertEqual(response.status_code, 302)
|
|
self.assertEqual(response.url, reverse("index2"))
|
|
self.assertEqual(Membership.objects.count(), 1)
|
|
membership = Membership.objects.get()
|
|
self.assertEqual(membership.addressmodel1.address_1, "123 Main St")
|
|
self.assertEqual(
|
|
MembershipPerson.objects.filter(membership=membership).count(),
|
|
1,
|
|
)
|
|
|
|
@patch("schasite.views.CaptchaForm")
|
|
def test_post_missing_address_rerenders_form(self, mock_captcha_form):
|
|
mock_captcha_form.return_value.is_valid.return_value = True
|
|
|
|
payload = {
|
|
"streetAddress": "",
|
|
"unit": "",
|
|
"city": "Wheaton",
|
|
"state": "IL",
|
|
"zipCode": "60189",
|
|
"firstName1": "Pat",
|
|
"lastName1": "Neighbor",
|
|
"email1": "pat@example.com",
|
|
"phone1": "6305551212",
|
|
"firstName2": "",
|
|
"lastName2": "",
|
|
"email2": "",
|
|
"phone2": "",
|
|
"captcha": "passed",
|
|
}
|
|
|
|
response = self.client.post(self.url, payload)
|
|
|
|
self.assertEqual(response.status_code, 200)
|
|
self.assertEqual(Membership.objects.count(), 0)
|
|
self.assertIn("addressForm", response.context)
|
|
|
|
|
|
class AddressFormTests(TestCase):
|
|
def test_valid_address(self):
|
|
form = AddressForm(
|
|
data={
|
|
"address_1": "123 Main St",
|
|
"address_2": "",
|
|
"city": "Wheaton",
|
|
"state": "IL",
|
|
"zip_code": "60189",
|
|
}
|
|
)
|
|
self.assertTrue(form.is_valid())
|
|
|
|
def test_missing_street_is_invalid(self):
|
|
form = AddressForm(
|
|
data={
|
|
"address_1": "",
|
|
"city": "Wheaton",
|
|
"state": "IL",
|
|
"zip_code": "60189",
|
|
}
|
|
)
|
|
self.assertFalse(form.is_valid())
|
|
self.assertIn("address_1", form.errors)
|
|
|
|
|
|
class PeopleFormTests(TestCase):
|
|
def test_valid_person(self):
|
|
form = PeopleForm(
|
|
data={
|
|
"first_name": "Pat",
|
|
"last_name": "Neighbor",
|
|
"email": "pat@example.com",
|
|
"phone_number": "+16305551212",
|
|
}
|
|
)
|
|
self.assertTrue(form.is_valid())
|
|
|
|
def test_missing_phone_is_invalid(self):
|
|
form = PeopleForm(
|
|
data={
|
|
"first_name": "Pat",
|
|
"last_name": "Neighbor",
|
|
"email": "pat@example.com",
|
|
"phone_number": "",
|
|
}
|
|
)
|
|
self.assertFalse(form.is_valid())
|
|
|
|
|
|
class MembershipModelTests(TestCase):
|
|
def test_get_address_str_and_person_1(self):
|
|
membership = Membership.objects.create()
|
|
AddressModel1.objects.create(
|
|
membership=membership,
|
|
address_1="456 Oak Ave",
|
|
city="Wheaton",
|
|
state="IL",
|
|
zip_code="60189",
|
|
)
|
|
MembershipPerson.objects.create(
|
|
membership=membership,
|
|
first_name="Alex",
|
|
last_name="Resident",
|
|
email="alex@example.com",
|
|
phone_number="+16305559876",
|
|
)
|
|
|
|
self.assertEqual(membership.get_address_str(), "456 Oak Ave")
|
|
self.assertEqual(membership.get_person_1(), "alex@example.com")
|
|
self.assertIn("456 Oak Ave", str(membership))
|
|
|
|
def test_get_person_1_with_no_people(self):
|
|
membership = Membership.objects.create()
|
|
AddressModel1.objects.create(
|
|
membership=membership,
|
|
address_1="789 Pine St",
|
|
city="Wheaton",
|
|
state="IL",
|
|
zip_code="60189",
|
|
)
|
|
|
|
self.assertEqual(membership.get_person_1(), "No Person")
|
|
|
|
|
|
class CalendarEventTests(TestCase):
|
|
def test_has_date_and_future_past_helpers(self):
|
|
today = datetime.date.today()
|
|
past = CalendarEvent.objects.create(
|
|
event_name="Past Picnic",
|
|
start_date=today - datetime.timedelta(days=7),
|
|
)
|
|
future = CalendarEvent.objects.create(
|
|
event_name="Future Picnic",
|
|
start_date=today + datetime.timedelta(days=7),
|
|
)
|
|
undated = CalendarEvent.objects.create(event_name="TBD Event")
|
|
|
|
self.assertTrue(past.has_date())
|
|
self.assertTrue(past.past_event())
|
|
self.assertFalse(past.future_event())
|
|
|
|
self.assertTrue(future.future_event())
|
|
self.assertFalse(future.past_event())
|
|
|
|
self.assertTrue(undated.no_date())
|
|
self.assertFalse(undated.has_date())
|
|
self.assertFalse(undated.past_event())
|
|
self.assertFalse(undated.future_event())
|
|
|
|
def test_in_progress_multiday_event_counts_as_upcoming(self):
|
|
today = datetime.date.today()
|
|
ongoing = CalendarEvent.objects.create(
|
|
event_name="Block Party",
|
|
start_date=today - datetime.timedelta(days=1),
|
|
end_date=today + datetime.timedelta(days=1),
|
|
)
|
|
ended = CalendarEvent.objects.create(
|
|
event_name="Finished Sale",
|
|
start_date=today - datetime.timedelta(days=4),
|
|
end_date=today - datetime.timedelta(days=1),
|
|
)
|
|
|
|
self.assertTrue(ongoing.future_event())
|
|
self.assertFalse(ongoing.past_event())
|
|
self.assertTrue(ended.past_event())
|
|
self.assertFalse(ended.future_event())
|
|
|
|
def test_end_time_hides_event_only_after_it_finishes(self):
|
|
now = datetime.datetime.now()
|
|
still_going = now + datetime.timedelta(hours=2)
|
|
already_over = now - datetime.timedelta(hours=1)
|
|
|
|
current = CalendarEvent.objects.create(
|
|
event_name="Still Going",
|
|
start_date=(now - datetime.timedelta(days=1)).date(),
|
|
end_date=still_going.date(),
|
|
end_time=still_going.time().replace(microsecond=0),
|
|
)
|
|
finished = CalendarEvent.objects.create(
|
|
event_name="Already Over",
|
|
start_date=(already_over - datetime.timedelta(days=1)).date(),
|
|
end_date=already_over.date(),
|
|
end_time=already_over.time().replace(microsecond=0),
|
|
)
|
|
|
|
self.assertTrue(current.future_event())
|
|
self.assertFalse(current.past_event())
|
|
self.assertTrue(finished.past_event())
|
|
self.assertFalse(finished.future_event())
|
|
|
|
def test_time_range_display(self):
|
|
event = CalendarEvent.objects.create(
|
|
event_name="Timed Event",
|
|
start_time=datetime.time(11, 0),
|
|
end_time=datetime.time(15, 0),
|
|
)
|
|
self.assertEqual(event.time_range_display(), "11:00 AM - 3:00 PM")
|
|
|
|
start_only = CalendarEvent.objects.create(
|
|
event_name="Start Only",
|
|
start_time=datetime.time(9, 30),
|
|
)
|
|
self.assertEqual(start_only.time_range_display(), "9:30 AM")
|
|
|
|
no_time = CalendarEvent.objects.create(event_name="No Time")
|
|
self.assertEqual(no_time.time_range_display(), "")
|
|
|
|
def test_calendar_page_splits_past_and_future(self):
|
|
today = datetime.date.today()
|
|
CalendarEvent.objects.create(
|
|
event_name="Archived Picnic",
|
|
start_date=today - datetime.timedelta(days=3),
|
|
)
|
|
CalendarEvent.objects.create(
|
|
event_name="Future Event",
|
|
start_date=today + datetime.timedelta(days=3),
|
|
)
|
|
CalendarEvent.objects.create(
|
|
event_name="Ongoing Event",
|
|
start_date=today - datetime.timedelta(days=1),
|
|
end_date=today + datetime.timedelta(days=1),
|
|
)
|
|
|
|
response = self.client.get(reverse("calendar2"))
|
|
|
|
self.assertEqual(response.status_code, 200)
|
|
self.assertEqual(len(response.context["past_events"]), 1)
|
|
self.assertEqual(len(response.context["future_events"]), 2)
|
|
self.assertEqual(response.context["past_events"][0].event_name, "Archived Picnic")
|
|
future_names = {event.event_name for event in response.context["future_events"]}
|
|
self.assertEqual(future_names, {"Future Event", "Ongoing Event"})
|
|
content = response.content.decode()
|
|
upcoming_html, past_html = content.split('id="past" role="tabpanel"', 1)
|
|
self.assertIn("Ongoing Event", upcoming_html)
|
|
self.assertNotIn("Ongoing Event", past_html)
|
|
self.assertIn("Archived Picnic", past_html)
|
|
self.assertNotIn("Archived Picnic", upcoming_html)
|
|
|
|
|
|
@override_settings(
|
|
STRIPE_PUBLISHABLE_KEY="pk_test_123",
|
|
STRIPE_SECRET_KEY="sk_test_123",
|
|
)
|
|
class StripeEndpointTests(TestCase):
|
|
def setUp(self):
|
|
self.client = Client()
|
|
|
|
def test_stripe_config_returns_publishable_key(self):
|
|
response = self.client.get("/config/")
|
|
|
|
self.assertEqual(response.status_code, 200)
|
|
self.assertEqual(response.json(), {"publicKey": "pk_test_123"})
|
|
|
|
@patch("schasite.views.stripe.checkout.Session.create")
|
|
def test_create_checkout_session_returns_session_id(self, mock_create):
|
|
mock_create.return_value = {"id": "cs_test_abc"}
|
|
|
|
response = self.client.get("/create-checkout-session/")
|
|
|
|
self.assertEqual(response.status_code, 200)
|
|
self.assertEqual(response.json(), {"sessionId": "cs_test_abc"})
|
|
mock_create.assert_called_once()
|
|
|
|
@patch("schasite.views.stripe.checkout.Session.create")
|
|
def test_create_checkout_session_returns_error_payload(self, mock_create):
|
|
mock_create.side_effect = Exception("stripe down")
|
|
|
|
response = self.client.get("/create-checkout-session/")
|
|
|
|
self.assertEqual(response.status_code, 200)
|
|
self.assertEqual(response.json(), {"error": "stripe down"})
|
|
|
|
|
|
class AuthViewTests(TestCase):
|
|
"""Auth member routes are gated on settings.DEBUG at URLconf import time.
|
|
|
|
Django forces DEBUG=False during tests, so those named URLs are not
|
|
registered. Call views via RequestFactory instead of reverse()/Client.
|
|
"""
|
|
|
|
def setUp(self):
|
|
from django.contrib.auth.models import AnonymousUser
|
|
from django.test import RequestFactory
|
|
|
|
from schasite import views
|
|
|
|
self.factory = RequestFactory()
|
|
self.views = views
|
|
self.AnonymousUser = AnonymousUser
|
|
|
|
@patch("schasite.views.render")
|
|
def test_login_get_uses_signin_template(self, mock_render):
|
|
from django.http import HttpResponse
|
|
|
|
mock_render.return_value = HttpResponse(status=200)
|
|
request = self.factory.get("/login/")
|
|
|
|
response = self.views.login(request)
|
|
|
|
self.assertEqual(response.status_code, 200)
|
|
mock_render.assert_called_once_with(request, "schasite/signin.html", {})
|
|
|
|
@patch("schasite.views.render")
|
|
def test_signup_get_uses_signup_template(self, mock_render):
|
|
from django.http import HttpResponse
|
|
|
|
mock_render.return_value = HttpResponse(status=200)
|
|
request = self.factory.get("/signup/")
|
|
|
|
response = self.views.signup(request)
|
|
|
|
self.assertEqual(response.status_code, 200)
|
|
mock_render.assert_called_once_with(request, "schasite/signup.html", {})
|
|
|
|
@patch("schasite.views.render")
|
|
def test_password_reset_get_uses_template(self, mock_render):
|
|
from django.http import HttpResponse
|
|
|
|
mock_render.return_value = HttpResponse(status=200)
|
|
request = self.factory.get("/password_reset/")
|
|
|
|
response = self.views.password_reset(request)
|
|
|
|
self.assertEqual(response.status_code, 200)
|
|
mock_render.assert_called_once_with(
|
|
request, "schasite/password_reset.html", {}
|
|
)
|
|
|
|
def test_member_directory_redirects_anonymous(self):
|
|
request = self.factory.get("/directory/")
|
|
request.user = self.AnonymousUser()
|
|
response = self.views.member_directory(request)
|
|
self.assertEqual(response.status_code, 302)
|
|
self.assertIn("/login", response.url)
|
|
|
|
def test_member_dashboard_redirects_anonymous(self):
|
|
request = self.factory.get("/dashboard/")
|
|
request.user = self.AnonymousUser()
|
|
response = self.views.member_dashboard(request)
|
|
self.assertEqual(response.status_code, 302)
|
|
self.assertIn("/login", response.url)
|
|
|
|
def test_member_posts_redirects_anonymous(self):
|
|
request = self.factory.get("/posts/")
|
|
request.user = self.AnonymousUser()
|
|
response = self.views.member_posts(request)
|
|
self.assertEqual(response.status_code, 302)
|
|
self.assertIn("/login", response.url)
|
|
|
|
|
|
def _membership_with_person(email="alex@example.com"):
|
|
membership = Membership.objects.create()
|
|
AddressModel1.objects.create(
|
|
membership=membership,
|
|
address_1="123 Main St",
|
|
city="Wheaton",
|
|
state="IL",
|
|
zip_code="60189",
|
|
)
|
|
person = MembershipPerson.objects.create(
|
|
membership=membership,
|
|
first_name="Alex",
|
|
last_name="Resident",
|
|
email=email,
|
|
phone_number="+16305559876",
|
|
)
|
|
return membership, person
|
|
|
|
|
|
class AdminPaidIndicatorTests(TestCase):
|
|
def setUp(self):
|
|
self.client = Client()
|
|
self.admin_user = User.objects.create_superuser(
|
|
"admin", "admin@example.com", "pass"
|
|
)
|
|
self.client.force_login(self.admin_user)
|
|
self.this_year = timezone.now().year
|
|
self.membership, self.person = _membership_with_person("paid@example.com")
|
|
|
|
def test_person_marked_paid_when_email_matches_current_year_payment(self):
|
|
Payments.objects.create(
|
|
email="PAID@example.com",
|
|
date=datetime.date(self.this_year, 3, 1),
|
|
)
|
|
|
|
paid = MembershipPerson.objects.annotate(
|
|
_has_payment=person_payment_exists()
|
|
).get(pk=self.person.pk)
|
|
self.assertTrue(paid._has_payment)
|
|
|
|
unpaid_membership, unpaid_person = _membership_with_person("unpaid@example.com")
|
|
unpaid = MembershipPerson.objects.annotate(
|
|
_has_payment=person_payment_exists()
|
|
).get(pk=unpaid_person.pk)
|
|
self.assertFalse(unpaid._has_payment)
|
|
|
|
membership = Membership.objects.annotate(
|
|
_has_payment=membership_payment_exists()
|
|
).get(pk=self.membership.pk)
|
|
self.assertTrue(membership._has_payment)
|
|
other = Membership.objects.annotate(
|
|
_has_payment=membership_payment_exists()
|
|
).get(pk=unpaid_membership.pk)
|
|
self.assertFalse(other._has_payment)
|
|
|
|
def test_prior_year_payment_does_not_count_as_paid(self):
|
|
Payments.objects.create(
|
|
email="paid@example.com",
|
|
date=datetime.date(self.this_year - 1, 6, 1),
|
|
)
|
|
person = MembershipPerson.objects.annotate(
|
|
_has_payment=person_payment_exists()
|
|
).get(pk=self.person.pk)
|
|
self.assertFalse(person._has_payment)
|
|
|
|
def test_person_changelist_shows_paid_icon(self):
|
|
Payments.objects.create(
|
|
email="paid@example.com",
|
|
date=datetime.date(self.this_year, 4, 15),
|
|
)
|
|
url = reverse("admin:schasite_membershipperson_changelist")
|
|
response = self.client.get(url)
|
|
self.assertEqual(response.status_code, 200)
|
|
self.assertContains(response, "Paid this year")
|
|
self.assertContains(response, "icon-yes.svg")
|
|
|
|
|
|
class AdminPaymentSearchTests(TestCase):
|
|
def setUp(self):
|
|
self.client = Client()
|
|
self.admin_user = User.objects.create_superuser(
|
|
"admin", "admin@example.com", "pass"
|
|
)
|
|
self.client.force_login(self.admin_user)
|
|
Payments.objects.create(email="dues@example.com", date=datetime.date(2026, 1, 10))
|
|
Payments.objects.create(email="other@example.com", date=datetime.date(2026, 2, 10))
|
|
|
|
def test_payments_admin_search_by_email(self):
|
|
url = reverse("admin:schasite_payments_changelist")
|
|
response = self.client.get(url, {"q": "dues@example.com"})
|
|
self.assertEqual(response.status_code, 200)
|
|
self.assertContains(response, "dues@example.com")
|
|
self.assertNotContains(response, "other@example.com")
|
|
|
|
|
|
class AdminCsvYearFilterTests(TestCase):
|
|
def setUp(self):
|
|
self.client = Client()
|
|
self.admin_user = User.objects.create_superuser(
|
|
"admin", "admin@example.com", "pass"
|
|
)
|
|
self.client.force_login(self.admin_user)
|
|
|
|
self.current, self.current_person = _membership_with_person("now@example.com")
|
|
self.old, self.old_person = _membership_with_person("old@example.com")
|
|
Membership.objects.filter(pk=self.old.pk).update(
|
|
created=timezone.make_aware(datetime.datetime(2024, 5, 1, 12, 0, 0))
|
|
)
|
|
|
|
Payments.objects.create(email="now@example.com", date=datetime.date(2026, 3, 1))
|
|
Payments.objects.create(email="old@example.com", date=datetime.date(2024, 3, 1))
|
|
|
|
def test_membership_csv_prompts_for_year(self):
|
|
url = reverse("admin:schasite_membership_changelist")
|
|
response = self.client.post(
|
|
url,
|
|
{
|
|
"action": "download_csv_by_members",
|
|
"_selected_action": [str(self.current.pk), str(self.old.pk)],
|
|
"index": "0",
|
|
},
|
|
)
|
|
self.assertEqual(response.status_code, 200)
|
|
self.assertContains(response, "Year:")
|
|
self.assertContains(response, "Download CSV")
|
|
|
|
def test_membership_csv_filters_by_created_year(self):
|
|
url = reverse("admin:schasite_membership_changelist")
|
|
response = self.client.post(
|
|
url,
|
|
{
|
|
"action": "download_csv_by_members",
|
|
"_selected_action": [str(self.current.pk), str(self.old.pk)],
|
|
"index": "0",
|
|
"apply": "Download CSV",
|
|
"year": "2024",
|
|
},
|
|
)
|
|
self.assertEqual(response.status_code, 200)
|
|
self.assertEqual(response["Content-Type"], "text/csv")
|
|
content = response.content.decode()
|
|
self.assertIn("old@example.com", content)
|
|
self.assertNotIn("now@example.com", content)
|
|
|
|
def test_membership_csv_by_address_filters_by_created_year(self):
|
|
url = reverse("admin:schasite_membership_changelist")
|
|
response = self.client.post(
|
|
url,
|
|
{
|
|
"action": "download_csv_by_address",
|
|
"_selected_action": [str(self.current.pk), str(self.old.pk)],
|
|
"index": "0",
|
|
"apply": "Download CSV",
|
|
"year": "2024",
|
|
},
|
|
)
|
|
self.assertEqual(response.status_code, 200)
|
|
content = response.content.decode()
|
|
self.assertIn("old@example.com", content)
|
|
self.assertNotIn("now@example.com", content)
|
|
|
|
def test_payments_csv_filters_by_payment_year(self):
|
|
url = reverse("admin:schasite_payments_changelist")
|
|
payments = list(Payments.objects.values_list("pk", flat=True))
|
|
response = self.client.post(
|
|
url,
|
|
{
|
|
"action": "download_payments",
|
|
"_selected_action": [str(pk) for pk in payments],
|
|
"index": "0",
|
|
"apply": "Download CSV",
|
|
"year": "2024",
|
|
},
|
|
)
|
|
self.assertEqual(response.status_code, 200)
|
|
content = response.content.decode()
|
|
self.assertIn("old@example.com", content)
|
|
self.assertNotIn("now@example.com", content)
|