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

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