188 lines
5.7 KiB
Python
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()
|