import pytest from src.api.schemas.anatel_schemas import ( TicketRequestEvent, ReasonCode, ERROR_CODE_MAPPING, Customer, Address, Subscriber, Complaint, Origin, SubmittedBy ) from src.agent.logic.validation import validate_required_fields from datetime import datetime # ---- Fixture compartilhada ---------------------------------------- @pytest.fixture def valid_ticket_payload(): """Retorna um payload compatível com TicketRequestEvent (Swagger v0.0.5)""" return { "caseType": "anatel", "origin": { "sourceSystem": "test", "submittedBy": {"userId": "u1", "name": "n1"} }, "ticketId": "DS-98765", "transactionId": "trans-123", "customer": { "cpfCnpj": "12345678901", "phones": ["+5511999999999"], "msisdn": "5511999999999", "name": "Cliente Exemplo", "odcCustomer": False, "contumazCustomer": False, "address": { "cep": "01001-000", "street": "Praca da Se", "neighborhood": "Se", "city": "Sao Paulo", "state": "SP" }, "subscriber": { "cpfCnpj": "12345678901", "subscriberName": "Parente do Cliente" } }, "complaint": { "complaintProtocol": "202603270001", "actionType": "nova", "inputChannel": "Anatel", "service": "S", "firstService": "FS", "modality": "M", "motive": "M", "description": "Descricao da reclamacao", "openedAt": datetime.now() } } # ---- Testes de caminho feliz (happy path) -------------------------- def test_validate_valid_ticket(valid_ticket_payload): """Valida um ticket 100% preenchido conforme o novo contrato.""" ticket = TicketRequestEvent(**valid_ticket_payload) result = validate_required_fields(ticket) assert result.is_valid is True # ---- Testes de cobertura total de ReasonCodes (Exaustivo) ----------- @pytest.mark.parametrize("loc_tuple, mapping_val", ERROR_CODE_MAPPING.items()) def test_exhaustive_reason_code_coverage(valid_ticket_payload, loc_tuple, mapping_val): """ Testa cada entrada do ERROR_CODE_MAPPING removendo o campo correspondente e verificando se o validate_required_fields retorna o ReasonCode e a mensagem esperados. """ reason_code, expected_text = mapping_val # Navega no dicionário para remover o campo data_ptr = valid_ticket_payload for part in loc_tuple[:-1]: data_ptr = data_ptr[part] # Remove ou limpa o campo field_to_clear = loc_tuple[-1] data_ptr[field_to_clear] = "" if isinstance(data_ptr[field_to_clear], str) else None # Reconstrói os modelos aninhados usando model_construct para evitar o Pydantic # mas permitir acesso por atributo (.) na lógica de validação manual address = Address.model_construct(**valid_ticket_payload["customer"]["address"]) cust_data = valid_ticket_payload["customer"].copy() cust_data.pop("address", None) cust_data.pop("subscriber", None) sub_data = valid_ticket_payload["customer"]["subscriber"].copy() subscriber = Subscriber.model_construct(**sub_data) customer = Customer.model_construct( address=address, subscriber=subscriber, **cust_data ) comp_data = valid_ticket_payload["complaint"].copy() complaint = Complaint.model_construct(**comp_data) orig_data = valid_ticket_payload["origin"].copy() sub_by_data = orig_data.pop("submittedBy", {}) sub_by = SubmittedBy.model_construct(**sub_by_data) origin = Origin.model_construct( submittedBy=sub_by, **orig_data ) top_data = valid_ticket_payload.copy() top_data.pop("customer", None) top_data.pop("complaint", None) top_data.pop("origin", None) ticket = TicketRequestEvent.model_construct( customer=customer, complaint=complaint, origin=origin, **top_data ) result = validate_required_fields(ticket) assert result.is_valid is False codes = [m["code"] for m in result.error_response["detail"]["messages"]] texts = [m["text"] for m in result.error_response["detail"]["messages"]] assert reason_code.value in codes assert expected_text in texts def test_validate_error_response_format(valid_ticket_payload): """Verifica se o formato do erro segue o padrão Swagger (title, status, detail).""" valid_ticket_payload["complaint"]["motive"] = "" ticket = TicketRequestEvent(**valid_ticket_payload) result = validate_required_fields(ticket) assert result.is_valid is False # Verificando formato fixo exigido pela TIM/Swagger assert result.error_response["title"] == "validation error" assert result.error_response["status"] == 400 assert "detail" in result.error_response assert "messages" in result.error_response["detail"] assert isinstance(result.error_response["detail"]["messages"], list)