Add Team/TeamRole/TeamMembership/TeamPicture models and fix two test bugs
- Fix Member.create(): post_save signal creates Member immediately on User creation,
so new users always hit the hasattr branch; check `created` flag to set initial
password and notes for genuinely new users
- Fix Season.for_date(): replace get() with filter().order_by("-start_date").first()
to handle overlapping seasons gracefully and raise DoesNotExist when none match
- Add TeamRole, Team, TeamMembership, TeamPicture models with rules permissions
- Add migration 0002 for new models
- Add test suites for members and teams covering all new model behaviour
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
246
teams/tests.py
246
teams/tests.py
@@ -1,3 +1,245 @@
|
||||
from django.test import TestCase
|
||||
import datetime
|
||||
|
||||
# Create your tests here.
|
||||
from django.contrib.auth import get_user_model
|
||||
from django.test import TestCase
|
||||
from django.utils import timezone
|
||||
|
||||
from members.models import Member
|
||||
from teams.models import Season, Team, TeamMembership, TeamPicture, TeamRole
|
||||
|
||||
User = get_user_model()
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Helpers
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
def make_member(email, first="Test", last="User"):
|
||||
user = User.objects.create_user(username=email, email=email, first_name=first, last_name=last)
|
||||
return user.member
|
||||
|
||||
|
||||
def make_season(start=datetime.date(2025, 9, 1), end=datetime.date(2026, 8, 31)):
|
||||
return Season.objects.create(start_date=start, end_date=end)
|
||||
|
||||
|
||||
def make_role(name="Player", abbreviation="P", admin_role=False):
|
||||
return TeamRole.objects.create(name=name, abbreviation=abbreviation, admin_role=admin_role)
|
||||
|
||||
|
||||
def make_team(name="Test Team"):
|
||||
return Team.objects.create(name=name)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Season
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
class SeasonStrTest(TestCase):
|
||||
def test_str(self):
|
||||
season = Season(start_date=datetime.date(2025, 9, 1), end_date=datetime.date(2026, 8, 31))
|
||||
self.assertEqual(str(season), "Season '25 - '26")
|
||||
|
||||
|
||||
class SeasonIsCurrentTest(TestCase):
|
||||
def setUp(self):
|
||||
today = timezone.now().date()
|
||||
self.season = Season.objects.create(
|
||||
start_date=today - datetime.timedelta(days=30),
|
||||
end_date=today + datetime.timedelta(days=30),
|
||||
)
|
||||
|
||||
def test_is_current_true(self):
|
||||
self.assertTrue(self.season.is_current)
|
||||
|
||||
def test_is_current_false_past(self):
|
||||
past = Season.objects.create(start_date=datetime.date(2020, 1, 1), end_date=datetime.date(2020, 12, 31))
|
||||
self.assertFalse(past.is_current)
|
||||
|
||||
def test_is_current_false_future(self):
|
||||
future = Season.objects.create(start_date=datetime.date(2099, 1, 1), end_date=datetime.date(2099, 12, 31))
|
||||
self.assertFalse(future.is_current)
|
||||
|
||||
|
||||
class SeasonDateRangeTest(TestCase):
|
||||
def test_date_range(self):
|
||||
start, end = datetime.date(2025, 9, 1), datetime.date(2026, 8, 31)
|
||||
season = Season(start_date=start, end_date=end)
|
||||
self.assertEqual(season.date_range, (start, end))
|
||||
|
||||
|
||||
class SeasonForDateTest(TestCase):
|
||||
def setUp(self):
|
||||
self.start = datetime.date(2025, 9, 1)
|
||||
self.end = datetime.date(2026, 8, 31)
|
||||
self.season = Season.objects.create(start_date=self.start, end_date=self.end)
|
||||
|
||||
def test_for_date_returns_season(self):
|
||||
self.assertEqual(Season.for_date(datetime.date(2026, 1, 15)), self.season)
|
||||
|
||||
def test_for_date_values_only(self):
|
||||
self.assertEqual(Season.for_date(datetime.date(2026, 1, 15), values_only=True), (self.start, self.end))
|
||||
|
||||
def test_for_date_defaults_to_today(self):
|
||||
today = timezone.now().date()
|
||||
season_today = Season.objects.create(
|
||||
start_date=today - datetime.timedelta(days=1),
|
||||
end_date=today + datetime.timedelta(days=364),
|
||||
)
|
||||
self.assertEqual(Season.for_date(), season_today)
|
||||
|
||||
|
||||
class SeasonGenerateDefaultTest(TestCase):
|
||||
def test_generate_year_duration(self):
|
||||
season = Season.generate_default(day=1, month=9, duration="1y")
|
||||
self.assertEqual(season.start_date, datetime.date(2026, 9, 1))
|
||||
self.assertEqual(season.end_date, datetime.date(2027, 8, 31))
|
||||
|
||||
def test_generate_month_duration(self):
|
||||
season = Season.generate_default(day=1, month=9, duration="3m")
|
||||
self.assertEqual(season.start_date, datetime.date(2026, 9, 1))
|
||||
self.assertEqual(season.end_date, datetime.date(2026, 11, 30))
|
||||
|
||||
def test_generate_invalid_duration_raises(self):
|
||||
with self.assertRaises(ValueError):
|
||||
Season.generate_default(day=1, month=9, duration="invalid")
|
||||
|
||||
def test_generate_persists_to_db(self):
|
||||
Season.generate_default(day=1, month=9, duration="1y")
|
||||
self.assertEqual(Season.objects.count(), 1)
|
||||
|
||||
|
||||
class SeasonAddMonthsTest(TestCase):
|
||||
def test_add_months_normal(self):
|
||||
self.assertEqual(Season._add_months(datetime.date(2025, 1, 1), 3), datetime.date(2025, 4, 1))
|
||||
|
||||
def test_add_months_year_rollover(self):
|
||||
self.assertEqual(Season._add_months(datetime.date(2025, 11, 1), 3), datetime.date(2026, 2, 1))
|
||||
|
||||
def test_add_months_day_clamp(self):
|
||||
self.assertEqual(Season._add_months(datetime.date(2025, 1, 31), 1), datetime.date(2025, 2, 28))
|
||||
|
||||
def test_add_months_twelve(self):
|
||||
self.assertEqual(Season._add_months(datetime.date(2025, 9, 1), 12), datetime.date(2026, 9, 1))
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# TeamRole
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
class TeamRoleStrTest(TestCase):
|
||||
def test_str(self):
|
||||
role = TeamRole(name="Coach", abbreviation="C")
|
||||
self.assertEqual(str(role), "Coach (C)")
|
||||
|
||||
def test_default_flags(self):
|
||||
role = TeamRole.objects.create(name="Player", abbreviation="P")
|
||||
self.assertFalse(role.staff_role)
|
||||
self.assertFalse(role.admin_role)
|
||||
self.assertEqual(role.sort_order, 10)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Team
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
class TeamStrTest(TestCase):
|
||||
def test_str(self):
|
||||
team = Team(name="Red Dragons")
|
||||
self.assertEqual(str(team), "Red Dragons")
|
||||
|
||||
|
||||
class TeamInitialsTest(TestCase):
|
||||
def test_single_word(self):
|
||||
self.assertEqual(Team(name="Falcons").initials, "F")
|
||||
|
||||
def test_two_words(self):
|
||||
self.assertEqual(Team(name="Red Dragons").initials, "RD")
|
||||
|
||||
def test_more_than_two_words_uses_first_two(self):
|
||||
self.assertEqual(Team(name="The Red Dragons").initials, "TR")
|
||||
|
||||
|
||||
class TeamGetShortNameTest(TestCase):
|
||||
def test_returns_short_name_when_set(self):
|
||||
team = Team(name="Red Dragons", short_name="RD")
|
||||
self.assertEqual(team.get_short_name(), "RD")
|
||||
|
||||
def test_falls_back_to_name_when_none(self):
|
||||
team = Team(name="Red Dragons", short_name=None)
|
||||
self.assertEqual(team.get_short_name(), "Red Dragons")
|
||||
|
||||
def test_falls_back_to_name_when_empty_string(self):
|
||||
team = Team(name="Red Dragons", short_name="")
|
||||
self.assertEqual(team.get_short_name(), "Red Dragons")
|
||||
|
||||
|
||||
class TeamMemberCountTest(TestCase):
|
||||
def setUp(self):
|
||||
today = timezone.now().date()
|
||||
self.season = Season.objects.create(
|
||||
start_date=today - datetime.timedelta(days=30),
|
||||
end_date=today + datetime.timedelta(days=335),
|
||||
)
|
||||
self.team = make_team()
|
||||
self.role = make_role()
|
||||
self.member1 = make_member("m1@test.com", "Alice", "One")
|
||||
self.member2 = make_member("m2@test.com", "Bob", "Two")
|
||||
|
||||
def test_member_count(self):
|
||||
TeamMembership.objects.create(team=self.team, member=self.member1, season=self.season, role=self.role)
|
||||
TeamMembership.objects.create(team=self.team, member=self.member2, season=self.season, role=self.role)
|
||||
self.assertEqual(self.team.member_count, 2)
|
||||
|
||||
def test_member_count_zero_when_empty(self):
|
||||
self.assertEqual(self.team.member_count, 0)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# TeamMembership
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
class TeamMembershipStrTest(TestCase):
|
||||
def setUp(self):
|
||||
today = timezone.now().date()
|
||||
self.season = Season.objects.create(
|
||||
start_date=today - datetime.timedelta(days=30),
|
||||
end_date=today + datetime.timedelta(days=335),
|
||||
)
|
||||
self.team = make_team("Blue Hawks")
|
||||
self.role = make_role("Forward", "F")
|
||||
self.member = make_member("player@test.com", "John", "Doe")
|
||||
self.membership = TeamMembership.objects.create(
|
||||
team=self.team, member=self.member, season=self.season, role=self.role
|
||||
)
|
||||
|
||||
def test_str(self):
|
||||
self.assertEqual(str(self.membership), "Blue Hawks - John Doe (F)")
|
||||
|
||||
def test_unique_number_constraint(self):
|
||||
from django.db import IntegrityError
|
||||
member2 = make_member("player2@test.com", "Jane", "Doe")
|
||||
TeamMembership.objects.create(team=self.team, member=member2, season=self.season, role=self.role, number=7)
|
||||
with self.assertRaises(IntegrityError):
|
||||
TeamMembership.objects.create(team=self.team, member=self.member, season=self.season, role=self.role, number=7)
|
||||
|
||||
def test_captain_default_false(self):
|
||||
self.assertFalse(self.membership.captain)
|
||||
self.assertFalse(self.membership.alternate_captain)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# TeamPicture
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
class TeamPictureStrTest(TestCase):
|
||||
def setUp(self):
|
||||
self.season = Season.objects.create(
|
||||
start_date=datetime.date(2025, 9, 1),
|
||||
end_date=datetime.date(2026, 8, 31),
|
||||
)
|
||||
self.team = make_team("Green Eagles")
|
||||
self.picture = TeamPicture(team=self.team, season=self.season, picture="teams/picture/green-eagles/2025/photo.jpg")
|
||||
|
||||
def test_str(self):
|
||||
self.assertEqual(str(self.picture), "Green Eagles - 2025")
|
||||
Reference in New Issue
Block a user