import pytest from fastapi import Depends, FastAPI, HTTPException from fastapi.testclient import TestClient from app import session from app.auth import NaoAutenticado, UsuarioLogado, autenticar_credenciais, exigir_admin, exigir_login from app.db import funcionarios as db_funcionarios from app.db.connection import get_connection app_teste = FastAPI() @app_teste.get("/protegido") def rota_protegida(usuario: UsuarioLogado = Depends(exigir_login)): return {"usuario": str(usuario), "perfil": usuario.perfil} @app_teste.get("/so-admin") def rota_admin(usuario: UsuarioLogado = Depends(exigir_admin)): return {"usuario": str(usuario)} @app_teste.exception_handler(NaoAutenticado) def _handler_nao_autenticado(request, exc): raise HTTPException(status_code=401, detail="não autenticado") client = TestClient(app_teste, follow_redirects=False) def _cookie_sessao(login: str, nome: str, perfil: str) -> dict: return {session.SESSION_COOKIE_NAME: session.criar_token(login, nome, perfil)} def test_sem_cookie_retorna_401(): resposta = client.get("/protegido") assert resposta.status_code == 401 def test_cookie_invalido_retorna_401(): resposta = client.get("/protegido", cookies={session.SESSION_COOKIE_NAME: "lixo"}) assert resposta.status_code == 401 def test_cookie_valido_retorna_dados_do_usuario(): resposta = client.get("/protegido", cookies=_cookie_sessao("admin", "Admin", "ADMIN")) assert resposta.status_code == 200 assert resposta.json() == {"usuario": "Admin", "perfil": "ADMIN"} def test_usuario_logado_str_retorna_nome(): usuario = UsuarioLogado(login="fulano", nome="Fulano da Silva", perfil="PADRAO") assert str(usuario) == "Fulano da Silva" def test_usuario_logado_eh_admin(): assert UsuarioLogado(login="a", nome="A", perfil="ADMIN").eh_admin is True assert UsuarioLogado(login="a", nome="A", perfil="PADRAO").eh_admin is False def test_exigir_admin_aceita_admin(): resposta = client.get("/so-admin", cookies=_cookie_sessao("admin", "Admin", "ADMIN")) assert resposta.status_code == 200 def test_exigir_admin_rejeita_padrao(funcionario_teste): # exigir_login agora reconfirma o perfil contra o banco, então o login # precisa existir de fato em FUNCIONARIO_PAINEL (funcionario_teste é # cadastrado com PERFIL="PADRAO" — ver fixture abaixo). resposta = client.get("/so-admin", cookies=_cookie_sessao(funcionario_teste, "Fulano", "PADRAO")) assert resposta.status_code == 403 def test_autenticar_credenciais_admin_env(): # ADMIN_USER/ADMIN_PASSWORD vêm de tests/conftest.py ("admin"/"teste123") usuario = autenticar_credenciais("admin", "teste123") assert usuario is not None assert usuario.perfil == "ADMIN" assert usuario.login == "admin" def test_autenticar_credenciais_admin_env_senha_errada(): assert autenticar_credenciais("admin", "senha_errada") is None def test_autenticar_credenciais_admin_env_vazio_nao_autentica_vazio(monkeypatch): monkeypatch.setenv("ADMIN_USER", "") monkeypatch.setenv("ADMIN_PASSWORD", "") assert autenticar_credenciais("", "") is None def _limpar_login(login: str) -> None: conn = get_connection() try: with conn.cursor() as cur: cur.execute("DELETE FROM FUNCIONARIO_PAINEL WHERE LOGIN = %s", (login,)) finally: conn.close() @pytest.fixture def funcionario_teste(): login = "teste.login.auth" _limpar_login(login) funcionario_id = db_funcionarios.criar_funcionario("Fulano de Teste", login, "senhaForte123", "PADRAO") yield login _limpar_login(login) def test_autenticar_credenciais_funcionario_ativo(funcionario_teste): usuario = autenticar_credenciais(funcionario_teste, "senhaForte123") assert usuario is not None assert usuario.nome == "Fulano de Teste" assert usuario.perfil == "PADRAO" def test_autenticar_credenciais_funcionario_senha_errada(funcionario_teste): assert autenticar_credenciais(funcionario_teste, "senhaErrada") is None def test_autenticar_credenciais_funcionario_inativo(funcionario_teste): funcionario = db_funcionarios.obter_por_login(funcionario_teste) db_funcionarios.alternar_ativo(funcionario["ID"]) # desativa assert autenticar_credenciais(funcionario_teste, "senhaForte123") is None def test_autenticar_credenciais_login_inexistente(): assert autenticar_credenciais("nao-existe-esse-login", "qualquer") is None def test_exigir_login_autentica_funcionario_ativo(funcionario_teste): resposta = client.get("/protegido", cookies=_cookie_sessao(funcionario_teste, "Fulano de Teste", "PADRAO")) assert resposta.status_code == 200 assert resposta.json() == {"usuario": "Fulano de Teste", "perfil": "PADRAO"} def test_exigir_login_revoga_sessao_ao_desativar_funcionario(funcionario_teste): # Cookie "válido" (assinatura e validade OK) gerado antes de desativar. cookies = _cookie_sessao(funcionario_teste, "Fulano de Teste", "PADRAO") assert client.get("/protegido", cookies=cookies).status_code == 200 funcionario = db_funcionarios.obter_por_login(funcionario_teste) db_funcionarios.alternar_ativo(funcionario["ID"]) # desativa resposta = client.get("/protegido", cookies=cookies) assert resposta.status_code == 401 def test_exigir_admin_usa_perfil_atual_do_banco_nao_do_cookie(funcionario_teste): # Cookie diz ADMIN, mas o funcionário no banco é PADRAO (ex: foi # rebaixado depois de logar) — exigir_admin precisa rejeitar. cookies = _cookie_sessao(funcionario_teste, "Fulano de Teste", "ADMIN") resposta = client.get("/so-admin", cookies=cookies) assert resposta.status_code == 403 def test_exigir_login_conta_bootstrap_nao_consulta_banco(monkeypatch): def _falha_se_chamado(login): raise AssertionError("exigir_login não deveria consultar o banco para a conta de bootstrap") monkeypatch.setattr(db_funcionarios, "obter_por_login", _falha_se_chamado) resposta = client.get("/protegido", cookies=_cookie_sessao("admin", "Admin", "ADMIN")) assert resposta.status_code == 200 assert resposta.json() == {"usuario": "Admin", "perfil": "ADMIN"}