FossilRepo

fossilrepo / tests / test_notification_prefs.py
Source Blame History 244 lines
c588255… ragelink 1 from unittest.mock import patch
c588255… ragelink 2
c588255… ragelink 3 import pytest
c588255… ragelink 4 from django.contrib.auth.models import User
c588255… ragelink 5
c588255… ragelink 6 from fossil.notifications import Notification, NotificationPreference
c588255… ragelink 7
c588255… ragelink 8 # --- NotificationPreference Model Tests ---
c588255… ragelink 9
c588255… ragelink 10
c588255… ragelink 11 @pytest.mark.django_db
c588255… ragelink 12 class TestNotificationPreferenceModel:
c588255… ragelink 13 def test_create_preference(self, admin_user):
c588255… ragelink 14 pref = NotificationPreference.objects.create(user=admin_user)
c588255… ragelink 15 assert pref.pk is not None
c588255… ragelink 16 assert pref.delivery_mode == "immediate"
c588255… ragelink 17 assert pref.notify_checkins is True
c588255… ragelink 18 assert pref.notify_tickets is True
c588255… ragelink 19 assert pref.notify_wiki is True
c588255… ragelink 20 assert pref.notify_releases is True
c588255… ragelink 21 assert pref.notify_forum is False
c588255… ragelink 22
c588255… ragelink 23 def test_str_repr(self, admin_user):
c588255… ragelink 24 pref = NotificationPreference.objects.create(user=admin_user, delivery_mode="daily")
c588255… ragelink 25 assert str(pref) == "admin: daily"
c588255… ragelink 26
c588255… ragelink 27 def test_one_to_one_constraint(self, admin_user):
c588255… ragelink 28 NotificationPreference.objects.create(user=admin_user)
c588255… ragelink 29 from django.db import IntegrityError
c588255… ragelink 30
c588255… ragelink 31 with pytest.raises(IntegrityError):
c588255… ragelink 32 NotificationPreference.objects.create(user=admin_user)
c588255… ragelink 33
c588255… ragelink 34 def test_delivery_mode_choices(self, admin_user):
c588255… ragelink 35 for mode in ["immediate", "daily", "weekly", "off"]:
c588255… ragelink 36 pref, _ = NotificationPreference.objects.update_or_create(user=admin_user, defaults={"delivery_mode": mode})
c588255… ragelink 37 pref.refresh_from_db()
c588255… ragelink 38 assert pref.delivery_mode == mode
c588255… ragelink 39
c588255… ragelink 40
c588255… ragelink 41 # --- Notification Preferences View Tests ---
c588255… ragelink 42
c588255… ragelink 43
c588255… ragelink 44 @pytest.mark.django_db
c588255… ragelink 45 class TestNotificationPreferencesView:
c588255… ragelink 46 def test_get_creates_default_prefs(self, admin_client, admin_user):
c588255… ragelink 47 assert not NotificationPreference.objects.filter(user=admin_user).exists()
c588255… ragelink 48 response = admin_client.get("/auth/notifications/")
c588255… ragelink 49 assert response.status_code == 200
c588255… ragelink 50 assert "Notification Preferences" in response.content.decode()
c588255… ragelink 51 assert NotificationPreference.objects.filter(user=admin_user).exists()
c588255… ragelink 52
c588255… ragelink 53 def test_get_renders_existing_prefs(self, admin_client, admin_user):
c588255… ragelink 54 NotificationPreference.objects.create(user=admin_user, delivery_mode="daily", notify_forum=True)
c588255… ragelink 55 response = admin_client.get("/auth/notifications/")
c588255… ragelink 56 assert response.status_code == 200
c588255… ragelink 57 content = response.content.decode()
c588255… ragelink 58 assert "Notification Preferences" in content
c588255… ragelink 59
c588255… ragelink 60 def test_post_updates_delivery_mode(self, admin_client, admin_user):
c588255… ragelink 61 NotificationPreference.objects.create(user=admin_user)
c588255… ragelink 62 response = admin_client.post(
c588255… ragelink 63 "/auth/notifications/",
c588255… ragelink 64 {
c588255… ragelink 65 "delivery_mode": "daily",
c588255… ragelink 66 "notify_checkins": "on",
c588255… ragelink 67 "notify_tickets": "on",
c588255… ragelink 68 },
c588255… ragelink 69 )
c588255… ragelink 70 assert response.status_code == 302
c588255… ragelink 71 pref = NotificationPreference.objects.get(user=admin_user)
c588255… ragelink 72 assert pref.delivery_mode == "daily"
c588255… ragelink 73 assert pref.notify_checkins is True
c588255… ragelink 74 assert pref.notify_tickets is True
c588255… ragelink 75 assert pref.notify_wiki is False
c588255… ragelink 76 assert pref.notify_releases is False
c588255… ragelink 77 assert pref.notify_forum is False
c588255… ragelink 78
c588255… ragelink 79 def test_post_updates_event_toggles(self, admin_client, admin_user):
c588255… ragelink 80 NotificationPreference.objects.create(user=admin_user)
c588255… ragelink 81 response = admin_client.post(
c588255… ragelink 82 "/auth/notifications/",
c588255… ragelink 83 {
c588255… ragelink 84 "delivery_mode": "weekly",
c588255… ragelink 85 "notify_checkins": "on",
c588255… ragelink 86 "notify_tickets": "on",
c588255… ragelink 87 "notify_wiki": "on",
c588255… ragelink 88 "notify_releases": "on",
c588255… ragelink 89 "notify_forum": "on",
c588255… ragelink 90 },
c588255… ragelink 91 )
c588255… ragelink 92 assert response.status_code == 302
c588255… ragelink 93 pref = NotificationPreference.objects.get(user=admin_user)
c588255… ragelink 94 assert pref.delivery_mode == "weekly"
c588255… ragelink 95 assert pref.notify_checkins is True
c588255… ragelink 96 assert pref.notify_tickets is True
c588255… ragelink 97 assert pref.notify_wiki is True
c588255… ragelink 98 assert pref.notify_releases is True
c588255… ragelink 99 assert pref.notify_forum is True
c588255… ragelink 100
c588255… ragelink 101 def test_post_turn_off(self, admin_client, admin_user):
c588255… ragelink 102 NotificationPreference.objects.create(user=admin_user, delivery_mode="daily")
c588255… ragelink 103 response = admin_client.post(
c588255… ragelink 104 "/auth/notifications/",
c588255… ragelink 105 {
c588255… ragelink 106 "delivery_mode": "off",
c588255… ragelink 107 },
c588255… ragelink 108 )
c588255… ragelink 109 assert response.status_code == 302
c588255… ragelink 110 pref = NotificationPreference.objects.get(user=admin_user)
c588255… ragelink 111 assert pref.delivery_mode == "off"
c588255… ragelink 112 # All unchecked checkboxes default to False
c588255… ragelink 113 assert pref.notify_checkins is False
c588255… ragelink 114 assert pref.notify_tickets is False
c588255… ragelink 115
c588255… ragelink 116 def test_denied_for_anon(self, client):
c588255… ragelink 117 response = client.get("/auth/notifications/")
c588255… ragelink 118 assert response.status_code == 302 # redirect to login
c588255… ragelink 119
c588255… ragelink 120
c588255… ragelink 121 # --- Digest Task Tests ---
c588255… ragelink 122
c588255… ragelink 123
c588255… ragelink 124 @pytest.mark.django_db
c588255… ragelink 125 class TestSendDigestTask:
c588255… ragelink 126 @pytest.fixture
c588255… ragelink 127 def daily_user(self, db):
c588255… ragelink 128 user = User.objects.create_user(username="dailyuser", email="[email protected]", password="testpass123")
c588255… ragelink 129 NotificationPreference.objects.create(user=user, delivery_mode="daily")
c588255… ragelink 130 return user
c588255… ragelink 131
c588255… ragelink 132 @pytest.fixture
c588255… ragelink 133 def weekly_user(self, db):
c588255… ragelink 134 user = User.objects.create_user(username="weeklyuser", email="[email protected]", password="testpass123")
c588255… ragelink 135 NotificationPreference.objects.create(user=user, delivery_mode="weekly")
c588255… ragelink 136 return user
c588255… ragelink 137
c588255… ragelink 138 @pytest.fixture
c588255… ragelink 139 def immediate_user(self, db):
c588255… ragelink 140 user = User.objects.create_user(username="immediateuser", email="[email protected]", password="testpass123")
c588255… ragelink 141 NotificationPreference.objects.create(user=user, delivery_mode="immediate")
c588255… ragelink 142 return user
c588255… ragelink 143
c588255… ragelink 144 def test_daily_digest_sends_email(self, daily_user, sample_project):
c588255… ragelink 145 # Create unread notifications
c588255… ragelink 146 for i in range(3):
c588255… ragelink 147 Notification.objects.create(
c588255… ragelink 148 user=daily_user,
c588255… ragelink 149 project=sample_project,
c588255… ragelink 150 event_type="checkin",
c588255… ragelink 151 title=f"Commit #{i}",
c588255… ragelink 152 )
c588255… ragelink 153
c588255… ragelink 154 from fossil.tasks import send_digest
c588255… ragelink 155
c588255… ragelink 156 with patch("django.core.mail.send_mail") as mock_send:
c588255… ragelink 157 send_digest.apply(kwargs={"mode": "daily"})
c588255… ragelink 158
c588255… ragelink 159 mock_send.assert_called_once()
c588255… ragelink 160 call_kwargs = mock_send.call_args
c588255… ragelink 161 assert "Daily" in call_kwargs[1]["subject"] or "Daily" in call_kwargs[0][0]
c588255… ragelink 162 assert daily_user.email in (call_kwargs[1].get("recipient_list") or call_kwargs[0][3])
c588255… ragelink 163
c588255… ragelink 164 # Notifications marked as read
c588255… ragelink 165 assert Notification.objects.filter(user=daily_user, read=False).count() == 0
c588255… ragelink 166
c588255… ragelink 167 def test_weekly_digest_sends_email(self, weekly_user, sample_project):
c588255… ragelink 168 Notification.objects.create(
c588255… ragelink 169 user=weekly_user,
c588255… ragelink 170 project=sample_project,
c588255… ragelink 171 event_type="ticket",
c588255… ragelink 172 title="New ticket",
c588255… ragelink 173 )
c588255… ragelink 174
c588255… ragelink 175 from fossil.tasks import send_digest
c588255… ragelink 176
c588255… ragelink 177 with patch("django.core.mail.send_mail") as mock_send:
c588255… ragelink 178 send_digest.apply(kwargs={"mode": "weekly"})
c588255… ragelink 179
c588255… ragelink 180 mock_send.assert_called_once()
c588255… ragelink 181
c588255… ragelink 182 def test_no_email_for_immediate_users(self, immediate_user, sample_project):
c588255… ragelink 183 Notification.objects.create(
c588255… ragelink 184 user=immediate_user,
c588255… ragelink 185 project=sample_project,
c588255… ragelink 186 event_type="checkin",
c588255… ragelink 187 title="Commit",
c588255… ragelink 188 )
c588255… ragelink 189
c588255… ragelink 190 from fossil.tasks import send_digest
c588255… ragelink 191
c588255… ragelink 192 with patch("django.core.mail.send_mail") as mock_send:
c588255… ragelink 193 send_digest.apply(kwargs={"mode": "daily"})
c588255… ragelink 194
c588255… ragelink 195 mock_send.assert_not_called()
c588255… ragelink 196
c588255… ragelink 197 def test_no_email_when_no_unread(self, daily_user, sample_project):
c588255… ragelink 198 # Create read notifications
c588255… ragelink 199 Notification.objects.create(
c588255… ragelink 200 user=daily_user,
c588255… ragelink 201 project=sample_project,
c588255… ragelink 202 event_type="checkin",
c588255… ragelink 203 title="Old commit",
c588255… ragelink 204 read=True,
c588255… ragelink 205 )
c588255… ragelink 206
c588255… ragelink 207 from fossil.tasks import send_digest
c588255… ragelink 208
c588255… ragelink 209 with patch("django.core.mail.send_mail") as mock_send:
c588255… ragelink 210 send_digest.apply(kwargs={"mode": "daily"})
c588255… ragelink 211
c588255… ragelink 212 mock_send.assert_not_called()
c588255… ragelink 213
c588255… ragelink 214 def test_digest_limits_to_50_notifications(self, daily_user, sample_project):
c588255… ragelink 215 for i in range(55):
c588255… ragelink 216 Notification.objects.create(
c588255… ragelink 217 user=daily_user,
c588255… ragelink 218 project=sample_project,
c588255… ragelink 219 event_type="checkin",
c588255… ragelink 220 title=f"Commit #{i}",
c588255… ragelink 221 )
c588255… ragelink 222
c588255… ragelink 223 from fossil.tasks import send_digest
c588255… ragelink 224
c588255… ragelink 225 with patch("django.core.mail.send_mail") as mock_send:
c588255… ragelink 226 send_digest.apply(kwargs={"mode": "daily"})
c588255… ragelink 227
c588255… ragelink 228 mock_send.assert_called_once()
c588255… ragelink 229 call_args = mock_send.call_args
c588255… ragelink 230 message = call_args[1].get("message") or call_args[0][1]
c588255… ragelink 231 assert "55 new notifications" in message
c588255… ragelink 232 assert "and 5 more" in message
c588255… ragelink 233
c588255… ragelink 234 # All 55 marked as read
c588255… ragelink 235 assert Notification.objects.filter(user=daily_user, read=False).count() == 0
c588255… ragelink 236
c588255… ragelink 237 def test_digest_no_users_with_mode(self):
c588255… ragelink 238 """When no users have the requested delivery mode, task completes without error."""
c588255… ragelink 239 from fossil.tasks import send_digest
c588255… ragelink 240
c588255… ragelink 241 with patch("django.core.mail.send_mail") as mock_send:
c588255… ragelink 242 send_digest.apply(kwargs={"mode": "daily"})
c588255… ragelink 243
c588255… ragelink 244 mock_send.assert_not_called()

Keyboard Shortcuts

Open search /
Next entry (timeline) j
Previous entry (timeline) k
Open focused entry Enter
Show this help ?
Toggle theme Top nav button