from fastapi.testclient import TestClient from app import session from app.main import app client = TestClient(app) def _cookie_sessao_admin() -> dict: return {session.SESSION_COOKIE_NAME: session.criar_token("admin", "admin", "ADMIN")} def test_raiz_sem_login_redireciona_para_login(): resposta = client.get("/", follow_redirects=False) assert resposta.status_code == 303 assert resposta.headers["location"] == "/login?next=/" def test_docs_desabilitado_retorna_404(): assert client.get("/docs").status_code == 404 assert client.get("/redoc").status_code == 404 assert client.get("/openapi.json").status_code == 404 def test_raiz_redireciona_para_clientes_com_login(monkeypatch): from app.routers import clientes as router_clientes monkeypatch.setattr(router_clientes.db_clientes, "listar_clientes", lambda busca=None, limite=100, offset=0, ordenar_por="razao", direcao="asc": []) monkeypatch.setattr(router_clientes.db_clientes, "contar_clientes", lambda busca=None, ordenar_por="razao": 0) resposta = client.get("/", cookies=_cookie_sessao_admin(), follow_redirects=False) assert resposta.status_code == 307 assert resposta.headers["location"] == "/clientes" def test_csrf_middleware_define_cookie_em_qualquer_resposta(): client.cookies.clear() resposta = client.get("/docs") assert resposta.status_code == 404 assert "csrf_token" in resposta.cookies def test_csrf_bloqueia_post_com_token_que_nao_bate_com_cookie(): client.cookies.clear() resposta = client.post( "/clientes/1/excluir", cookies=_cookie_sessao_admin(), data={"csrf_token": "token-forjado-sem-cookie"} ) assert resposta.status_code == 403 def test_csrf_libera_post_quando_token_bate_com_cookie(monkeypatch): from app.routers import clientes as router_clientes monkeypatch.setattr(router_clientes.db_clientes, "excluir_cliente", lambda cliente_id: None) client.cookies.clear() client.get("/docs") # só para o middleware definir o cookie csrf_token no client de teste token_cookie = client.cookies.get("csrf_token") assert token_cookie cookies = {**_cookie_sessao_admin(), "csrf_token": token_cookie} resposta = client.post( "/clientes/1/excluir", cookies=cookies, data={"csrf_token": token_cookie}, follow_redirects=False ) assert resposta.status_code == 303 def test_pagina_clientes_tem_csrf_token_preenchido_no_botao_sair(monkeypatch): """ Regressão: GET /clientes (a página onde todo login cai) não passava csrf_token no contexto do template, então o formulário de logout em base.html renderizava com value="" e POST /logout era rejeitado com 403 por app/csrf.py. base.html agora usa "{{ csrf_token or request.state.csrf_token }}" como fallback. """ from app.routers import clientes as router_clientes monkeypatch.setattr( router_clientes.db_clientes, "listar_clientes", lambda busca=None, limite=100, offset=0, ordenar_por="razao", direcao="asc": [], ) monkeypatch.setattr(router_clientes.db_clientes, "contar_clientes", lambda busca=None, ordenar_por="razao": 0) client.cookies.clear() resposta = client.get("/clientes", cookies=_cookie_sessao_admin()) assert resposta.status_code == 200 assert 'name="csrf_token" value=""' not in resposta.text import re match = re.search(r'name="csrf_token" value="([^"]*)"', resposta.text) assert match is not None assert len(match.group(1)) > 0 def test_logout_funciona_depois_de_visitar_clientes(monkeypatch): """ Regressão complementar: simula o fluxo real — visitar /clientes (onde o cookie csrf_token é definido pelo middleware) e então enviar POST /logout usando o csrf_token real do cookie do navegador, sem passar csrf_token manualmente no contexto do template. """ from app.routers import clientes as router_clientes monkeypatch.setattr( router_clientes.db_clientes, "listar_clientes", lambda busca=None, limite=100, offset=0, ordenar_por="razao", direcao="asc": [], ) monkeypatch.setattr(router_clientes.db_clientes, "contar_clientes", lambda busca=None, ordenar_por="razao": 0) client.cookies.clear() client.cookies.update(_cookie_sessao_admin()) resposta_clientes = client.get("/clientes") assert resposta_clientes.status_code == 200 token_cookie = client.cookies.get("csrf_token") assert token_cookie resposta_logout = client.post("/logout", data={"csrf_token": token_cookie}, follow_redirects=False) assert resposta_logout.status_code == 303