Files
int-layer/tests/services/test_notification_manager.py
2026-02-20 20:38:59 +00:00

188 lines
5.7 KiB
Python

"""Tests for NotificationManagerService."""
from unittest.mock import AsyncMock, Mock
import pytest
from capa_de_integracion.config import Settings
from capa_de_integracion.models.notification import ExternalNotificationRequest
from capa_de_integracion.services import DLPService, NotificationManagerService
from capa_de_integracion.services.storage import FirestoreService, RedisService
@pytest.fixture
def mock_settings():
"""Create mock settings."""
settings = Mock(spec=Settings)
settings.dlp_template_complete_flow = "test-template"
return settings
@pytest.fixture
def mock_redis():
"""Create mock Redis service."""
redis = Mock(spec=RedisService)
redis.save_or_append_notification = AsyncMock()
return redis
@pytest.fixture
def mock_firestore():
"""Create mock Firestore service."""
firestore = Mock(spec=FirestoreService)
firestore.save_or_append_notification = AsyncMock()
return firestore
@pytest.fixture
def mock_dlp():
"""Create mock DLP service."""
dlp = Mock(spec=DLPService)
dlp.get_obfuscated_string = AsyncMock(return_value="Obfuscated text")
return dlp
@pytest.fixture
def service(mock_settings, mock_redis, mock_firestore, mock_dlp):
"""Create NotificationManagerService instance."""
return NotificationManagerService(
settings=mock_settings,
redis_service=mock_redis,
firestore_service=mock_firestore,
dlp_service=mock_dlp,
)
@pytest.mark.asyncio
async def test_process_notification_basic(service, mock_redis, mock_dlp):
"""Test basic notification processing."""
request = ExternalNotificationRequest(
telefono="555-1234",
texto="Your card was blocked",
parametros_ocultos=None,
)
await service.process_notification(request)
# Verify DLP was called
mock_dlp.get_obfuscated_string.assert_called_once_with(
"Your card was blocked",
"test-template",
)
# Verify Redis save was called
mock_redis.save_or_append_notification.assert_called_once()
call_args = mock_redis.save_or_append_notification.call_args
notification = call_args[0][0]
assert notification.telefono == "555-1234"
assert notification.texto == "Obfuscated text"
assert notification.status == "active"
assert notification.nombre_evento_dialogflow == "notificacion"
@pytest.mark.asyncio
async def test_process_notification_with_parameters(service, mock_redis, mock_dlp):
"""Test notification processing with hidden parameters."""
request = ExternalNotificationRequest(
telefono="555-1234",
texto="Transaction alert",
parametros_ocultos={
"amount": "100.00",
"merchant": "Store ABC",
},
)
await service.process_notification(request)
# Verify Redis save was called
mock_redis.save_or_append_notification.assert_called_once()
notification = mock_redis.save_or_append_notification.call_args[0][0]
# Verify parameters have prefix
assert "notification_po_amount" in notification.parametros
assert notification.parametros["notification_po_amount"] == "100.00"
assert "notification_po_merchant" in notification.parametros
assert notification.parametros["notification_po_merchant"] == "Store ABC"
@pytest.mark.asyncio
async def test_process_notification_firestore_async(service, mock_redis, mock_firestore):
"""Test that Firestore save is asynchronous (fire-and-forget)."""
request = ExternalNotificationRequest(
telefono="555-1234",
texto="Test notification",
parametros_ocultos=None,
)
await service.process_notification(request)
# Redis should be called immediately
mock_redis.save_or_append_notification.assert_called_once()
# Firestore may or may not be called yet (it's async)
# We can't easily test the fire-and-forget behavior without waiting
@pytest.mark.asyncio
async def test_process_notification_empty_parameters(service, mock_redis):
"""Test notification processing with empty parameters."""
request = ExternalNotificationRequest(
telefono="555-1234",
texto="Test",
parametros_ocultos={},
)
await service.process_notification(request)
notification = mock_redis.save_or_append_notification.call_args[0][0]
assert notification.parametros == {}
@pytest.mark.asyncio
async def test_process_notification_generates_unique_id(service, mock_redis):
"""Test that each notification gets a unique ID."""
request = ExternalNotificationRequest(
telefono="555-1234",
texto="Test",
parametros_ocultos=None,
)
await service.process_notification(request)
notification1 = mock_redis.save_or_append_notification.call_args[0][0]
await service.process_notification(request)
notification2 = mock_redis.save_or_append_notification.call_args[0][0]
assert notification1.id_notificacion != notification2.id_notificacion
@pytest.mark.asyncio
async def test_process_notification_firestore_exception_handling(
service, mock_redis, mock_firestore
):
"""Test that Firestore exceptions are handled gracefully in background task."""
import asyncio
# Make Firestore save fail
mock_firestore.save_or_append_notification = AsyncMock(
side_effect=Exception("Firestore connection error")
)
request = ExternalNotificationRequest(
telefono="555-1234",
texto="Test notification",
parametros_ocultos=None,
)
await service.process_notification(request)
# Redis should succeed
mock_redis.save_or_append_notification.assert_called_once()
# Give the background task time to execute
await asyncio.sleep(0.1)
# Firestore should have been attempted (and failed)
mock_firestore.save_or_append_notification.assert_called_once()