import pytest import json from types import SimpleNamespace from django.contrib import admin from django.test import RequestFactory from apps.notifications.api.views import get_notification, list_notifications from apps.notifications.services import RedisNotificationStore from apps.notifications.tasks import send_manual_notification from apps.users.admin import ManualNotificationForm, UserAdmin from apps.users.models import User from core.authentication import create_jwt_token @pytest.mark.unit class TestNotificationStoreTests: def test_summary_hides_full_body_and_advertises_it(self): notification = RedisNotificationStore._normalize_notification({'id': 'one', 'title': 'Title', 'message': 'Digest', 'body_markdown': '# Full body'}) summary = RedisNotificationStore.summary(notification) assert summary['has_body'] assert 'body_markdown' not in summary assert notification['body_markdown'] == '# Full body' def test_read_filter_applies_before_pagination(self, mocker): redis_client = mocker.patch('apps.notifications.services.redis_client') redis_client.zrevrange.return_value = ['one', 'two', 'three'] redis_client.pipeline.return_value.execute.return_value = [json.dumps({'id': 'one', 'is_seen': False}), json.dumps({'id': 'two', 'is_seen': True}), json.dumps({'id': 'three', 'is_seen': False})] unread, count = RedisNotificationStore.list('user', limit=1, offset=1, read_filter='unread') assert count == 2 assert [item['id'] for item in unread] == ['three'] read, count = RedisNotificationStore.list('user', read_filter='read') assert count == 1 assert read[0]['id'] == 'two' def test_detail_is_scoped_to_authenticated_recipient(self, mocker): store = mocker.patch('apps.notifications.api.views.RedisNotificationStore') store.get.side_effect = lambda user_id, _: {'id': 'one', 'body_markdown': '# Secret'} if user_id == '1' else None owner = SimpleNamespace(auth=SimpleNamespace(id=1)) other = SimpleNamespace(auth=SimpleNamespace(id=2)) assert get_notification(owner, 'one')[0] == 200 assert get_notification(other, 'one')[0] == 404 store.get.assert_any_call('2', 'one') def test_list_returns_summaries_and_rejects_unknown_status(self, mocker): store = mocker.patch('apps.notifications.api.views.RedisNotificationStore') request = SimpleNamespace(auth=SimpleNamespace(id=1)) store.list.return_value = ([{'id': 'one', 'body_markdown': '# Secret'}], 1) store.summary.side_effect = RedisNotificationStore.summary store.unread_count.return_value = 1 result = list_notifications(request, status='unread') assert 'body_markdown' not in result['notifications'][0] assert store.list.call_args.kwargs['read_filter'] == 'unread' assert list_notifications(request, status='invalid').status_code == 400 def test_marking_old_ephemeral_notification_read_keeps_it_visible(self, mocker): redis_client = mocker.patch('apps.notifications.services.redis_client') get = mocker.patch.object(RedisNotificationStore, 'get') _count = mocker.patch.object(RedisNotificationStore, 'unread_count', return_value=0) _publish = mocker.patch.object(RedisNotificationStore, '_publish_event') get.return_value = {'id': 'one', 'is_seen': False, 'delete_on_seen': True, 'body_markdown': '# Body'} result = RedisNotificationStore.mark_seen('user', 'one') assert not result['deleted'] assert result['notification']['is_seen'] redis_client.hset.assert_called_once() redis_client.pipeline.assert_not_called()