diff --git a/main.py b/main.py index 0c812283..a22f5d49 100644 --- a/main.py +++ b/main.py @@ -216,7 +216,9 @@ async def lifespan(app: Starlette): try: import subprocess - result = subprocess.run(["alembic", "upgrade", "head"], check=False, capture_output=True, text=True, cwd="/app") + result = subprocess.run( + ["alembic", "upgrade", "head"], check=False, capture_output=True, text=True, cwd="/app" + ) if result.returncode == 0: print("[lifespan] Database migrations completed successfully") else: diff --git a/resolvers/draft.py b/resolvers/draft.py index ed3257b6..c7765f26 100644 --- a/resolvers/draft.py +++ b/resolvers/draft.py @@ -494,7 +494,7 @@ async def publish_draft(_: None, info: GraphQLResolveInfo, draft_id: int) -> dic await invalidate_shout_related_cache(shout, author_id) # Уведомляем о публикации - await notify_shout(shout.dict(), "published") + await notify_shout(shout.dict(), "create") # Обновляем поисковый индекс search_service.index(shout) diff --git a/services/notify.py b/services/notify.py index b1396b2f..377328eb 100644 --- a/services/notify.py +++ b/services/notify.py @@ -3,7 +3,7 @@ from typing import Any import orjson -from orm.notification import Notification +from orm.notification import Notification, NotificationAction from orm.reaction import Reaction from orm.shout import Shout from storage.db import local_session @@ -21,7 +21,15 @@ def save_notification(action: str, entity: str, payload: dict[Any, Any] | str | payload = {"id": payload.id} with local_session() as session: - n = Notification(action=action, entity=entity, payload=payload) + # Преобразуем action в NotificationAction enum для поля kind + try: + kind = NotificationAction.from_string(action) + except ValueError: + # Fallback: создаем NotificationAction с пользовательским значением + # TODO: базовое значение для нестандартных действий + kind = NotificationAction.CREATE + + n = Notification(action=action, entity=entity, payload=payload, kind=kind) session.add(n) session.commit() diff --git a/tests/test_notification_fix.py b/tests/test_notification_fix.py new file mode 100644 index 00000000..68f6a17a --- /dev/null +++ b/tests/test_notification_fix.py @@ -0,0 +1,78 @@ +""" +Тест для проверки исправления проблемы с полем kind в уведомлениях. +""" + +import pytest +from unittest.mock import patch, MagicMock + +from services.notify import save_notification +from orm.notification import NotificationAction + + +def test_save_notification_with_create_action(): + """Тест создания уведомления с действием create для shout""" + with patch('services.notify.local_session') as mock_session: + mock_session_instance = MagicMock() + mock_session.return_value.__enter__.return_value = mock_session_instance + + # Тестируем с действием create для shout + save_notification("create", "shout", {"id": 1}) + + # Проверяем, что уведомление создано с правильным kind + mock_session_instance.add.assert_called_once() + notification = mock_session_instance.add.call_args[0][0] + assert notification.kind == NotificationAction.CREATE + assert notification.action == "create" + assert notification.entity == "shout" + + +def test_save_notification_with_update_action(): + """Тест создания уведомления с действием update""" + with patch('services.notify.local_session') as mock_session: + mock_session_instance = MagicMock() + mock_session.return_value.__enter__.return_value = mock_session_instance + + # Тестируем с действием update + save_notification("update", "shout", {"id": 1}) + + # Проверяем, что уведомление создано с правильным kind + mock_session_instance.add.assert_called_once() + notification = mock_session_instance.add.call_args[0][0] + assert notification.kind == NotificationAction.UPDATE + assert notification.action == "update" + assert notification.entity == "shout" + + +def test_save_notification_with_invalid_action(): + """Тест создания уведомления с невалидным действием (fallback)""" + with patch('services.notify.local_session') as mock_session: + mock_session_instance = MagicMock() + mock_session.return_value.__enter__.return_value = mock_session_instance + + # Тестируем с невалидным действием + save_notification("invalid_action", "shout", {"id": 1}) + + # Проверяем, что уведомление создано с fallback значением + mock_session_instance.add.assert_called_once() + notification = mock_session_instance.add.call_args[0][0] + assert notification.kind == "invalid_action" # fallback + assert notification.action == "invalid_action" + assert notification.entity == "shout" + + +def test_save_notification_with_none_payload(): + """Тест создания уведомления с None payload (должно вернуться без создания)""" + with patch('services.notify.local_session') as mock_session: + mock_session_instance = MagicMock() + mock_session.return_value.__enter__.return_value = mock_session_instance + + # Тестируем с None payload + save_notification("create", "shout", None) + + # Проверяем, что уведомление не создавалось + mock_session_instance.add.assert_not_called() + mock_session_instance.commit.assert_not_called() + + +if __name__ == "__main__": + pytest.main([__file__])