feat(tests): adicionar suite completa de testes automatizados

- 198 testes cobrindo todos os módulos do backend
- Testes unitários para calculador de pontuação (56 testes)
- Testes para value objects de período (23 testes)
- Testes para cliente Elasticsearch com mocks (27 testes)
- Testes para repository de consultores (48 testes)
- Testes de integração ES + Repository (6 testes)
- Testes para API routes FastAPI (23 testes)
- Testes para job de processamento (16 testes)
- Cobertura de 54% do código
This commit is contained in:
Frederico Castro
2025-12-29 08:06:08 -03:00
parent e0692ee49c
commit 143ec401f5
19 changed files with 2899 additions and 0 deletions

View File

View File

View File

@@ -0,0 +1,301 @@
import pytest
from unittest.mock import AsyncMock, MagicMock, patch
from fastapi.testclient import TestClient
from fastapi import FastAPI
from src.interface.api.routes import router
from src.interface.api.dependencies import (
get_repository,
get_ranking_store,
get_processar_job,
get_es_client,
get_ranking_oracle_repo,
)
@pytest.fixture
def mock_repository():
repo = AsyncMock()
repo.contar_total.return_value = 100
return repo
@pytest.fixture
def mock_ranking_store():
store = MagicMock()
store.is_ready.return_value = False
return store
@pytest.fixture
def mock_oracle_repo():
repo = MagicMock()
repo.contar_total.return_value = 1000
return repo
@pytest.fixture
def mock_es_client():
return AsyncMock()
@pytest.fixture
def mock_job():
return AsyncMock()
@pytest.fixture
def app(mock_repository, mock_ranking_store, mock_oracle_repo, mock_es_client, mock_job):
app = FastAPI()
app.include_router(router)
app.dependency_overrides[get_repository] = lambda: mock_repository
app.dependency_overrides[get_ranking_store] = lambda: mock_ranking_store
app.dependency_overrides[get_ranking_oracle_repo] = lambda: mock_oracle_repo
app.dependency_overrides[get_es_client] = lambda: mock_es_client
app.dependency_overrides[get_processar_job] = lambda: mock_job
return app
@pytest.fixture
def client(app):
return TestClient(app)
class TestHealthCheck:
def test_health_check_ok(self, client):
response = client.get("/api/v1/health")
assert response.status_code == 200
data = response.json()
assert data["status"] == "ok"
class TestRankingStatus:
def test_status_inicial(self, client):
with patch("src.interface.api.routes.job_status") as mock_status:
mock_status.to_dict.return_value = {
"running": False,
"progress": 0,
"processados": 0,
"total": 0,
"mensagem": "",
"batch_atual": 0,
"total_batches": 0,
"tempo_decorrido": None,
"tempo_estimado": None,
"inicio": None,
"fim": None,
"erro": None,
}
response = client.get("/api/v1/ranking/status")
assert response.status_code == 200
data = response.json()
assert data["running"] is False
class TestRankingPaginado:
def test_ranking_paginado_sem_oracle(self, client, mock_oracle_repo):
mock_oracle_repo.contar_total.return_value = 0
response = client.get("/api/v1/ranking/paginado")
assert response.status_code == 503
def test_ranking_paginado_com_dados(self, client, mock_oracle_repo):
mock_oracle_repo.contar_total.return_value = 100
mock_oracle_repo.buscar_paginado.return_value = [
MagicMock(
id_pessoa=1,
nome="Consultor 1",
posicao=1,
pontuacao_total=500,
componente_a=200,
componente_b=100,
componente_c=100,
componente_d=50,
componente_e=50,
ativo=True,
anos_atuacao=5.0,
json_detalhes="{}"
)
]
response = client.get("/api/v1/ranking/paginado?page=1&size=10")
assert response.status_code == 200
data = response.json()
assert data["total"] == 100
assert len(data["consultores"]) == 1
def test_ranking_paginado_filtro_ativo(self, client, mock_oracle_repo):
mock_oracle_repo.contar_total.return_value = 50
mock_oracle_repo.buscar_paginado.return_value = []
response = client.get("/api/v1/ranking/paginado?ativo=true")
assert response.status_code == 200
def test_ranking_paginado_filtro_selos(self, client, mock_oracle_repo):
mock_oracle_repo.contar_total.return_value = 0
response = client.get("/api/v1/ranking/paginado?selos=COORD_PPG,BPQ")
assert response.status_code == 200
data = response.json()
assert data["total"] == 0
class TestBuscaPorNome:
def test_busca_por_nome_encontrado(self, client, mock_oracle_repo):
mock_oracle_repo.buscar_por_nome.return_value = [
{"ID_PESSOA": 1, "NOME": "João Silva", "POSICAO": 10, "PONTUACAO_TOTAL": 500}
]
response = client.get("/api/v1/ranking/busca?nome=João")
assert response.status_code == 200
data = response.json()
assert len(data) == 1
assert data[0]["nome"] == "João Silva"
def test_busca_por_nome_nao_encontrado(self, client, mock_oracle_repo):
mock_oracle_repo.buscar_por_nome.return_value = []
response = client.get("/api/v1/ranking/busca?nome=XYZ")
assert response.status_code == 200
data = response.json()
assert len(data) == 0
def test_busca_por_nome_minimo_3_chars(self, client):
response = client.get("/api/v1/ranking/busca?nome=AB")
assert response.status_code == 422
class TestRankingEstatisticas:
def test_estatisticas_sem_dados(self, client, mock_oracle_repo):
mock_oracle_repo.contar_total.return_value = 0
response = client.get("/api/v1/ranking/estatisticas")
assert response.status_code == 503
def test_estatisticas_com_dados(self, client, mock_oracle_repo):
mock_oracle_repo.contar_total.return_value = 1000
mock_oracle_repo.obter_estatisticas.return_value = {
"total_consultores": 1000,
"total_ativos": 800,
"total_inativos": 200,
"ultima_atualizacao": "2024-01-01",
"pontuacao_media": 150.5,
"pontuacao_maxima": 850,
"pontuacao_minima": 10,
"media_componentes": {"a": 50, "b": 30, "c": 40, "d": 20, "e": 10}
}
mock_oracle_repo.obter_distribuicao.return_value = [
{"faixa": "0-100", "quantidade": 500},
{"faixa": "100-200", "quantidade": 300},
]
response = client.get("/api/v1/ranking/estatisticas")
assert response.status_code == 200
data = response.json()
assert data["total_consultores"] == 1000
assert data["total_ativos"] == 800
class TestPosicaoRanking:
def test_posicao_encontrada(self, client, mock_oracle_repo):
mock_oracle_repo.contar_total.return_value = 1000
mock_oracle_repo.buscar_por_id.return_value = MagicMock(
id_pessoa=123,
nome="João Silva",
posicao=42,
pontuacao_total=500,
componente_a=200,
componente_b=100,
componente_c=100,
componente_d=50,
componente_e=50,
ativo=True,
)
response = client.get("/api/v1/ranking/posicao/123")
assert response.status_code == 200
data = response.json()
assert data["id_pessoa"] == 123
assert data["posicao"] == 42
assert data["encontrado"] is True
def test_posicao_nao_encontrada(self, client, mock_oracle_repo):
mock_oracle_repo.contar_total.return_value = 1000
mock_oracle_repo.buscar_por_id.return_value = None
response = client.get("/api/v1/ranking/posicao/99999")
assert response.status_code == 200
data = response.json()
assert data["encontrado"] is False
class TestProcessarRanking:
def test_processar_ranking_inicia(self, client, mock_job):
with patch("src.interface.api.routes.job_status") as mock_status:
mock_status.is_running = False
response = client.post("/api/v1/ranking/processar")
assert response.status_code == 200
data = response.json()
assert data["sucesso"] is True
def test_processar_ranking_ja_executando(self, client):
with patch("src.interface.api.routes.job_status") as mock_status:
mock_status.is_running = True
response = client.post("/api/v1/ranking/processar")
assert response.status_code == 409
class TestListarSelos:
def test_listar_selos(self, client):
response = client.get("/api/v1/ranking/selos")
assert response.status_code == 200
data = response.json()
assert "selos" in data
assert isinstance(data["selos"], list)
class TestExportarInfo:
def test_info_exportacao(self, client, mock_oracle_repo):
mock_oracle_repo.contar_para_exportacao.return_value = 500
response = client.get("/api/v1/ranking/exportar/info")
assert response.status_code == 200
data = response.json()
assert data["total_consultores"] == 500
assert "estimativa_tamanho_mb" in data
class TestCorrigirEncoding:
def test_corrigir_encoding_sem_problemas(self):
from src.interface.api.routes import corrigir_encoding
result = corrigir_encoding("Texto normal")
assert result == "Texto normal"
def test_corrigir_encoding_none(self):
from src.interface.api.routes import corrigir_encoding
result = corrigir_encoding(None)
assert result is None
def test_corrigir_encoding_vazio(self):
from src.interface.api.routes import corrigir_encoding
result = corrigir_encoding("")
assert result == ""
class TestNormalizarTexto:
def test_normalizar_texto_acentos(self):
from src.interface.api.routes import normalizar_texto
result = normalizar_texto("Ciências Ambientais")
assert result == "ciencias ambientais"
def test_normalizar_texto_html_entities(self):
from src.interface.api.routes import normalizar_texto
result = normalizar_texto("Ciências")
assert "ciencias" in result.lower()
def test_normalizar_texto_vazio(self):
from src.interface.api.routes import normalizar_texto
result = normalizar_texto("")
assert result == ""