Testing Backlog — SSOT
SSOT do que falta cobrir em testes na plataforma OLP. Atualizado em cada sprint. Toda nova lacuna deve ser registrada aqui antes de virar card.
Status atual da pirâmide de testes (após Sprint P0 — CI Fix):
| Camada | Arquivos | Tests | Em CI? |
|---|---|---|---|
Unit + Integration (Vitest src/**) | 76 | 794 | ✅ |
Deno (supabase/functions/**) | 85 | ~600 | ✅ |
Contract (tests/contracts/) | 1 | — | ✅ (após deploy-staging) |
Security/IDOR (tests/security/) | poucos | — | ✅ |
| E2E (Playwright) | poucos | — | ✅ |
Foco notável — Resultados do Coordenador (core do produto)
Disparado pelo incidente Nível S (3º EM) reportado em 2026-06-04. Auditoria E2E em AUDIT_RESULTADOS_NIVEL_S_2026-06-04.md.
Cobertura severa de toda superfície que a tela de Resultados toca:
- Cap silencioso 1000 do PostgREST — testar
fetchAllRows(helper em_shared/pagination-helpers.ts, suite Deno já incluída) e adicionar teste de integração degestao-resultados.listsimulando >1000 inscrições para confirmar que todos os níveis aparecem emfilterMeta.niveise na contagem total. Causa raiz confirmada do incidente Nível S/J (escola com 1.4k inscrições → tela mostrava 1000 cravados → níveis do EM sumiam). - Pipeline de inserção (planilha + manual) — matriz 13 cenários (C1-C13), incluindo aluno sem turma, série não mapeada ao nível, divergência de nome do nível, OMU vs Canguru.
- Mapeamento níveis↔séries —
niveis_competicao_series+olimpiada_series_participantesconsistência por edição. - Filtros de elegibilidade silenciosos —
serieIncompativel,naoClassificadoFaseAnterior,pontuacaoMaximaPorNivel[nome]fallbacknull. - Stats por nível ativo — já mapeado em B3 do audit anterior.
- Publicação parcial silenciosa (PFS-6) — RTL no painel de liberações: "X de Y níveis publicados", badge "pendente" por nível com resultados mas sem
mural_liberacoes, exit-guard ao sair com publicação incompleta. - Edge fn
gestao-resultados— actions:insert_lote,start_import_session,list(incluindohandleList → alunosDoNivel).
Prioridade: alta — não bloqueia outros P1, mas precede qualquer refator estrutural na feature.
Testes alvo (extraídos da §5 do audit):
- RTL
inserir-dialog.test.tsx— matriz C1-C13. - Deno
gestao-resultados/__tests__/insert-lote-nivel-s.test.ts. - Deno
_shared/__tests__/resultados-queries-alunos-do-nivel.test.ts. - E2E coordenador — fluxo completo Nível S Canguru.
- Contract — consistência
olimpiada_series_participantes ∩ niveis_competicao_seriespor edição.
Foco notável — Publicação parcial silenciosa (mural)
Causa raiz confirmada do incidente Nível S (2026-06-04): planilha foi importada com sucesso (224 rows em resultados_aluno), mas o mural ficou vazio porque o coordenador publicou apenas 5 dos 6 níveis (P→E→B→C→J, S esquecido). Sem mural_liberacoes para Nível S → recomputeSnapshotsIfPublished nunca dispara → mural_dados_publicados vazio.
Cobertura necessária:
- RTL
mural-liberacoes.test.tsx— cenário "5 níveis publicados, 1 com resultados pendente": indicador amarelo no botão do nível, badge "5 de 6 publicados" no header, modal de confirmação ao sair. - Deno
mural-escola/__tests__/get_liberacao_stats.cap.test.ts— escola com > 1000resultados_alunoretorna todos os níveis distintos (risco latente do default cap PostgREST). - Deno
mural-escola/__tests__/get_niveis_pendentes.test.ts— novo action que cruza níveis com resultados × liberações. - E2E coordenador — publicar Canguru com 6 níveis e verificar que portal mostra todos.
Prioridade: alta — risco de repetir o incidente em qualquer escola com olimpíadas de 3+ níveis.
Sprint corrente — Webhooks (P0+1)
| Item | Tipo | Status |
|---|---|---|
resend-webhook — HMAC + tolerance window + idempotência + bounce/complained → unsubscribe | Deno black-box contra staging | 🔄 em andamento |
wasender-webhook — HMAC + idempotência + status mapping + rate limit | Deno black-box | ⬜ todo |
mercadopago-webhook — adicionar contract test de assinatura ts/v1 + tipos payment.* e subscription_preapproval | Deno (existe base) | ⬜ todo |
WEBHOOK_TESTING_STANDARD.md — padrão SSOT de teste de webhook | Doc | 🔄 |
Memória mem://testing/webhook-contract-standard | Memory | ⬜ |
Definição de pronto da sprint: 3 webhooks com suite Deno completa, CI verde, doc + memória publicadas.
P1 — Anti-regressão crítica (próximas 2–3 sprints)
Hooks useAdmin* (8 hooks sem teste)
| Hook | Cobertura mínima |
|---|---|
useAdminDashboard | render + queryKey contém escola_id + onError sanitiza |
useAdminLogs | filtros (acao, periodo) + paginação |
useAdminFinanceiro | mutations + optimistic rollback |
useAdminFaturas | listagem + ações de cobrança |
useAdminComunicacao | envio + audiência |
useAdminEscolas | CRUD escola |
useAdminUsuarios | CRUD usuário + escopo de papel |
useAdminRelatorios | filtros e export |
Edge Functions sem teste (P1)
| Function | Risco | Tests obrigatórios |
|---|---|---|
admin-dashboard | leitura ampla — IDOR | auth, IDOR cross-escola, shape do payload |
admin-logs | PII | masking, filtros, IDOR |
admin-financeiro | dinheiro | IDOR, mutações com 403 em zero rows |
admin-comunicacao | envio de massa | rate limit, audiência, IDOR |
admin-faturas | dinheiro | IDOR, idempotência de cobrança |
IDOR coverage tests/security/
Para cada função admin-* acima: teste cross-escola provando 403 quando JWT de escola A tenta acessar recurso de escola B.
Sanitização de erros
Adicionar fixtures de erros Postgres reais (23505, 23503, 42501, PGRST*) ao suite unit de error-helpers.ts garantindo que nenhuma jargão técnica vaza pra UI.
Smoke RTL das páginas admin (10 páginas)
Render sem crash + presença do título + ausência de erros no console.
P2 — Cobertura de feature
Hooks restantes (46 sem teste — 57% do total)
Priorizar por:
- Hooks que fazem mutação (rollback, toast, queryKey invalidation)
- Hooks com derived data complexa (memo + filter)
- Hooks de Mural Olímpico (alto risco LGPD)
Edge Functions sem teste (~15)
mural-*(6 funções): OTP, login, snapshot, releasesportal-*: gateway, redirectimport-*: validação Excel + processamento background
Contratos externos (Contract suite)
| Provider | Cenário |
|---|---|
| Twilio (SMS) | retry, falha 4xx, custo > 0 |
| Cloudflare Worker (gateway) | redirect whitelisted, anti-loop |
| Resend (transactional) | já coberto via webhook — falta envio |
E2E flows críticos (Playwright)
- Login (CPF/INEP/CNPJ) + OTP + role switch
- Criação de aplicação pelo coordenador
- Aprovação de inscrição pelo especialista
- Ciclo trial → pagamento MP → fatura paga
P3 — Qualidade transversal
| Item | Ferramenta | Gate |
|---|---|---|
| Performance budgets (LCP/TBT) | Lighthouse CI | LCP < 2.5s nas rotas top-5 |
| Acessibilidade A11y | axe-core + Playwright | Zero violações serious/critical |
| Visual regression | Playwright snapshots | Telas admin + portal |
| Cobertura mínima | Vitest coverage | ≥ 60% global, ≥ 80% em lib/ e _shared/ |
P4 — Observabilidade do CI
- Upload de coverage (Codecov ou artifact persistente)
- Badge no README
- Tempo por job + alerta
>10min - Mutation testing (Stryker) em
_shared/esrc/lib/
Métricas de saída (definição de pronto do backlog)
| Métrica | Hoje | Meta |
|---|---|---|
| Edge Functions com teste | 74% | 100% |
| Hooks com teste | 43% | ≥ 80% |
| Páginas com smoke test | 0% | 100% |
| Contratos externos com suite | 1/5 | 5/5 |
Cobertura IDOR admin-* | parcial | 100% |
| Cobertura global (Vitest + Deno) | desconhecida | ≥ 60% |
Backlog de Features (produto)
✅ Boleto Bancário Mercado Pago — CONCLUÍDO (2026-06-17)
Entrega original (2026-06): emissão automática via CRON + manual via UI escola/admin, adapter createBoleto/cancelBoleto em _shared/payment-gateway.ts, edge function mercadopago-boleto (actions gerar/cancelar), hook useMercadoPagoBoleto, campos boleto_* em escola_faturas, doc canônica em features/billing/boleto-manual.md.
Cobertura de testes entregue:
| Camada | Arquivo | Cenários |
|---|---|---|
| SSOT régua (front+edge) | src/lib/__tests__/cobranca-regua.test.ts + supabase/functions/_shared/__tests__/cobranca-regua.test.ts | D+3/D+10/D+17, paridade |
| Adapter | supabase/functions/_shared/__tests__/payment-gateway-boleto.test.ts | Payload bolbradesco, idempotency-key, vencimento BRT, erros 400/401/429 |
| Edge fn (contract) | supabase/functions/mercadopago-boleto/__tests__/index.test.ts | CORS, 401 auth, action inválida, GET 405 |
| Edge fn (IDOR) | tests/security/idor-mercadopago-boleto.test.ts | Cross-escola 403 (gerar/cancelar) |
| CRON background | supabase/functions/faturamento-cron/__tests__/boleto-background.test.ts | Skip CNPJ, skip endereço, skip migrada/pago, skip já-tem-boleto, sucesso → log boleto_emitido |
| Webhook | supabase/functions/mercadopago-webhook/__tests__/boleto-bolbradesco.test.ts | Notificação de boleto rejeitada por HMAC (security) |
| Hook UI | src/hooks/__tests__/useMercadoPagoBoleto.integration.test.tsx | gerar/cancelar + invalidação cache + forcar=true |
| E2E (gated) | e2e/billing-boleto.spec.ts | Escola gera boleto; admin reemite/cancela via drawer (BILLING_E2E_ENABLED=1) |
Lacunas remanescentes (não bloqueiam — registrar como P3):
- RTL completo de
pagamentos-escola.tsx(655 linhas, ~12 hooks) eescola-drawer.tsx(admin) — esforço alto vs ganho marginal (lógica de boleto já coberta por hook + adapter- E2E). Tratar quando feature for refatorada.
- Happy path real do adapter contra MP sandbox (custo $$, fica fora de CI).
Ref: SSOT em features/billing/boleto-manual.md, memória mem://features/billing/boleto-manual-ui-backlog.
Como contribuir com este backlog
- Encontrou superfície sem teste? Adicione linha à tabela P1/P2/P3 conforme criticidade.
- Concluiu item? Mover para
## Históricono final + atualizar Métricas. - Mudou prioridade? Atualizar com nota
decisão YYYY-MM-DD: motivo.
Este arquivo é referenciado em:
docs/development/TESTING_MASTER.md(seção "Backlog")docs/development/AUDIT.md(@audit testes)- Sidebar:
Testes > Backlog
Billing — Boleto Manual (triagem 2026-06-20)
Cards abertos a partir da triagem dos itens fora de escopo do MVP. Detalhe em docs/features/billing/boleto-manual.md §Roadmap pós-MVP.
- B-BOL-1 (P2) — Feriados nacionais no D+3: tabela
feriados_nacionais+ helper SSOTferiados-br.ts(front+edge); cobertura com véspera de Natal, Carnaval (móvel via Gauss), feriado em sábado. Roadmap:mem://features/billing/feriados-dias-uteis-roadmap. - B-BOL-2 (P2) — Histórico append-only de boletos por fatura: tabela
escola_fatura_boleto_historico+ persistência em todogerar/cancelar; pré-requisito para ADR-017. Testes: persistência multi-emissão, IDOR cross-escola, query helper. - B-BOL-3 (P2) — Refund de boleto pago: action
reembolsarseparada decancelar; adapterrefundPaymentnopayment-gateway.ts; webhook idempotente para eventorefunded. Testes: adapter, edge contract, IDOR, webhook idempotência. - B-BOL-4 (P3) — Rate limit por escola em
gerar: reusarportal_rate_metrics; default N=5/min/escola. Teste: 6ª chamada em 60s retorna 429 comRetry-After. - B-BOL-5 (P2, gated ADR-017) — Override de valor pelo admin: 6 testes obrigatórios listados em ADR-017 §2.7. Não criar testes antes do ADR ser aceito.
Histórico
- 2026-05-09 — Incidente CI (deno-tests + security-tests): 477 falhas Deno + 1 falha security tests classificadas em AUDIT_TEST_BREAKAGE_2026-05-09. Corrigidos: env do step
deno-tests(passada via secrets STAGING_*),pdf-helpers-xss.test.tsmovido para suite jsdom. Em aberto: D1 (fallback hardcoded eme2e-login.contract.test.ts), D2 (reavaliar 401 antes de Zod após CI verde), D3 (helperrequireEnv). Confirmado: OLP não depende de Node 20 (CI em Node 22). - 2026-05-09 — Sprint P0 (CI Fix) concluída: 794 Vitest tests + 600 Deno tests agora rodam em CI (antes só 4 sentinelas).
getUserFriendlyErrorganhou padrões para Postgres/PGRST/connection. Modais de senha alinhados ao SSOT "Sua segurança importa".