Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions src/comms/migrations/0011_fix_news_item_ordering.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# Generated by Django 4.2.20 on 2026-03-31 19:54

from django.db import migrations


class Migration(migrations.Migration):
dependencies = [
("comms", "0010_historicalnewsitem_body_cy_and_more"),
]

operations = [
migrations.AlterModelOptions(
name="newsitem",
options={"ordering": ("-pinned", "sequence", "-start_display", "title")},
),
]
2 changes: 1 addition & 1 deletion src/comms/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ class NewsItem(models.Model):
active_objects = ActiveNewsItemManager()

class Meta:
ordering = ("pinned", "-posted", "title")
ordering = ("-pinned", "sequence", "-start_display", "title")

@property
def url(self):
Expand Down
107 changes: 106 additions & 1 deletion src/comms/tests.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
from django.test import TestCase
import datetime

from django.test import TestCase, override_settings
from django.urls import reverse
from django.contrib.contenttypes.models import ContentType
from django.utils import timezone
Expand Down Expand Up @@ -116,3 +118,106 @@ def test_manage_news_delete_image(self):
core_models.File.objects.filter(pk=image_file.pk).exists(),
msg="File was not deleted as expected.",
)


class NewsItemOrderingTest(TestCase):
@classmethod
def setUpTestData(cls):
cls.press = helpers.create_press()
cls.journal_one, cls.journal_two = helpers.create_journals()
cls.content_type = ContentType.objects.get_for_model(cls.journal_one)

today = datetime.date.today()
yesterday = today - datetime.timedelta(days=1)
last_week = today - datetime.timedelta(days=7)

cls.item_pinned = models.NewsItem.objects.create(
content_type=cls.content_type,
object_id=cls.journal_one.pk,
title="Pinned Item",
body="body",
start_display=yesterday,
pinned=True,
)
cls.item_seq_1 = models.NewsItem.objects.create(
content_type=cls.content_type,
object_id=cls.journal_one.pk,
title="Sequence 1",
body="body",
start_display=today,
sequence=1,
)
cls.item_seq_2 = models.NewsItem.objects.create(
content_type=cls.content_type,
object_id=cls.journal_one.pk,
title="Sequence 2",
body="body",
start_display=last_week,
sequence=2,
)
# Two items sharing the same sequence and start_display — title breaks the tie.
cls.item_title_apple = models.NewsItem.objects.create(
content_type=cls.content_type,
object_id=cls.journal_one.pk,
title="Apple",
body="body",
start_display=last_week,
sequence=3,
)
cls.item_title_zebra = models.NewsItem.objects.create(
content_type=cls.content_type,
object_id=cls.journal_one.pk,
title="Zebra",
body="body",
start_display=last_week,
sequence=3,
)
cls.url = reverse("core_news_list")

@override_settings(URL_CONFIG="domain")
def test_pinned_item_appears_first(self):
response = self.client.get(self.url, SERVER_NAME=self.journal_one.domain)
items = list(response.context["news_items"])
self.assertEqual(items[0], self.item_pinned)

@override_settings(URL_CONFIG="domain")
def test_sequence_respected_after_pinned(self):
response = self.client.get(self.url, SERVER_NAME=self.journal_one.domain)
items = list(response.context["news_items"])
self.assertEqual(items[1], self.item_seq_1)
self.assertEqual(items[2], self.item_seq_2)

@override_settings(URL_CONFIG="domain")
def test_ordering_uses_start_display_not_posted(self):
# item_seq_2 has an older start_display but was created after item_seq_1.
# Sequence drives order so seq_1 (sequence=1) precedes seq_2 (sequence=2),
# confirming posted timestamp is not used.
response = self.client.get(self.url, SERVER_NAME=self.journal_one.domain)
items = [i for i in response.context["news_items"] if not i.pinned]
self.assertEqual(items[0], self.item_seq_1)
self.assertEqual(items[1], self.item_seq_2)

@override_settings(URL_CONFIG="domain")
def test_same_sequence_and_start_display_orders_by_title(self):
# item_title_apple and item_title_zebra share sequence=3 and start_display,
# so title alphabetical order is the tiebreaker.
response = self.client.get(self.url, SERVER_NAME=self.journal_one.domain)
items = [i for i in response.context["news_items"] if i.sequence == 3]
self.assertEqual(items[0], self.item_title_apple)
self.assertEqual(items[1], self.item_title_zebra)

@override_settings(URL_CONFIG="domain")
def test_higher_sequence_with_newer_start_display_sorts_after_lower_sequence(self):
# item_seq_1 (sequence=1, start_display=today) must appear before
# item_seq_2 (sequence=2, start_display=last_week) even though a naive
# date-first sort would put last_week after today.
response = self.client.get(self.url, SERVER_NAME=self.journal_one.domain)
items = [
i
for i in response.context["news_items"]
if not i.pinned and i.sequence in (1, 2)
]
self.assertLess(
items.index(self.item_seq_1),
items.index(self.item_seq_2),
)
Loading