News and NewsPhoto (team-tagged instead of categorised, one photo taggable as main via a partial unique constraint), gated per club/services/access.py: any current-season coach_manager, EDITOR, or ADMIN can draft and edit a news item; only EDITOR/ADMIN can publish it, or edit it once it's live. Publishing takes a date so it can be scheduled ahead of time rather than only right now. Authoring/release only for now -- no member-facing reading page or public API yet, the visibility field (internal/external/both) is there for when those land.
101 lines
3.7 KiB
Python
101 lines
3.7 KiB
Python
import datetime
|
|
|
|
from django.core.files.uploadedfile import SimpleUploadedFile
|
|
from django.db import IntegrityError
|
|
from django.test import TestCase
|
|
from django.utils import timezone
|
|
|
|
from club.models import Club
|
|
|
|
from .models import News, NewsPhoto
|
|
|
|
|
|
def make_photo(news_item, *, is_main=False):
|
|
image = SimpleUploadedFile("photo.jpg", b"fake-image-bytes", content_type="image/jpeg")
|
|
return NewsPhoto.objects.create(news_item=news_item, image=image, is_main=is_main)
|
|
|
|
|
|
class NewsModelTests(TestCase):
|
|
def setUp(self):
|
|
self.club = Club.objects.create(name="Ajax United", slug="ajax-united")
|
|
|
|
def test_slug_is_derived_from_title(self):
|
|
item = News.objects.create(club=self.club, title="Season Kickoff", body="Body text.")
|
|
|
|
self.assertEqual(item.slug, "season-kickoff")
|
|
|
|
def test_slug_is_unique_per_club_not_globally(self):
|
|
News.objects.create(club=self.club, title="Season Kickoff", body="First.")
|
|
second = News.objects.create(club=self.club, title="Season Kickoff", body="Second.")
|
|
|
|
self.assertEqual(second.slug, "season-kickoff-2")
|
|
|
|
def test_two_clubs_can_share_the_same_slug(self):
|
|
other_club = Club.objects.create(name="Rival FC", slug="rival-fc")
|
|
News.objects.create(club=self.club, title="Season Kickoff", body="First.")
|
|
|
|
other = News.objects.create(club=other_club, title="Season Kickoff", body="Other club.")
|
|
|
|
self.assertEqual(other.slug, "season-kickoff")
|
|
|
|
def test_defaults_to_draft_and_internal(self):
|
|
item = News.objects.create(club=self.club, title="Draft item", body="Body.")
|
|
|
|
self.assertEqual(item.status, News.Status.DRAFT)
|
|
self.assertEqual(item.visibility, News.Visibility.INTERNAL)
|
|
self.assertIsNone(item.published_at)
|
|
|
|
def test_publish_defaults_the_publish_date_to_now(self):
|
|
item = News.objects.create(club=self.club, title="Item", body="Body.")
|
|
|
|
item.publish()
|
|
|
|
self.assertEqual(item.status, News.Status.PUBLISHED)
|
|
self.assertIsNotNone(item.published_at)
|
|
self.assertFalse(item.is_scheduled)
|
|
|
|
def test_publish_accepts_a_future_date_and_is_scheduled(self):
|
|
item = News.objects.create(club=self.club, title="Item", body="Body.")
|
|
future = timezone.now() + datetime.timedelta(days=7)
|
|
|
|
item.publish(at=future)
|
|
|
|
self.assertEqual(item.status, News.Status.PUBLISHED)
|
|
self.assertEqual(item.published_at, future)
|
|
self.assertTrue(item.is_scheduled)
|
|
|
|
def test_unpublish_clears_the_publish_date(self):
|
|
item = News.objects.create(club=self.club, title="Item", body="Body.")
|
|
item.publish()
|
|
|
|
item.unpublish()
|
|
|
|
self.assertEqual(item.status, News.Status.DRAFT)
|
|
self.assertIsNone(item.published_at)
|
|
|
|
|
|
class NewsPhotoModelTests(TestCase):
|
|
def setUp(self):
|
|
self.club = Club.objects.create(name="Ajax United", slug="ajax-united")
|
|
self.item = News.objects.create(club=self.club, title="Match report", body="Body.")
|
|
|
|
def test_a_second_main_photo_is_rejected_at_the_database_level(self):
|
|
make_photo(self.item, is_main=True)
|
|
|
|
with self.assertRaises(IntegrityError):
|
|
make_photo(self.item, is_main=True)
|
|
|
|
def test_two_non_main_photos_are_fine(self):
|
|
make_photo(self.item, is_main=False)
|
|
make_photo(self.item, is_main=False)
|
|
|
|
self.assertEqual(self.item.photos.count(), 2)
|
|
|
|
def test_two_different_news_items_can_each_have_a_main_photo(self):
|
|
other_item = News.objects.create(club=self.club, title="Other item", body="Body.")
|
|
|
|
make_photo(self.item, is_main=True)
|
|
make_photo(other_item, is_main=True)
|
|
|
|
self.assertEqual(NewsPhoto.objects.filter(is_main=True).count(), 2)
|