Split backend tests into unit and integration suites, add isolated fixtures and coverage enforcement, and update CI and test documentation. Cover current authentication, content, notification, payment, media, and admin flows; fix behaviors revealed by the new tests. Validate both suites and the per-module gate against local PostgreSQL.
65 lines
3.6 KiB
Python
65 lines
3.6 KiB
Python
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()
|