Files
compass_backoffice/tests_original_develop/unit/test_imdb_enrichment.py
2026-06-13 08:23:21 -03:00

188 lines
7.5 KiB
Python

import pytest
from unittest.mock import patch, AsyncMock
from src.agent.nodes.imdb_enrichment_node import imdb_enrich_ticket, should_continue
from src.components.clients.exceptions.imdb_exceptions import (
ImdbClientError,
ImdbHttpError,
ImdbTimeoutError,
ImdbConnectionError,
)
# ---------------------------------------------------------------------------
# Helpers
# ---------------------------------------------------------------------------
def make_state(request_context=None, session_id="sess-123"):
"""Build a minimal AgentState for tests."""
return {
"messages": [],
"current_step": "start",
"iteration_count": 0,
"tool_results": None,
"final_response": None,
"session_id": session_id,
"metadata": {"request_context": request_context} if request_context else {},
"error": None,
}
def _valid_context(cpf_cnpj="12345678901", msisdn="5511999999999"):
return {
"customer": {
"cpfCnpj": cpf_cnpj,
"msisdn": msisdn,
}
}
MOCK_IMDB_RESPONSE = {
"plan": "TIM Black",
"statusType": "Active",
"statusDescription": "Acesso Full",
"socialSecNo": "06252533106",
}
IMDB_CLIENT_PATH = "src.agent.nodes.imdb_enrichment_node.ImdbClient"
# ---------------------------------------------------------------------------
# Tests
# ---------------------------------------------------------------------------
@pytest.mark.unit
@pytest.mark.asyncio
class TestImdbEnrichmentNode:
# --- Happy path ---------------------------------------------------------
@patch(IMDB_CLIENT_PATH)
async def test_imdb_enrichment_node_success(self, mock_client_cls):
"""Node should store imdb_access_data in context and set step to 'imdb_enrichment_completed'."""
# Arrange
mock_client = AsyncMock()
mock_client.get_imdb_access_data_with_retry.return_value = AsyncMock(model_dump=lambda: MOCK_IMDB_RESPONSE)
mock_client_cls.return_value = mock_client
state = make_state(request_context=_valid_context())
# Act
final_state = await imdb_enrich_ticket(state)
# Assert
assert final_state["current_step"] == "imdb_enrichment_completed"
assert final_state["error"] is None
imdb_data = final_state["metadata"]["request_context"]["imdb_access_data"]
assert imdb_data["plan"] == "TIM Black"
assert imdb_data["statusType"] == "Active"
assert imdb_data["cpf_cnpj"] == "12345678901"
assert imdb_data["socialSecNo"] == "06252533106"
mock_client.get_imdb_access_data_with_retry.assert_called_once()
# --- Validation errors --------------------------------------------------
async def test_imdb_enrichment_node_missing_context(self):
"""No request_context → ValidationError."""
state = make_state(request_context=None)
final_state = await imdb_enrich_ticket(state)
assert final_state["error"]["type"] == "ValidationError"
assert final_state["error"]["step"] == "imdb_enrichment_failed"
async def test_imdb_enrichment_node_missing_msisdn(self):
"""msisdn=None → Redirect (smart human handling)."""
state = make_state(request_context=_valid_context(msisdn=None))
final_state = await imdb_enrich_ticket(state)
assert final_state["current_step"] == "imdb_enrichment_completed"
assert final_state["metadata"]["request_context"]["smart_human_handling"] is True
# --- Client errors ------------------------------------------------------
@patch(IMDB_CLIENT_PATH)
async def test_imdb_enrichment_node_http_error(self, mock_client_cls):
"""ImdbHttpError → error.type == 'ImdbHttpError', step == 'imdb_enrichment_failed'."""
mock_client = AsyncMock()
mock_client.get_imdb_access_data_with_retry.side_effect = ImdbHttpError(404, "Not Found")
mock_client_cls.return_value = mock_client
final_state = await imdb_enrich_ticket(make_state(request_context=_valid_context()))
assert final_state["error"]["type"] == "ImdbHttpError"
assert final_state["current_step"] == "imdb_enrichment_failed"
@patch(IMDB_CLIENT_PATH)
async def test_imdb_enrichment_node_timeout_error(self, mock_client_cls):
"""ImdbTimeoutError → error.type == 'ImdbTimeoutError'."""
mock_client = AsyncMock()
mock_client.get_imdb_access_data_with_retry.side_effect = ImdbTimeoutError("url", 5.0)
mock_client_cls.return_value = mock_client
final_state = await imdb_enrich_ticket(make_state(request_context=_valid_context()))
assert final_state["error"]["type"] == "ImdbTimeoutError"
assert final_state["current_step"] == "imdb_enrichment_failed"
@patch(IMDB_CLIENT_PATH)
async def test_imdb_enrichment_node_connection_error(self, mock_client_cls):
"""ImdbConnectionError → error.type == 'ImdbConnectionError'."""
mock_client = AsyncMock()
mock_client.get_imdb_access_data_with_retry.side_effect = ImdbConnectionError("url", "Connection refused")
mock_client_cls.return_value = mock_client
final_state = await imdb_enrich_ticket(make_state(request_context=_valid_context()))
assert final_state["error"]["type"] == "ImdbConnectionError"
assert final_state["current_step"] == "imdb_enrichment_failed"
@patch(IMDB_CLIENT_PATH)
async def test_imdb_enrichment_node_no_content_response(self, mock_client_cls):
"""204 No Content (None response) → step == 'imdb_enrichment_completed'."""
mock_client = AsyncMock()
mock_client.get_imdb_access_data_with_retry.return_value = None
mock_client_cls.return_value = mock_client
final_state = await imdb_enrich_ticket(make_state(request_context=_valid_context()))
assert final_state["current_step"] == "imdb_enrichment_completed"
assert final_state["error"] is None
context = final_state["metadata"]["request_context"]
assert "imdb_access_data" in context
assert context["imdb_access_data"]["plan"] is None
assert context["imdb_access_data"]["status_code"] == 204
@patch(IMDB_CLIENT_PATH)
async def test_imdb_enrichment_node_generic_client_error(self, mock_client_cls):
"""Generic ImdbClientError → error.type == 'ImdbClientError'."""
mock_client = AsyncMock()
mock_client.get_imdb_access_data_with_retry.side_effect = ImdbClientError("Unexpected error")
mock_client_cls.return_value = mock_client
final_state = await imdb_enrich_ticket(make_state(request_context=_valid_context()))
assert final_state["error"]["type"] == "ImdbClientError"
assert final_state["current_step"] == "imdb_enrichment_failed"
# ---------------------------------------------------------------------------
# Router tests
# ---------------------------------------------------------------------------
@pytest.mark.unit
class TestShouldContinue:
def test_should_continue_when_imdb_enrichment_completed(self):
state = make_state()
state["current_step"] = "imdb_enrichment_completed"
assert should_continue(state) == "continue"
def test_should_end_when_imdb_enrichment_failed(self):
state = make_state()
state["current_step"] = "imdb_enrichment_failed"
assert should_continue(state) == "failed"
def test_should_end_on_unknown_step(self):
state = make_state()
state["current_step"] = "some_other_step"
assert should_continue(state) == "failed"