docs: registrar bugs encontrados na verificação real em navegador

Usuário testou em produção e confirmou que os dois bugs do commit
anterior (Content-Type em requisições sem corpo, DELETE de supressão sem
motivo) estão corrigidos. Atualiza TODO.md/RELATORIO_FINAL.md para
refletir a verificação visual como concluída, e documenta a lição em
TROUBLESHOOTING.md: teste via curl prova que o backend funciona, não que
o frontend consegue falar com ele.
This commit is contained in:
2026-08-27 20:52:48 -03:00
parent fa8339003d
commit a10a40a489
3 changed files with 55 additions and 16 deletions

30
TODO.md
View File

@@ -321,20 +321,22 @@ banco. Todos os fixtures de teste foram removidos/desativados ao final.
aceite) não exige essa tela. Criar uma tela sem a API por trás seria
construir um mockup, o que a diretriz do projeto proíbe. Fica
registrado como pendência explícita, não descartado silenciosamente.
- [ ] **Verificação visual em navegador não foi possível nesta sessão**
ambiente é um servidor Debian headless sem display/browser. A
verificação feita foi: `tsc --noEmit` limpo, `eslint` limpo, build de
produção (`next build`) gerando as 27 rotas sem erro, e testes
funcionais via `curl` reproduzindo exatamente as chamadas que o
navegador faria — login via `/api/auth/login`, cookie de sessão
validado pelo middleware (`/` redireciona para `/login` sem cookie,
libera com cookie), todas as 24 páginas protegidas retornando HTTP
200 através do Nginx, e os endpoints de dados (`/api/dashboard`,
`/api/reports/*`, `/api/compliance/*` etc.) retornando dados reais
com o mesmo cookie de sessão. O que **não** foi verificado: renderização
visual real, interações de clique/formulário no DOM, responsividade,
tema escuro na prática. Recomenda-se ao usuário abrir
`http://10.10.32.142/` em um navegador para essa validação final.
- [x] **Verificação visual em navegador** não foi possível durante a
construção (ambiente headless, sem display/browser); feita depois
pelo usuário real em `http://10.10.32.142/`, e **encontrou dois bugs
reais que o teste via curl nunca teria pego**: (1) o cliente HTTP do
frontend mandava `Content-Type: application/json` em requisições sem
corpo, e o Fastify rejeitava com 400 "body is empty" — quebrava
**todo** botão de ação sem payload (disponibilizar agente, pausar,
publicar dialplan, iniciar/pausar campanha, redefinir senha de
ramal); (2) botão "Desbloquear" da lista de bloqueio sempre falhava
porque a API exige motivo no corpo do DELETE e o cliente não tinha
como enviá-lo. Ambos corrigidos na raiz (função `request()` central)
e confirmados pelo usuário: "testei, funcionou". Detalhes em
`docs/TROUBLESHOOTING.md`. **Lição registrada**: testes via curl
provam que o backend funciona, não que o frontend consegue falar com
ele — teste real em navegador continua sendo necessário antes de dar
uma tela por concluída.
## Fase 9 — Segurança e produção
- [x] Criptografia de segredos de trunk (AES-256-GCM) — já implementado na

View File

@@ -130,8 +130,12 @@ Itens genuinamente pendentes, nunca escondidos:
3. **`scripts/install.sh`/`update.sh`** não foram testados de ponta a ponta
num servidor limpo (o servidor atual já estava provisionado) — validados
por inspeção linha a linha contra os comandos manuais já executados.
4. **Verificação visual em navegador real** não foi feita (ambiente
headless) — validado via `curl` reproduzindo as chamadas do navegador.
4. ~~Verificação visual em navegador real~~ — feita posteriormente pelo
usuário em produção. Confirmou que o `curl` sozinho não é suficiente:
achou dois bugs reais de integração frontend↔API invisíveis a testes
via curl (Content-Type em requisições sem corpo quebrando todo botão de
ação; DELETE de supressão sem forma de enviar o motivo exigido). Ambos
corrigidos, usuário confirmou funcionamento. Ver `docs/TROUBLESHOOTING.md`.
5. **Tela de Callbacks** no frontend não existe — o agendamento e a
reativação automática funcionam via API/worker (`POST /agent-console/
dispose`, `GET /api/callbacks`, `callback-sweep.ts`), mas não há uma

View File

@@ -3,6 +3,39 @@
Problemas reais encontrados durante o desenvolvimento deste projeto e como
foram diagnosticados/resolvidos — mantido como referência para o futuro.
## "Body cannot be empty when content-type is set to 'application/json'" em quase todo botão de ação
**Sintoma**: usuário real testando no navegador (não `curl`) relatou erro
"body is empty" ao clicar em redefinir senha de um ramal. Investigação
mostrou que o mesmo bug afetava **todo** botão de ação sem payload:
disponibilizar agente, pausar/despausar, publicar dialplan, iniciar/
pausar/parar campanha, etc.
**Causa**: o cliente HTTP central do frontend
(`apps/frontend/src/lib/api-client.ts`) sempre enviava
`Content-Type: application/json` mesmo em requisições sem corpo. O
Fastify rejeita isso com 400 antes de a requisição chegar ao controller
— o `@Body()` do NestJS nem entra em cena, o parser de body do Fastify já
barra antes.
**Por que passou despercebido por tanto tempo**: todo teste de API deste
projeto, em toda fase anterior, foi feito via `curl` — e `curl -X POST`
sem `-H 'Content-Type: ...'` e sem `-d` não define esse header. O
`fetch()` do navegador (usado pelo cliente HTTP real do frontend) sempre
define o header por padrão. Ou seja: **testar só via curl não teria
pegado nunca esse bug** — só apareceu quando um usuário de verdade clicou
em botões reais no navegador. Lição: `curl` prova que o *backend*
funciona: não prova que o *frontend* consegue falar com ele.
**Fix**: só incluir o header `Content-Type` quando `body !== undefined`,
na única função `request()` usada por `get`/`post`/`patch`/`delete`.
**Bug relacionado, achado na mesma revisão**: `DELETE /api/suppression/:id`
exige `removalReason` no corpo, mas `api.delete()` nem aceitava um
argumento de corpo — o botão "Desbloquear" da lista de bloqueio sempre
falhava com 403. Mesma causa raiz (função de ação sem forma de levar
payload), mesmo tipo de bug que só um clique real revela.
## "Argument calledNumber is missing" ao originar chamada
**Sintoma**: `PrismaClientValidationError` ao reservar um lead, mesmo a