# Plano de testes — `PR-1` Calcular o ponto de cada palpite (10/5/3)

**Story:** `docs/stories/PR-1-calcular-ponto.md`

Por que esta story concentra risco: é a conta que decide o campeão. Errar uma
faixa não gera erro visível — gera ranking errado que ninguém confere. E há uma
sutileza que o texto "vencedor e saldo" esconde: **jogo empatado não tem faixa
de saldo**, porque todo empate tem saldo 0.

| ID | Título | Tipo | Prioridade |
| -- | ------ | ---- | ---------- |
| PR-1-01 | Placar exato | caminho feliz | alta |
| PR-1-02 | Vencedor e saldo, sem exato | caminho feliz | alta |
| PR-1-03 | Só o vencedor | caminho feliz | alta |
| PR-1-04 | Errou o vencedor | caminho feliz | alta |
| PR-1-05 | Empate exato | borda | alta |
| PR-1-06 | Empate com placar diferente vale "só vencedor" | borda | alta |
| PR-1-07 | Palpitou empate, deu vitória | borda | alta |
| PR-1-08 | Palpitou vitória, deu empate | borda | alta |
| PR-1-09 | Saldo igual em vitória do visitante | borda | média |
| PR-1-10 | Tabela customizada é lida do bolão | borda | alta |
| PR-1-11 | Jogo sem palpite vale 0, não nulo | borda | alta |
| PR-1-12 | Jogo terminado sem placar não pontua | borda | alta |
| PR-1-13 | Apuração é idempotente | borda | alta |
| PR-1-14 | App não recalcula ponto localmente | segurança | alta |

---

### PR-1-01 — Placar exato
**Tipo:** caminho feliz · **Prioridade:** alta
**Dados de teste:** palpite `2x1`, oficial `2x1`, tabela padrão.
**Resultado esperado:** `band: PLACAR_EXATO`, `points: 10`.

### PR-1-02 — Vencedor e saldo, sem exato
**Tipo:** caminho feliz · **Prioridade:** alta
**Dados de teste:** palpite `3x1`, oficial `2x0`.
**Resultado esperado:** `band: VENCEDOR_E_SALDO`, `points: 5`. Mesmo vencedor
(casa) e mesmo saldo (+2), placar diferente.

### PR-1-03 — Só o vencedor
**Tipo:** caminho feliz · **Prioridade:** alta
**Dados de teste:** palpite `1x0`, oficial `3x0`.
**Resultado esperado:** `band: SO_VENCEDOR`, `points: 3`. Vencedor certo, saldo
diferente (+1 contra +3).

### PR-1-04 — Errou o vencedor
**Tipo:** caminho feliz · **Prioridade:** alta
**Dados de teste:** palpite `0x2`, oficial `2x0`.
**Resultado esperado:** `band: ERROU`, `points: 0`.

### PR-1-05 — Empate exato
**Tipo:** borda · **Prioridade:** alta
**Dados de teste:** palpite `1x1`, oficial `1x1`.
**Resultado esperado:** `PLACAR_EXATO`, 10 pontos.

### PR-1-06 — Empate com placar diferente vale "só vencedor"
**Tipo:** borda · **Prioridade:** alta
**Dados de teste:** palpite `1x1`, oficial `4x4`.
**Resultado esperado:** `SO_VENCEDOR`, **3** pontos — não 5.
_Este é o caso que a tela de regras descrevia errado (06/08): todo empate tem
saldo 0, então "acertar o saldo" não diz nada além de "acertar o empate". Se
algum dia o produto decidir que isso vale 5, a mudança é no PRD e **este caso
falha primeiro**, que é exatamente o que se quer._

### PR-1-07 — Palpitou empate, deu vitória
**Tipo:** borda · **Prioridade:** alta
**Dados de teste:** palpite `1x1`, oficial `2x1`.
**Resultado esperado:** `ERROU`, 0 pontos.

### PR-1-08 — Palpitou vitória, deu empate
**Tipo:** borda · **Prioridade:** alta
**Dados de teste:** palpite `2x1`, oficial `1x1`.
**Resultado esperado:** `ERROU`, 0 pontos. Simétrico de PR-1-07 — os dois
existem porque a implementação usa `Math.sign`, e sinal invertido passaria num
só deles.

### PR-1-09 — Saldo igual em vitória do visitante
**Tipo:** borda · **Prioridade:** média
**Dados de teste:** palpite `0x2`, oficial `1x3`.
**Resultado esperado:** `VENCEDOR_E_SALDO`, 5 pontos. Saldo −2 nos dois lados.

### PR-1-10 — Tabela customizada é lida do bolão
**Tipo:** borda · **Prioridade:** alta
**Pré-condições:** bolão criado com `20 / 10 / 5`.
**Dados de teste:** palpite exato.
**Resultado esperado:** 20 pontos. A conta lê a tabela **daquele bolão**, não
uma constante no código — dois bolões com tabelas diferentes têm que conviver.

### PR-1-11 — Jogo sem palpite vale 0, não nulo
**Tipo:** borda · **Prioridade:** alta
**Pré-condições:** jogo terminado; participante sem palpite nele.
**Resultado esperado:** no extrato, `points: 0` com estado `SEM_PALPITE`, e a
soma do extrato bate com o ranking. Nulo aqui viraria "sem informação" e
esconderia do participante que ele esqueceu de palpitar.

### PR-1-12 — Jogo terminado sem placar não pontua
**Tipo:** borda · **Prioridade:** alta
**Pré-condições:** `status = FINISHED` mas `homeScore` nulo (fonte atrasou).
**Resultado esperado:** nenhuma apuração; extrato mostra
`AGUARDANDO_RESULTADO` e `points: null`. Mostrar 0 acusaria o participante de
errar um jogo que ninguém apurou.

### PR-1-13 — Apuração é idempotente
**Tipo:** borda · **Prioridade:** alta
**Passos:**
1. Apurar o jogo.
2. Apurar de novo (job rodando duas vezes no mesmo intervalo).
**Resultado esperado:** pontos idênticos, sem duplicação e sem soma dobrada.

### PR-1-14 — App não recalcula ponto localmente
**Tipo:** segurança · **Prioridade:** alta
**Passos:**
1. Alterar o `points` de um palpite **direto no banco** para um valor
   arbitrário (ex: 77).
2. Reabrir a tela de extrato no app.
**Resultado esperado:** o app exibe **77**. Se exibir o valor "correto" pela
tabela, é porque está recalculando local — e aí duas telas podem discordar, o
que é pior que estar errado nos dois lugares.
