# Execução — `AC-2` Login com e-mail e senha

**Plano:** `AC-2-plano.md` · **Data:** 07/08/2026
**Ambiente:** backend local (`test/sessao.e2e-spec.ts`,
`test/login-rate.e2e-spec.ts`), Flutter em teste de widget
(`test/login_page_test.dart`, criado nesta execução — a tela de login não tinha
teste nenhum).

**Resultado: 10/10 ✅** — nenhum defeito; 6 casos novos, cinco deles de tela.

| ID | Resultado | Onde |
| -- | --------- | ---- |
| AC-2-01 | ✅ | e2e "faz login e devolve os dois tokens" |
| AC-2-02 | ✅ | e2e "recusa senha errada e e-mail inexistente com a MESMA resposta" |
| AC-2-03 | ✅ | e2e "bloqueia com 429 depois do limite de tentativas erradas" |
| AC-2-04 | ✅ | e2e "o bloqueio também vale para a senha correta" |
| AC-2-05 | ✅ | e2e "sessões de devices diferentes são independentes" |
| AC-2-06 | ✅ **novo** | e2e "AC-2-06 e-mail em maiúsculas entra na mesma conta" + widget |
| AC-2-07 | ✅ **novo** | widget "AC-2-07 erro do servidor aparece com o texto dele" |
| AC-2-08 | ✅ **novo** | widget "AC-2-08 durante o envio o botão não dispara de novo" |
| AC-2-09 | ✅ **novo** | widget "AC-2-09 não oferece 'esqueci minha senha'" |
| AC-2-10 | ✅ | widget "o login também avisa, para quem já tem conta" |

## Observações

- **A tela de login não tinha um único teste** — é a porta de entrada de quem já
  tem conta, e o lugar onde uma promessa falsa custa mais caro.
- **`AC-2-09` protege uma decisão de produto, não um comportamento:** `FE-3` foi
  cancelada em 03/08 porque não há recuperação de senha. O caso falha se alguém
  acrescentar um link de "esqueci minha senha" achando que está melhorando a tela —
  a pessoa tentaria, não receberia nada, e concluiria que o app está quebrado.
- **`AC-2-08` tem consequência concreta:** dois toques no botão criariam duas
  sessões e consumiriam o rate limit da própria conta por acidente — a pessoa se
  bloquearia sozinha.
- **`AC-2-06` cobre os dois lados do mesmo problema:** o servidor normaliza o
  e-mail (maiúsculas entram na mesma conta) e o app manda sem espaço em volta. O
  teclado de celular capitaliza a primeira letra sozinho; sem isso a pessoa não
  entra na própria conta e, sem recuperação de senha, conclui que perdeu tudo.
- Armadilha de teste registrada: `SessaoNotifier.entrar` grava a conta no
  **secure storage**, que não existe no ambiente de teste — o `pumpAndSettle`
  ficava pendurado para sempre. O falso sobrescreve `entrar` para exercitar a tela
  sem tocar o armazenamento.
