Add tests for uncovered members code paths

Close the small coverage gaps left after the members app move:
- FamilyMembership.__str__ string representation
- FamilyAdmin.member_count display (with and without members)
- CSV import skips a row with an empty required field (was only
  exercising the invalid-date path)
- MemberImportResult.successful_rows property

Coverage 98% -> 99% (69 tests). The only remaining uncovered lines are
the redundant re-link branch in MemberCsvImporter.import_row (131-132),
which is unreachable because update_or_create already sets member.user.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-12 00:20:27 +02:00
parent f3b1ae7e82
commit 4165729d61

View File

@@ -3,6 +3,7 @@ from datetime import date
from io import StringIO from io import StringIO
from pathlib import Path from pathlib import Path
from django.contrib.admin.sites import AdminSite
from django.core.management import call_command from django.core.management import call_command
from django.core.management.base import CommandError from django.core.management.base import CommandError
from django.db import IntegrityError from django.db import IntegrityError
@@ -11,7 +12,9 @@ from django.test import TestCase
from authentication.models import User from authentication.models import User
from club.models import Club, ClubMembership from club.models import Club, ClubMembership
from members.admin import FamilyAdmin
from members.models import Family, FamilyMembership, Member from members.models import Family, FamilyMembership, Member
from members.services import MemberImportResult
# Create your tests here. # Create your tests here.
@@ -167,6 +170,13 @@ class FamilyMembershipModelTests(TestCase):
self.assertEqual(membership.role, FamilyMembership.FamilyRole.PARENT) self.assertEqual(membership.role, FamilyMembership.FamilyRole.PARENT)
def test_str_includes_family_member_and_role(self):
family = Family.objects.create(name="The Smiths")
member = Member.objects.create(first_name="Pat", last_name="Smith")
membership = FamilyMembership.objects.create(family=family, member=member, role=FamilyMembership.FamilyRole.GUARDIAN)
self.assertEqual(str(membership), "The Smiths - Pat Smith (guardian)")
def test_member_unique_per_family(self): def test_member_unique_per_family(self):
family = Family.objects.create(name="Fam") family = Family.objects.create(name="Fam")
member = Member.objects.create(first_name="Solo", last_name="Once") member = Member.objects.create(first_name="Solo", last_name="Once")
@@ -197,6 +207,22 @@ class FamilyMembershipModelTests(TestCase):
self.assertTrue(Member.objects.filter(pk=member.pk).exists()) self.assertTrue(Member.objects.filter(pk=member.pk).exists())
class FamilyAdminTests(TestCase):
def test_member_count_reflects_memberships(self):
family = Family.objects.create(name="The Smiths")
for i in range(3):
member = Member.objects.create(first_name=f"Kid{i}", last_name="Smith")
FamilyMembership.objects.create(family=family, member=member, role=FamilyMembership.FamilyRole.CHILD)
admin_instance = FamilyAdmin(Family, AdminSite())
self.assertEqual(admin_instance.member_count(family), 3)
def test_member_count_is_zero_without_members(self):
family = Family.objects.create(name="Empty")
admin_instance = FamilyAdmin(Family, AdminSite())
self.assertEqual(admin_instance.member_count(family), 0)
class AdminSmokeTests(TestCase): class AdminSmokeTests(TestCase):
"""Exercise the admin config end-to-end to catch misregistration """Exercise the admin config end-to-end to catch misregistration
(bad search_fields, autocomplete targets, fieldsets, custom forms).""" (bad search_fields, autocomplete targets, fieldsets, custom forms)."""
@@ -493,3 +519,28 @@ class ImportMembersCsvCommandTests(TestCase):
) )
self.assertIn("CSV file is empty or missing a header row.", str(context.exception)) self.assertIn("CSV file is empty or missing a header row.", str(context.exception))
def test_import_skips_row_with_empty_required_field(self):
csv_path = self.write_csv(
"\n".join(
[
"first_name,last_name,email,date_of_birth,create_account,club_name,license_number",
"Jane,Doe,jane@example.com,2010-04-12,false,City Swim Club,LIC-001",
",Missing,nofirst@example.com,2011-05-13,false,City Swim Club,LIC-002",
]
)
)
stdout, stderr = self.call_import_command(csv_path)
self.assertIn("Row 3 skipped:", stderr)
self.assertIn("first_name is required.", stderr)
self.assertIn("Members created: 1.", stdout)
self.assertIn("Rows skipped: 1.", stdout)
self.assertFalse(Member.objects.filter(email="nofirst@example.com").exists())
class MemberImportResultTests(TestCase):
def test_successful_rows_sums_created_and_updated(self):
result = MemberImportResult(created_members=2, updated_members=3)
self.assertEqual(result.successful_rows, 5)