Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 13 additions & 13 deletions api/db/registro.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
from modules.academy import modelo as modelo_academia
from modules.agenda import modelo as modelo_agenda
from modules.comunicados import modelo as modelo_comunicados
from modules.gamificacao import modelo as modelo_gamificacao
from modules.integracoes import modelo as modelo_integracoes
from modules.leads import modelo as modelo_leads
from modules.lgpd import modelo as modelo_lgpd
from modules.portfolio import modelo as modelo_portfolio
from modules.projetos import modelo as modelo_projetos
from modules.servicos import modelo as modelo_servicos
from modules.tarefas import modelo as modelo_tarefas
from modules.users import modelo as modelo_usuarios
from modules.academy import model as modelo_academia
from modules.agenda import model as modelo_agenda
from modules.comunicados import model as modelo_comunicados
from modules.gamificacao import model as modelo_gamificacao
from modules.integracoes import model as modelo_integracoes
from modules.leads import model as modelo_leads
from modules.lgpd import model as modelo_lgpd
from modules.portfolio import model as modelo_portfolio
from modules.projetos import model as modelo_projetos
from modules.servicos import model as modelo_servicos
from modules.tarefas import model as modelo_tarefas
from modules.users import model as modelo_usuarios

# utilizado na env de migração do allembic
# utilizado na env de migração do Alembic
TODOS_MODELOS = (
modelo_academia,
modelo_agenda,
Expand Down
13 changes: 13 additions & 0 deletions api/modules/users/controller.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
from modules.users.schema import (
UsuarioAtualizar,
UsuarioCriar,
UsuarioRegistrar,
UsuarioResposta,
)
from modules.users.service import ServicoUsuario
Expand Down Expand Up @@ -112,3 +113,15 @@ async def aprovar(usuario_id: int, servico: DependenciaServico):
async def deletar(usuario_id: int, servico: DependenciaServico):
"""Função para excluir um registro pelo ID."""
await servico.deletar(usuario_id)


@router.post(
'/registro',
response_model=UsuarioResposta,
status_code=status.HTTP_201_CREATED,
summary='Registra um novo usuário pendente de aprovação',
)
async def registrar(dados: UsuarioRegistrar, servico: DependenciaServico):
"""Função para registrar um novo usuário pendente de aprovação."""
return await servico.registrar(dados)

1 change: 1 addition & 0 deletions tests/integration/test_academy.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ def _dados(tipo: str) -> dict[str, object]:
async def test_listar_academia_filtra_body_json(
cliente_admin: AsyncClient, cliente_http: AsyncClient
):
"""Valida que listar academia filtra body json."""
ebook = (await cliente_admin.post('/api/v1/academy', json=_dados('ebook'))).json()
await cliente_admin.post('/api/v1/academy', json=_dados('curso'))

Expand Down
1 change: 1 addition & 0 deletions tests/integration/test_health.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
@pytest.mark.asyncio
@exige_banco
async def test_saude_retorna_ok(cliente_http: AsyncClient):
"""Valida que saude retorna ok."""
resposta = await cliente_http.get('/health/db')
assert resposta.status_code == 200
assert resposta.json() == {'status': 'ok'}
2 changes: 2 additions & 0 deletions tests/integration/test_integracoes.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
async def test_criar_integracao_status(
cliente_admin: AsyncClient, situacao_entrada: str
):
"""Valida que criar integracao status."""
dados = {
'nome': 'google_calendar',
'status': situacao_entrada,
Expand All @@ -26,6 +27,7 @@ async def test_criar_integracao_status(
@pytest.mark.asyncio
@exige_banco
async def test_listar_integracoes(cliente_admin: AsyncClient):
"""Valida que listar integracoes."""
resposta = await cliente_admin.get('/api/v1/integracoes')
assert resposta.status_code == 200
assert isinstance(resposta.json(), list)
3 changes: 3 additions & 0 deletions tests/integration/test_leads.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ def _dados(**substituicoes):
@exige_banco
@pytest.mark.parametrize('prioridade', ['baixa', 'media', 'alta'])
async def test_criar_lead_com_prioridades(cliente_admin: AsyncClient, prioridade: str):
"""Valida que criar lead com prioridades."""
resposta = await cliente_admin.post(
'/api/v1/leads', json=_dados(prioridade=prioridade)
)
Expand All @@ -44,6 +45,7 @@ async def test_criar_lead_com_prioridades(cliente_admin: AsyncClient, prioridade
async def test_atualizar_status_lead(
cliente_admin: AsyncClient, situacao_inicial: str, nova_situacao: str
):
"""Valida que atualizar status lead."""
criado = (
await cliente_admin.post('/api/v1/leads', json=_dados(status=situacao_inicial))
).json()
Expand All @@ -57,5 +59,6 @@ async def test_atualizar_status_lead(
@pytest.mark.asyncio
@exige_banco
async def test_lead_nao_encontrado_retorna_404(cliente_admin: AsyncClient):
"""Valida que lead nao encontrado retorna 404."""
resposta = await cliente_admin.get('/api/v1/leads/99999999')
assert resposta.status_code == 404
3 changes: 3 additions & 0 deletions tests/integration/test_servicos.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ def _dados_servico(**substituicoes):
@exige_banco
@pytest.mark.parametrize('status', ['ativo', 'inativo'])
async def test_criar_servico_com_status(cliente_admin: AsyncClient, status: str):
"""Valida que criar servico com status."""
resposta = await cliente_admin.post(
'/api/v1/servicos', json=_dados_servico(status=status)
)
Expand All @@ -33,6 +34,7 @@ async def test_criar_servico_com_status(cliente_admin: AsyncClient, status: str)
@pytest.mark.asyncio
@exige_banco
async def test_adicionar_listar_e_atualizar_entregavel(cliente_admin: AsyncClient):
"""Valida que adicionar listar e atualizar entregavel."""
servico = (
await cliente_admin.post('/api/v1/servicos', json=_dados_servico())
).json()
Expand All @@ -59,5 +61,6 @@ async def test_adicionar_listar_e_atualizar_entregavel(cliente_admin: AsyncClien
@pytest.mark.asyncio
@exige_banco
async def test_servico_nao_encontrado_retorna_404(cliente_admin: AsyncClient):
"""Valida que servico nao encontrado retorna 404."""
resposta = await cliente_admin.get('/api/v1/servicos/99999999')
assert resposta.status_code == 404
9 changes: 9 additions & 0 deletions tests/integration/test_users.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ def _dados_funcionario():
async def test_admin_cria_usuarios_de_tipos_diversos(
cliente_http: AsyncClient, cabecalhos_admin: dict[str, str], construtor_dados
):
"""Valida que admin cria usuarios de tipos diversos."""
dados = construtor_dados()
resposta = await cliente_http.post(
'/api/v1/usuarios', json=dados, headers=cabecalhos_admin
Expand All @@ -52,6 +53,7 @@ async def test_admin_cria_usuarios_de_tipos_diversos(
@pytest.mark.asyncio
@exige_banco
async def test_criar_usuario_sem_token_retorna_401(cliente_http: AsyncClient):
"""Valida que criar usuario sem token retorna 401."""
resposta = await cliente_http.post('/api/v1/usuarios', json=_dados_cliente())
assert resposta.status_code == 401

Expand All @@ -61,6 +63,7 @@ async def test_criar_usuario_sem_token_retorna_401(cliente_http: AsyncClient):
async def test_criar_usuario_com_role_nao_admin_retorna_403(
cliente_http: AsyncClient, cabecalhos_cliente: dict[str, str]
):
"""Valida que criar usuario com role nao admin retorna 403."""
resposta = await cliente_http.post(
'/api/v1/usuarios', json=_dados_cliente(), headers=cabecalhos_cliente
)
Expand All @@ -72,6 +75,7 @@ async def test_criar_usuario_com_role_nao_admin_retorna_403(
async def test_email_duplicado_retorna_409(
cliente_http: AsyncClient, cabecalhos_admin: dict[str, str]
):
"""Valida que email duplicado retorna 409."""
dados = _dados_cliente()
primeira_resposta = await cliente_http.post(
'/api/v1/usuarios', json=dados, headers=cabecalhos_admin
Expand All @@ -86,6 +90,7 @@ async def test_email_duplicado_retorna_409(
@pytest.mark.asyncio
@exige_banco
async def test_registro_publico_cria_usuario_pendente(cliente_http: AsyncClient):
"""Valida que registro publico cria usuario pendente."""
dados = _dados_cliente()
resposta = await cliente_http.post('/api/v1/usuarios/registro', json=dados)
assert resposta.status_code == 201, resposta.text
Expand All @@ -96,6 +101,7 @@ async def test_registro_publico_cria_usuario_pendente(cliente_http: AsyncClient)
@pytest.mark.asyncio
@exige_banco
async def test_registro_publico_nao_permite_admin(cliente_http: AsyncClient):
"""Valida que registro publico nao permite admin."""
dados = _dados_cliente()
dados['role'] = 'admin'
resposta = await cliente_http.post('/api/v1/usuarios/registro', json=dados)
Expand All @@ -107,6 +113,7 @@ async def test_registro_publico_nao_permite_admin(cliente_http: AsyncClient):
async def test_admin_aprova_cadastro_pendente(
cliente_http: AsyncClient, cabecalhos_admin: dict[str, str]
):
"""Valida que admin aprova cadastro pendente."""
registro = await cliente_http.post(
'/api/v1/usuarios/registro', json=_dados_funcionario()
)
Expand All @@ -124,6 +131,7 @@ async def test_admin_aprova_cadastro_pendente(
async def test_aprovar_sem_admin_retorna_403(
cliente_http: AsyncClient, cabecalhos_cliente: dict[str, str]
):
"""Valida que aprovar sem admin retorna 403."""
registro = await cliente_http.post(
'/api/v1/usuarios/registro', json=_dados_funcionario()
)
Expand All @@ -141,6 +149,7 @@ async def test_editar_usuario_requer_admin(
cabecalhos_admin: dict[str, str],
cabecalhos_cliente: dict[str, str],
):
"""Valida que editar usuario requer admin."""
criado = await cliente_http.post(
'/api/v1/usuarios', json=_dados_cliente(), headers=cabecalhos_admin
)
Expand Down
2 changes: 2 additions & 0 deletions tests/test_app.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,14 @@


def test_saude():
"""Valida que saude."""
resposta = cliente_http.get('/health')
assert resposta.status_code == HTTPStatus.OK
assert resposta.json() == {'message': 'API is running...'}


def test_swagger_authorize_usa_bearer_sem_chamada_de_login():
"""Valida que swagger authorize usa bearer sem chamada de login."""
openapi = cliente_http.get('/openapi.json')
esquema_seguranca = openapi.json()['components']['securitySchemes']['HTTPBearer']
assert openapi.status_code == HTTPStatus.OK
Expand Down
1 change: 1 addition & 0 deletions tests/unit/test_agenda_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@


def test_evento_local_para_site_usa_fuso_horario_configurado():
"""Valida que evento local para site usa fuso horario configurado."""
evento = EventoAgenda(
id=1,
usuario_id=1,
Expand Down
156 changes: 156 additions & 0 deletions tests/unit/test_base_repository.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
from typing import Any

import pytest
from db.base_repository import RepositorioBase
from modules.servicos.model import Servico
from sqlalchemy import select


class RepositorioServicoTeste(RepositorioBase[Servico]):
modelo = Servico


class ResultadoRepositorioFake:
def __init__(self, valores: list[Any] | None = None, escalar: Any = None):
self.valores = valores or []
self.escalar = escalar

def scalars(self):
return self

def all(self):
return self.valores

def scalar_one_or_none(self):
return self.escalar


class SessaoRepositorioFake:
def __init__(self):
self.adicionados: list[Any] = []
self.flushes = 0
self.refreshes: list[Any] = []
self.execucoes: list[Any] = []
self.resultado = ResultadoRepositorioFake()
self.entidades: dict[tuple[type[Any], int], Any] = {}

def add(self, entidade: Any):
self.adicionados.append(entidade)

async def flush(self):
self.flushes += 1

async def refresh(self, entidade: Any):
self.refreshes.append(entidade)

async def get(self, modelo: type[Any], entidade_id: int):
return self.entidades.get((modelo, entidade_id))

async def execute(self, instrucao: Any):
self.execucoes.append(instrucao)
return self.resultado


@pytest.mark.asyncio
async def test_repositorio_base_adiciona_e_atualiza_estado_da_sessao():
"""Valida que repositorio base adiciona e atualiza estado da sessao."""
sessao = SessaoRepositorioFake()
repositorio = RepositorioServicoTeste(sessao)
servico = Servico(id=1, nome='Site', slug='site')

resposta = await repositorio.adicionar(servico)

assert resposta is servico
assert sessao.adicionados == [servico]
assert sessao.flushes == 1
assert sessao.refreshes == [servico]


@pytest.mark.asyncio
async def test_repositorio_base_obtem_por_id_usando_modelo_configurado():
"""Valida que repositorio base obtem por id usando modelo configurado."""
sessao = SessaoRepositorioFake()
servico = Servico(id=1, nome='Site', slug='site')
sessao.entidades[(Servico, 1)] = servico
repositorio = RepositorioServicoTeste(sessao)

assert await repositorio.obter(1) is servico


@pytest.mark.asyncio
async def test_repositorio_base_lista_com_paginacao_e_filtros_validos():
"""Valida que repositorio base lista com paginacao e filtros validos."""
servico = Servico(id=1, nome='Site', slug='site')
sessao = SessaoRepositorioFake()
sessao.resultado = ResultadoRepositorioFake(valores=[servico])
repositorio = RepositorioServicoTeste(sessao)

resposta = await repositorio.listar_todos(
offset=5,
limit=10,
filtros={'status': None, 'slug': 'site', 'campo_inexistente': 'x'},
)

assert resposta == [servico]
assert len(sessao.execucoes) == 1


@pytest.mark.asyncio
async def test_repositorio_base_atualiza_ignorando_valores_none():
"""Valida que repositorio base atualiza ignorando valores none."""
servico = Servico(id=1, nome='Site atualizado', slug='site')
sessao = SessaoRepositorioFake()
sessao.resultado = ResultadoRepositorioFake(escalar=servico)
repositorio = RepositorioServicoTeste(sessao)

resposta = await repositorio.atualizar(1, {'nome': 'Site atualizado', 'slug': None})

assert resposta is servico
assert sessao.flushes == 1
assert len(sessao.execucoes) == 1


@pytest.mark.asyncio
async def test_repositorio_base_atualizacao_vazia_retorna_entidade_atual():
"""Valida que repositorio base atualizacao vazia retorna entidade atual."""
servico = Servico(id=1, nome='Site', slug='site')
sessao = SessaoRepositorioFake()
sessao.entidades[(Servico, 1)] = servico
repositorio = RepositorioServicoTeste(sessao)

resposta = await repositorio.atualizar(1, {'nome': None})

assert resposta is servico
assert sessao.execucoes == []


@pytest.mark.asyncio
@pytest.mark.parametrize(('escalar', 'esperado'), [(1, True), (None, False)])
async def test_repositorio_base_deleta_retornando_se_linha_existia(escalar, esperado):
"""Valida que repositorio base deleta retornando se linha existia."""
sessao = SessaoRepositorioFake()
sessao.resultado = ResultadoRepositorioFake(escalar=escalar)
repositorio = RepositorioServicoTeste(sessao)

assert await repositorio.deletar(1) is esperado
assert sessao.flushes == 1
assert len(sessao.execucoes) == 1


def test_repositorio_base_remove_valores_vazios():
"""Valida que repositorio base remove valores vazios."""
assert RepositorioBase._remover_valores_vazios({'a': 1, 'b': None}) == {'a': 1}


def test_repositorio_base_aplicar_filtros_ignora_valores_nulos_e_campos_invalidos():
"""Valida filtros nulos e campos invalidos no repositorio base."""
repositorio = RepositorioServicoTeste(SessaoRepositorioFake())
consulta = select(Servico)

filtrada = repositorio._aplicar_filtros(
consulta,
{'slug': 'site', 'status': None, 'inexistente': 'x'},
)

assert filtrada is not consulta
assert repositorio._aplicar_filtros(consulta, None) is consulta
Loading
Loading