# Execução — `MG-3` Sigilo do palpite e prazo nas rules

**Plano:** `MG-3-plano.md` · **Data:** 07/08/2026
**Ambiente:** emuladores de Firestore, Auth e **Functions** (`demo-bolao`).
26 casos de security rule + 8 de integração com a Function. Suíte total: **66**.

**Resultado: 34/34 ✅** — com uma descoberta que muda o que o app precisa pedir.

## A descoberta: rules não são filtros

O caso `MG-3-07` falhou com uma mensagem que parecia bug de rule:

```
Property userId is undefined on object. for 'list'
```

Não era. Numa **consulta**, a rule não é aplicada documento por documento como
filtro: ela é avaliada **contra a consulta**, e o `where` precisa provar o acesso.
A rule de leitura do palpite depende de `userId` e `matchFinished`; uma consulta que
filtra só por `matchId` deixa os dois indefinidos para o avaliador, e ele recusa —
**mesmo que todos os documentos devolvidos fossem legíveis**.

Isso não é limitação a contornar, é como o Firestore é: quem consulta tem de **pedir
o que pode ver**. Consequência concreta para `MG-7`: a tela que mostra os palpites
de um jogo terminado precisa consultar

```
where('matchId','==', X).where('matchFinished','==', true)
```

e não apenas por jogo. O caso ficou com as duas metades — a consulta sem o filtro
**falha**, a consulta com ele **passa** — para que a exigência não se perca.

`MG-3-08` guarda o outro lado: consulta de jogo **não** terminado falha inteira. A
rule é avaliada por documento e um só que falhe derruba a consulta. Falhar é o
comportamento desejado; vazamento parcial seria pior que erro.

## O risco do sigilo mudou de lugar, e o teste segue ele

No NestJS, sigilo era filtro em consulta: código certo, regra válida sempre. Aqui é
um campo (`matchFinished`) que **alguém precisa virar** — e esse alguém é uma
Function. Então os casos 27 a 30 testam a **transição**, não o estado:

- `MG-3-27`: o apito não libera. Libera no **fim**, não no início — quem vê palpite
  alheio no intervalo sabe o que precisa acontecer no segundo tempo.
- `MG-3-28`: `FINISHED` **sem placar** não libera. A fonte oficial marca o jogo como
  terminado antes de publicar o resultado; liberar aí revelaria o palpite de todos
  sem ninguém ter pontuado e sem o que conferir.
- `MG-3-29`: terminar com placar libera todos os palpites do jogo, em todos os
  bolões (consulta de grupo).
- `MG-3-30`: **a Function não rodando.** Estado resultante: jogo terminado, palpites
  ainda fechados. Falha **segura** — o participante vê "aguardando" em vez de ver o
  palpite dos outros. O inverso não tem como acontecer: só a Function escreve o
  campo, e só no fim com placar.

## Defeito no teste, não no produto

`MG-3-27` e `MG-3-28` falharam na primeira execução por **timeout do Jest**, não por
regra: o caso negativo só conclui depois de esperar, e esperar 5 s com o limite
padrão de 5 s estoura o teste antes de ele afirmar qualquer coisa. Corrigido com
`jest.setTimeout(30s)` e espera de 4 s nos negativos.

Vale registrar porque é sutil: um caso negativo com orçamento de tempo errado
**parece** defeito de produto, e o reflexo de mexer na regra em vez do teste é forte.

## Observações

- **A tabela de pontos e o flag de sigilo são só de leitura para o cliente**
  (`MG-3-19`, `MG-3-20`). Sem isso o participante escolheria os próprios pontos e o
  momento de revelar o palpite.
- **O id determinístico `{matchId}_{uid}`** faz duas coisas: a rule sabe de quem é o
  palpite **sem ler o documento**, e o reenvio da fila offline de `PP-4` sobrescreve
  em vez de duplicar. `MG-3-17` trava o formato.
- **Revisão só na mudança de valor** (`MG-3-32`, `MG-3-33`). É o defeito real de
  `PP-2-04` prevenido por construção, agora com o caso extra da apuração — que
  escreve no mesmo documento e não deve poluir o histórico.
- ⚠️ **O índice de grupo em `palpites.matchId` está declarado mas não verificado:**
  o emulador não exige índice. A Function de sigilo usa consulta de grupo, então
  esse índice é obrigatório em produção. Entra na verificação de `MG-8`.
