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

@@ -0,0 +1,599 @@
import pytest
from datetime import datetime
from dateutil.relativedelta import relativedelta
from src.domain.services.calculador_pontuacao import CalculadorPontuacao
from src.domain.entities.consultor import (
Consultor,
CoordenacaoCapes,
Consultoria,
Inscricao,
AvaliacaoComissao,
Premiacao,
BolsaCNPQ,
Participacao,
)
from src.domain.value_objects.periodo import Periodo
from src.domain.value_objects.criterios_pontuacao import CRITERIOS
def criar_periodo(anos_atras: int, ativo: bool = False, duracao_anos: int = 0) -> Periodo:
inicio = datetime.now() - relativedelta(years=anos_atras)
if ativo:
fim = None
else:
fim = inicio + relativedelta(years=duracao_anos) if duracao_anos > 0 else datetime.now()
return Periodo(inicio=inicio, fim=fim)
class TestBlocoACoordenacaoCapes:
def test_coordenacao_vazia_retorna_bloco_vazio(self):
resultado = CalculadorPontuacao.calcular_bloco_a([])
assert resultado.bloco == "A"
assert resultado.total == 0
assert len(resultado.atuacoes) == 0
def test_coordenador_area_base_200_pontos(self):
coord = CoordenacaoCapes(
codigo="CA",
tipo="Coordenador de Área",
area_avaliacao="CIÊNCIAS AMBIENTAIS",
periodo=criar_periodo(anos_atras=0, duracao_anos=0),
)
resultado = CalculadorPontuacao.calcular_bloco_a([coord])
assert resultado.total >= 200
def test_coordenador_area_ativo_recebe_bonus_atualidade(self):
coord = CoordenacaoCapes(
codigo="CA",
tipo="Coordenador de Área",
area_avaliacao="CIÊNCIAS AMBIENTAIS",
periodo=criar_periodo(anos_atras=1, ativo=True),
)
resultado = CalculadorPontuacao.calcular_bloco_a([coord])
atuacao = resultado.atuacoes[0]
assert atuacao.bonus >= 30
def test_coordenador_area_historico_sem_bonus_atualidade(self):
coord = CoordenacaoCapes(
codigo="CA",
tipo="Coordenador de Área",
area_avaliacao="MEDICINA I",
periodo=criar_periodo(anos_atras=5, duracao_anos=3),
)
resultado = CalculadorPontuacao.calcular_bloco_a([coord])
atuacao = resultado.atuacoes[0]
assert atuacao.codigo == "CA"
assert 30 not in [atuacao.bonus] or atuacao.bonus < 30
def test_coordenador_area_5_anos_pontos_tempo(self):
coord = CoordenacaoCapes(
codigo="CA",
tipo="Coordenador de Área",
area_avaliacao="CIÊNCIAS AMBIENTAIS",
periodo=criar_periodo(anos_atras=5, duracao_anos=5),
)
resultado = CalculadorPontuacao.calcular_bloco_a([coord])
atuacao = resultado.atuacoes[0]
assert atuacao.tempo == 50
def test_coordenador_area_teto_tempo_100_pontos(self):
coord = CoordenacaoCapes(
codigo="CA",
tipo="Coordenador de Área",
area_avaliacao="CIÊNCIAS AMBIENTAIS",
periodo=criar_periodo(anos_atras=15, duracao_anos=15),
)
resultado = CalculadorPontuacao.calcular_bloco_a([coord])
atuacao = resultado.atuacoes[0]
assert atuacao.tempo == 100
def test_coordenador_area_teto_maximo_450(self):
coord = CoordenacaoCapes(
codigo="CA",
tipo="Coordenador de Área",
area_avaliacao="CIÊNCIAS AMBIENTAIS",
periodo=criar_periodo(anos_atras=20, ativo=True),
)
resultado = CalculadorPontuacao.calcular_bloco_a([coord])
atuacao = resultado.atuacoes[0]
assert atuacao.total <= 450
def test_coordenador_adjunto_base_150_pontos(self):
coord = CoordenacaoCapes(
codigo="CAJ",
tipo="Coordenador Adjunto",
area_avaliacao="ENGENHARIA I",
periodo=criar_periodo(anos_atras=0, duracao_anos=0),
)
resultado = CalculadorPontuacao.calcular_bloco_a([coord])
assert resultado.total >= 150
def test_coordenador_adjunto_mp_base_120_pontos(self):
coord = CoordenacaoCapes(
codigo="CAJ_MP",
tipo="Coordenador Adjunto de Mestrado Profissionalizante",
area_avaliacao="ADMINISTRAÇÃO",
periodo=criar_periodo(anos_atras=0, duracao_anos=0),
)
resultado = CalculadorPontuacao.calcular_bloco_a([coord])
assert resultado.total >= 120
def test_camara_tematica_base_100_pontos(self):
coord = CoordenacaoCapes(
codigo="CAM",
tipo="Câmara Temática",
area_avaliacao="INTERDISCIPLINAR",
periodo=criar_periodo(anos_atras=0, duracao_anos=0),
)
resultado = CalculadorPontuacao.calcular_bloco_a([coord])
assert resultado.total >= 100
def test_multiplas_coordenacoes_mesmo_tipo_soma_tempo(self):
periodo1 = criar_periodo(anos_atras=10, duracao_anos=3)
periodo2 = criar_periodo(anos_atras=5, duracao_anos=3)
coords = [
CoordenacaoCapes(
codigo="CA", tipo="Coordenador de Área",
area_avaliacao="ÁREA 1", periodo=periodo1
),
CoordenacaoCapes(
codigo="CA", tipo="Coordenador de Área",
area_avaliacao="ÁREA 2", periodo=periodo2
),
]
resultado = CalculadorPontuacao.calcular_bloco_a(coords)
assert len(resultado.atuacoes) == 1
atuacao = resultado.atuacoes[0]
assert atuacao.quantidade == 2
assert atuacao.tempo == 60
def test_retorno_coordenacao_gera_bonus(self):
periodo1 = criar_periodo(anos_atras=10, duracao_anos=3)
periodo2 = criar_periodo(anos_atras=3, ativo=True)
coords = [
CoordenacaoCapes(
codigo="CA", tipo="Coordenador de Área",
area_avaliacao="ÁREA 1", periodo=periodo1
),
CoordenacaoCapes(
codigo="CA", tipo="Coordenador de Área",
area_avaliacao="ÁREA 1", periodo=periodo2
),
]
resultado = CalculadorPontuacao.calcular_bloco_a(coords)
atuacao = resultado.atuacoes[0]
assert atuacao.bonus >= 20
def test_tipos_diferentes_geram_atuacoes_separadas(self):
coords = [
CoordenacaoCapes(
codigo="CA", tipo="Coordenador de Área",
area_avaliacao="ÁREA 1", periodo=criar_periodo(2, duracao_anos=2)
),
CoordenacaoCapes(
codigo="CAJ", tipo="Coordenador Adjunto",
area_avaliacao="ÁREA 1", periodo=criar_periodo(2, duracao_anos=2)
),
]
resultado = CalculadorPontuacao.calcular_bloco_a(coords)
assert len(resultado.atuacoes) == 2
def test_codigo_com_hifen_normalizado(self):
coord = CoordenacaoCapes(
codigo="CAJ-MP",
tipo="Coordenador Adjunto de Mestrado Profissionalizante",
area_avaliacao="ADMINISTRAÇÃO",
periodo=criar_periodo(anos_atras=2, duracao_anos=2),
)
resultado = CalculadorPontuacao.calcular_bloco_a([coord])
assert len(resultado.atuacoes) == 1
assert resultado.atuacoes[0].codigo == "CAJ_MP"
class TestBlocoBConsultoria:
def test_consultoria_vazia_retorna_bloco_vazio(self):
resultado = CalculadorPontuacao.calcular_bloco_b(None)
assert resultado.bloco == "B"
assert resultado.total == 0
def test_consultor_ativo_base_150_pontos(self):
consultoria = Consultoria(
codigo="CONS_ATIVO",
situacao="Atividade Contínua",
periodo=criar_periodo(anos_atras=1, ativo=True),
anos_consecutivos=1,
retornos=0,
)
resultado = CalculadorPontuacao.calcular_bloco_b(consultoria)
assert resultado.total >= 150
def test_consultor_historico_base_100_pontos(self):
consultoria = Consultoria(
codigo="CONS_HIST",
situacao="Inativo",
periodo=criar_periodo(anos_atras=5, duracao_anos=3),
anos_consecutivos=3,
retornos=0,
)
resultado = CalculadorPontuacao.calcular_bloco_b(consultoria)
assert resultado.total >= 100
def test_consultor_falecido_base_100_pontos(self):
consultoria = Consultoria(
codigo="CONS_FALECIDO",
situacao="Falecido",
periodo=criar_periodo(anos_atras=10, duracao_anos=8),
anos_consecutivos=8,
retornos=0,
)
resultado = CalculadorPontuacao.calcular_bloco_b(consultoria)
assert resultado.total >= 100
def test_consultoria_5_anos_pontos_tempo(self):
consultoria = Consultoria(
codigo="CONS_ATIVO",
situacao="Atividade Contínua",
periodo=criar_periodo(anos_atras=5, ativo=True),
periodos=[criar_periodo(anos_atras=5, ativo=True)],
anos_consecutivos=5,
retornos=0,
)
resultado = CalculadorPontuacao.calcular_bloco_b(consultoria)
atuacao = resultado.atuacoes[0]
assert atuacao.tempo == 25
def test_consultoria_teto_tempo_50_pontos(self):
consultoria = Consultoria(
codigo="CONS_ATIVO",
situacao="Atividade Contínua",
periodo=criar_periodo(anos_atras=15, ativo=True),
periodos=[criar_periodo(anos_atras=15, ativo=True)],
anos_consecutivos=15,
retornos=0,
)
resultado = CalculadorPontuacao.calcular_bloco_b(consultoria)
atuacao = resultado.atuacoes[0]
assert atuacao.tempo == 50
def test_consultor_ativo_bonus_atualidade_20(self):
consultoria = Consultoria(
codigo="CONS_ATIVO",
situacao="Atividade Contínua",
periodo=criar_periodo(anos_atras=1, ativo=True),
anos_consecutivos=1,
retornos=0,
)
resultado = CalculadorPontuacao.calcular_bloco_b(consultoria)
atuacao = resultado.atuacoes[0]
assert atuacao.bonus >= 20
def test_consultor_8_anos_bonus_continuidade(self):
consultoria = Consultoria(
codigo="CONS_ATIVO",
situacao="Atividade Contínua",
periodo=criar_periodo(anos_atras=8, ativo=True),
anos_consecutivos=8,
retornos=0,
)
resultado = CalculadorPontuacao.calcular_bloco_b(consultoria)
atuacao = resultado.atuacoes[0]
assert atuacao.bonus >= 40
def test_consultor_com_retorno_bonus_15(self):
consultoria = Consultoria(
codigo="CONS_ATIVO",
situacao="Atividade Contínua",
periodo=criar_periodo(anos_atras=2, ativo=True),
periodos=[
criar_periodo(anos_atras=8, duracao_anos=3),
criar_periodo(anos_atras=2, ativo=True),
],
anos_consecutivos=2,
retornos=1,
)
resultado = CalculadorPontuacao.calcular_bloco_b(consultoria)
atuacao = resultado.atuacoes[0]
assert atuacao.bonus >= 35
def test_consultoria_teto_maximo_230(self):
consultoria = Consultoria(
codigo="CONS_ATIVO",
situacao="Atividade Contínua",
periodo=criar_periodo(anos_atras=20, ativo=True),
periodos=[criar_periodo(anos_atras=20, ativo=True)],
anos_consecutivos=20,
retornos=1,
)
resultado = CalculadorPontuacao.calcular_bloco_b(consultoria)
atuacao = resultado.atuacoes[0]
assert atuacao.total <= 230
class TestBlocoCPremiacoesAvaliacoes:
def test_bloco_c_vazio(self):
resultado = CalculadorPontuacao.calcular_bloco_c([], [], [], [], [])
assert resultado.bloco == "C"
assert resultado.total == 0
def test_inscricao_autor_base_10_pontos(self):
inscricao = Inscricao(
codigo="INSC_AUTOR", tipo="Autor",
premio="PCT", ano=2024
)
resultado = CalculadorPontuacao.calcular_bloco_c([inscricao], [], [], [], [])
atuacao = next((a for a in resultado.atuacoes if a.codigo == "INSC_AUTOR"), None)
assert atuacao is not None
assert atuacao.base >= 10
def test_inscricao_institucional_base_20_pontos(self):
inscricao = Inscricao(
codigo="INSC_INST_AUTOR", tipo="Institucional",
premio="PCT", ano=2024
)
resultado = CalculadorPontuacao.calcular_bloco_c([inscricao], [], [], [], [])
atuacao = next((a for a in resultado.atuacoes if a.codigo == "INSC_INST_AUTOR"), None)
assert atuacao is not None
assert atuacao.base >= 20
def test_avaliacao_comissao_premio_base_30(self):
avaliacao = AvaliacaoComissao(
codigo="AVAL_COMIS_PREMIO", tipo="Membro de Comissão",
premio="PCT", ano=2024
)
resultado = CalculadorPontuacao.calcular_bloco_c([], [avaliacao], [], [], [])
atuacao = next((a for a in resultado.atuacoes if a.codigo == "AVAL_COMIS_PREMIO"), None)
assert atuacao is not None
assert atuacao.base >= 30
def test_avaliacao_comissao_gp_base_40(self):
avaliacao = AvaliacaoComissao(
codigo="AVAL_COMIS_GP", tipo="Membro de Comissão",
premio="Grande Prêmio", ano=2024
)
resultado = CalculadorPontuacao.calcular_bloco_c([], [avaliacao], [], [], [])
atuacao = next((a for a in resultado.atuacoes if a.codigo == "AVAL_COMIS_GP"), None)
assert atuacao is not None
assert atuacao.base >= 40
def test_coord_comissao_premio_base_40(self):
avaliacao = AvaliacaoComissao(
codigo="COORD_COMIS_PREMIO", tipo="Coordenador",
premio="PCT", ano=2024
)
resultado = CalculadorPontuacao.calcular_bloco_c([], [avaliacao], [], [], [])
atuacao = next((a for a in resultado.atuacoes if a.codigo == "COORD_COMIS_PREMIO"), None)
assert atuacao is not None
assert atuacao.base >= 40
def test_coord_comissao_gp_base_50(self):
avaliacao = AvaliacaoComissao(
codigo="COORD_COMIS_GP", tipo="Coordenador",
premio="Grande Prêmio", ano=2024
)
resultado = CalculadorPontuacao.calcular_bloco_c([], [avaliacao], [], [], [])
atuacao = next((a for a in resultado.atuacoes if a.codigo == "COORD_COMIS_GP"), None)
assert atuacao is not None
assert atuacao.base >= 50
def test_premiacao_gp_autor_base_100(self):
premiacao = Premiacao(
codigo="PREMIACAO_GP_AUTOR", tipo="Grande Prêmio",
nome_premio="Grande Prêmio CAPES", ano=2024
)
resultado = CalculadorPontuacao.calcular_bloco_c([], [], [premiacao], [], [])
atuacao = next((a for a in resultado.atuacoes if a.codigo == "PREMIACAO_GP_AUTOR"), None)
assert atuacao is not None
assert atuacao.base >= 100
def test_premiacao_autor_base_50(self):
premiacao = Premiacao(
codigo="PREMIACAO_AUTOR", tipo="Prêmio",
nome_premio="Prêmio CAPES de Tese", ano=2024
)
resultado = CalculadorPontuacao.calcular_bloco_c([], [], [premiacao], [], [])
atuacao = next((a for a in resultado.atuacoes if a.codigo == "PREMIACAO_AUTOR"), None)
assert atuacao is not None
assert atuacao.base >= 50
def test_mencao_autor_base_30(self):
premiacao = Premiacao(
codigo="MENCAO_AUTOR", tipo="Menção Honrosa",
nome_premio="Prêmio CAPES de Tese", ano=2024
)
resultado = CalculadorPontuacao.calcular_bloco_c([], [], [premiacao], [], [])
atuacao = next((a for a in resultado.atuacoes if a.codigo == "MENCAO_AUTOR"), None)
assert atuacao is not None
assert atuacao.base >= 30
def test_avaliacao_recorrente_bonus_anual(self):
avaliacoes = [
AvaliacaoComissao(
codigo="AVAL_COMIS_PREMIO", tipo="Membro",
premio="PCT", ano=2022
),
AvaliacaoComissao(
codigo="AVAL_COMIS_PREMIO", tipo="Membro",
premio="PCT", ano=2023
),
AvaliacaoComissao(
codigo="AVAL_COMIS_PREMIO", tipo="Membro",
premio="PCT", ano=2024
),
]
resultado = CalculadorPontuacao.calcular_bloco_c([], avaliacoes, [], [], [])
atuacao = next((a for a in resultado.atuacoes if a.codigo == "AVAL_COMIS_PREMIO"), None)
assert atuacao is not None
assert atuacao.bonus > 0
assert atuacao.quantidade == 3
def test_inscricoes_multiplas_acumulam(self):
inscricoes = [
Inscricao(codigo="INSC_AUTOR", tipo="Autor", premio="PCT", ano=2022),
Inscricao(codigo="INSC_AUTOR", tipo="Autor", premio="PCT", ano=2023),
]
resultado = CalculadorPontuacao.calcular_bloco_c(inscricoes, [], [], [], [])
atuacao = next((a for a in resultado.atuacoes if a.codigo == "INSC_AUTOR"), None)
assert atuacao is not None
assert atuacao.quantidade == 2
assert atuacao.base == 20
def test_teto_inscricao_autor_20_pontos(self):
inscricoes = [
Inscricao(codigo="INSC_AUTOR", tipo="Autor", premio="PCT", ano=i)
for i in range(2015, 2025)
]
resultado = CalculadorPontuacao.calcular_bloco_c(inscricoes, [], [], [], [])
atuacao = next((a for a in resultado.atuacoes if a.codigo == "INSC_AUTOR"), None)
assert atuacao is not None
assert atuacao.total <= 20
class TestBlocoDParticipacoes:
def test_bloco_d_vazio(self):
resultado = CalculadorPontuacao.calcular_bloco_d([], [])
assert resultado.bloco == "D"
assert resultado.total == 0
def test_bolsa_cnpq_base_30(self):
bolsa = BolsaCNPQ(codigo="BOL_BPQ_NIVEL", nivel="1A")
resultado = CalculadorPontuacao.calcular_bloco_d([bolsa], [])
atuacao = next((a for a in resultado.atuacoes if a.codigo == "BOL_BPQ_NIVEL"), None)
assert atuacao is not None
assert atuacao.base >= 30
def test_evento_base_1_ponto(self):
evento = Participacao(codigo="EVENTO", tipo="Evento", ano=2024)
resultado = CalculadorPontuacao.calcular_bloco_d([], [evento])
atuacao = next((a for a in resultado.atuacoes if a.codigo == "EVENTO"), None)
assert atuacao is not None
assert atuacao.base >= 1
def test_projeto_base_10_pontos(self):
projeto = Participacao(codigo="PROJ", tipo="Projeto", ano=2024)
resultado = CalculadorPontuacao.calcular_bloco_d([], [projeto])
atuacao = next((a for a in resultado.atuacoes if a.codigo == "PROJ"), None)
assert atuacao is not None
assert atuacao.base >= 10
def test_eventos_multiplos_bonus_recorrencia(self):
eventos = [
Participacao(codigo="EVENTO", tipo="Evento", ano=i)
for i in range(2020, 2025)
]
resultado = CalculadorPontuacao.calcular_bloco_d([], eventos)
atuacao = next((a for a in resultado.atuacoes if a.codigo == "EVENTO"), None)
assert atuacao is not None
assert atuacao.quantidade == 5
assert atuacao.bonus > 0
def test_evento_teto_5_pontos(self):
eventos = [
Participacao(codigo="EVENTO", tipo="Evento", ano=i)
for i in range(2000, 2025)
]
resultado = CalculadorPontuacao.calcular_bloco_d([], eventos)
atuacao = next((a for a in resultado.atuacoes if a.codigo == "EVENTO"), None)
assert atuacao is not None
assert atuacao.total <= 5
def test_projeto_teto_30_pontos(self):
projetos = [
Participacao(codigo="PROJ", tipo="Projeto", ano=i)
for i in range(2000, 2025)
]
resultado = CalculadorPontuacao.calcular_bloco_d([], projetos)
atuacao = next((a for a in resultado.atuacoes if a.codigo == "PROJ"), None)
assert atuacao is not None
assert atuacao.total <= 30
class TestBlocoECoordPPG:
def test_bloco_e_sem_coordenador(self):
resultado = CalculadorPontuacao.calcular_bloco_e(False)
assert resultado.bloco == "E"
assert resultado.total == 0
def test_bloco_e_com_coordenador(self):
resultado = CalculadorPontuacao.calcular_bloco_e(True)
assert resultado.bloco == "E"
assert len(resultado.atuacoes) == 1
assert resultado.atuacoes[0].codigo == "PPG_COORD"
class TestPontuacaoCompleta:
def test_consultor_vazio_pontuacao_zero(self, consultor_vazio):
resultado = CalculadorPontuacao.calcular_pontuacao_completa(consultor_vazio)
assert resultado.total == 0
def test_consultor_com_coordenacao(self, coordenacao_ca_ativa):
consultor = Consultor(
id_pessoa=1,
nome="Coordenador",
coordenacoes_capes=[coordenacao_ca_ativa],
)
resultado = CalculadorPontuacao.calcular_pontuacao_completa(consultor)
assert resultado.bloco_a.total > 0
assert resultado.bloco_b.total == 0
def test_consultor_completo_todos_blocos(self, consultor_completo):
resultado = CalculadorPontuacao.calcular_pontuacao_completa(consultor_completo)
assert resultado.bloco_a.total > 0
assert resultado.bloco_b.total > 0
assert resultado.bloco_c.total > 0
assert resultado.bloco_d.total > 0
def test_pontuacao_total_soma_blocos(self, consultor_completo):
resultado = CalculadorPontuacao.calcular_pontuacao_completa(consultor_completo)
soma_esperada = (
resultado.bloco_a.total
+ resultado.bloco_b.total
+ resultado.bloco_c.total
+ resultado.bloco_d.total
+ resultado.bloco_e.total
)
assert resultado.total == soma_esperada
def test_detalhamento_retorna_dict(self, consultor_completo):
resultado = CalculadorPontuacao.calcular_pontuacao_completa(consultor_completo)
detalhes = resultado.detalhamento
assert isinstance(detalhes, dict)
assert "bloco_a" in detalhes
assert "bloco_b" in detalhes
assert "bloco_c" in detalhes
assert "bloco_d" in detalhes
assert "bloco_e" in detalhes
assert "pontuacao_total" in detalhes
class TestCriteriosConfigurados:
def test_criterio_ca_existe(self):
assert "CA" in CRITERIOS
criterio = CRITERIOS["CA"]
assert criterio.base == 200
assert criterio.teto == 450
def test_criterio_caj_existe(self):
assert "CAJ" in CRITERIOS
criterio = CRITERIOS["CAJ"]
assert criterio.base == 150
assert criterio.teto == 370
def test_criterio_cons_ativo_existe(self):
assert "CONS_ATIVO" in CRITERIOS
criterio = CRITERIOS["CONS_ATIVO"]
assert criterio.base == 150
assert criterio.teto == 230
def test_todos_criterios_tem_codigo_valido(self):
for codigo, criterio in CRITERIOS.items():
assert criterio.codigo == codigo
assert criterio.base >= 0
assert criterio.teto >= 0