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 pathlib import Path
from django.contrib.admin.sites import AdminSite
from django.core.management import call_command
from django.core.management.base import CommandError
from django.db import IntegrityError
@@ -11,7 +12,9 @@ from django.test import TestCase
from authentication.models import User
from club.models import Club, ClubMembership
from members.admin import FamilyAdmin
from members.models import Family, FamilyMembership, Member
from members.services import MemberImportResult
# Create your tests here.
@@ -167,6 +170,13 @@ class FamilyMembershipModelTests(TestCase):
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):
family = Family.objects.create(name="Fam")
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())
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):
"""Exercise the admin config end-to-end to catch misregistration
(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))
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)