test: migrate backend suites to pytest with module coverage gate
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.
This commit is contained in:
64
apps/notifications/tests/unit/test_store.py
Normal file
64
apps/notifications/tests/unit/test_store.py
Normal file
@@ -0,0 +1,64 @@
|
||||
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()
|
||||
Reference in New Issue
Block a user