plans(OPS34-OPS36): acelerar o processo de verificação e2e — paralelizar job CI, paradigma HTTP sem browser, journeys seriais

This commit is contained in:
Francisco Solla 2026-08-10 12:29:42 -03:00
parent 21056eb384
commit 52ad9d8f85
3 changed files with 216 additions and 0 deletions

View file

@ -0,0 +1,73 @@
# CI e2e: paralelizar o job (cadeia serial só em dev + sharding do e2e)
Status: rascunho
Atualizado em: 2026-08-10
Issue: #599
Priority: P1
Model: composer-2.5
Impeccable: A — N/A (sem UI; infra de CI/testes)
Canvas UI: N/A — sem UI
Appetite: ~1 dia eng; um outcome verificável
Responsável: —
## Intenção
O job `e2e` é o mais longo da cascata CI (medido 2026-08-10: **617–635 s** no `ci.yml` de main, incluindo build+migrate+seed; job `int` ~248 s, `build` ~224 s). Duas fontes de serialização identificadas:
1. **Cadeia de projetos no Playwright** (`setup → campaign → frontend → admin`, `playwright.config.ts`): existe para o `setup` preaquecer rotas no **dev-mode** (compile frio do Next dev). No CI o servidor é o **build de produção** (`pnpm start`), onde não existe compile — o prewarm é custo puro e `frontend` (1 teste) e `admin` (3 testes) ficam travados depois de toda a família `campaign` (~29 arquivos).
2. **Um único runner** executa o job inteiro (~10 min), enquanto o runner fica ocioso na maior parte do tempo (2 workers Playwright).
Queremos o mesmo conjunto de testes, as mesmas asserções, com o job e2e rodando na metade do tempo e crescendo de forma sustentável.
## Persona e fluxo
- **Persona / contexto:** time de engenharia (humanos e agentes) esperando o merge em main / PR para deploy.
- **Job principal:** saber que o e2e passou no menor tempo possível, com o mesmo nível de confiança.
- **Fluxo desejado:** push/merge → job e2e conclui em ~metade do tempo atual → deploy desbloqueado antes.
- **Anti-goals de produto:** não remover/reduzir cobertura; não mudar asserções; não piorar o dev local (onde o prewarm ainda tem valor).
## Objetivo e aceite
- Job `e2e` do `ci.yml` (main, suite completa) cai de ~10 min para ≤ ~6 min de wall time (mesma classe de runner, comparando contra a linha de base de 2026-08-10).
- A mesma suite completa roda no CI de main e no modo full de PR — nenhuma spec removida nem alterada.
- O dev local (`pnpm test:e2e` sem CI/E2E_PROD) mantém o comportamento atual de prewarm e cadeia — a experiência de quem escreve specs não muda.
- A configuração de workers do CI é explícita e **não** altera o default local (que permanece 2).
- Sem classe nova de flake: a contagem de vermelhos por causa de paralelismo não piora vs. a linha de base (flakes conhecidos já ledgerados — 1-worker de `campaignMunicipalities`, ordem de arquivo de `campaignLeaderships` — são rastreados à parte).
## Dados (intenção)
Dados: N/A — não há superfície de dados de produto; a única métrica é duração do job CI (registrar antes/depois no changelog).
## Direção no codebase (hipótese)
- **Áreas prováveis:** `.github/workflows/ci.yml` e `ci-pr.yml` (job `e2e`), `playwright.config.ts` (projects/dependencies), `scripts/gate-ci.mjs` (paridade local), `scripts/run-e2e-affected.mjs`.
- **Precedente a olhar:** o job já usa `NEXT_DIST_DIR: .next/e2e` e `pnpm start` no CI (o prewarm do `setup.e2e.spec.ts` é documentado como dev-only); `test:e2e:affected` mostra o padrão de seleção de specs.
- **Risco de acoplamento:** sharding duplica o build por runner se cada shard compilar sozinho (~4 min de build por shard) — a escolha build-uma-vez-com-artefato vs. build-por-shard é uma decisão de engenharia com trade-off real (ver Questões em aberto); a cadeia de projetos precisa continuar existindo em dev.
## Dependências
- Nenhuma dura. Beneficia (mas não depende de) OPS35 (paradigma HTTP reduz o tamanho do e2e).
## Fora de escopo
- Mudar/remover asserções de qualquer spec (cada spec mantém o que garante hoje).
- Reduzir o que a CI roda (suite completa de main continua completa; seleção afetada do PR continua).
- Otimizar o job `build`/`int` (são ~4 min; fora deste item).
- Corrigir flakes pré-existentes (são outros items já rastreados).
## Rabbit holes de produto
- **"Melhorar o paralelismo também em dev"**: mexer na cadeia/prewarm do dev-mode para "ganhar mais" — quebra exatamente o que estabiliza o dev local sob carga. **Corte neste item:** CI/prod-mode apenas.
- **"Aproveitar e subir o default de workers para 4"**: altera o dev local e amplifica a classe de flake por carga. **Corte:** workers do CI via env explícita; default local intocado.
- **"Shard por família de spec para cache/afinidade"**: complexidade sem ganho medido. **Corte:** sharding padrão do Playwright (`--shard`), deterministicamente por teste.
## Questões em aberto (produto)
- **O build do e2e fica por shard ou vira artefato compartilhado?** **Opções:** A) cada shard compila (simples, wall time ≈ build + teste/2); B) job `build-e2e` próprio + upload/download de artefato `.next/e2e` (testes mais rápidos, acoplamento de artefato e custo de download). **Recomendação:** começar em A (simples, deterministicamente verde) e migrar para B se o wall time do build dominar depois que os testes encolherem. _(assumido — validar no gate)_
- **Quantos shards?** **Opções:** 2 | 3 | N pelo tamanho do job. **Recomendação:** 2 no primeiro passo (metade do wall time, sem estourar a quota de runners), reavaliar com medição. _(assumido — validar no gate)_
- **Workers do CI: manter 2 ou subir?** **Opções:** manter 2 | 3 | 4 via env. **Recomendação:** manter 2 inicialmente (a paralelização do shard já entrega o ganho; workers adicionais exigem medir `max_connections` do Postgres service e a classe de flake por carga). _(assumido — validar no gate)_
## Referências
- GitHub Issue #599
- `playwright.config.ts` (projects/cadeia/workers), `.github/workflows/ci.yml` + `ci-pr.yml` (job e2e), `scripts/gate-ci.mjs`, `tests/e2e/setup.e2e.spec.ts` (prewarm dev-only), `docs/CHANGELOG-AGENTS.md` (entradas OPS20/OPS30, medições de job e2e)

View file

@ -0,0 +1,73 @@
# E2E full-stack sem browser (paradigma HTTP) para asserções de servidor
Status: rascunho
Atualizado em: 2026-08-10
Issue: #600
Priority: P1
Model: composer-2.5
Impeccable: A — N/A (sem UI de produto; infra de testes)
Canvas UI: N/A — sem UI
Appetite: ~1–1,5 dia eng; um outcome verificável
Responsável: —
## Intenção
Uma parte grande dos testes e2e existe para garantir **comportamento de servidor**: criar fixture → navegar até uma lista/detalhe → o servidor renderiza o dado certo para o papel certo (RBAC), persistência, redirects/404, rotas JSON de mutação. Esses testes pagam browser + hidratação + interação client mesmo quando a asserção é 100% server-side.
Já existe a prova de que o paradigma funciona: o `setup.e2e.spec.ts` roda contra o servidor real **sem lançar browser** (só a fixture `request` do Playwright) — cobrindo SSR, cookies, redirects e as rotas JSON POST de `/campanha`.
Queremos um modo de spec e2e full-stack **sem browser** (servidor + DB reais, HTTP real, sem UI): as famílias cujas asserções são de servidor migram para ele, mantendo as mesmas garantias e gastando uma fração do tempo — e a suíte continua crescendo sem crescer o browser.
## Persona e fluxo
- **Persona / contexto:** agentes e humanos mantendo/estendendo a suíte e2e; CI rodando o job e2e.
- **Job principal:** garantir o contrato HTTP/SSR/RBAC/persistência das rotas de campanha sem pagar o custo do browser.
- **Fluxo desejado:** spec nova de família "sem browser" → roda no mesmo job e2e (mesmo servidor e DB) → assere o mesmo conteúdo renderizado e o mesmo efeito de persistência que o teste de browser asseria → verde em ~frações do tempo.
- **Anti-goals de produto:** não é para substituir os testes de browser onde o client importa (hidratação, autosave otimista, Drawers, WebAuthn, PWA, viewports mobile, guard de console); não é para chamar server actions por HTTP cru (protocolo RSC criptografado — frágil e caro de manter).
## Objetivo e aceite
- Um modo novo de spec e2e roda no job e2e existente **sem lançar browser**, contra o servidor real (prod build no CI, dev local idem).
- A primeira leva de famílias migra com **asserções equivalentes às atuais** (mesmos fixtures, mesmas expectativas — o que era DOM vira conteúdo renderizado/efeito verificado) — nada é removido sem equivalente em outro lugar.
- Migração com medição: para cada família migrada, tempo de browser vs. tempo sem browser registrado no changelog (o ganho é o outcome, não o artefato).
- RBAC continua assegurado: o escopo de assessor/liderança é asserido no conteúdo renderizado pelo servidor (ex.: lista de um assessor não contém municípios fora do portfolio dele).
- Autenticação funciona no modo sem browser (sessão real de campanha, cookie `campaign-token`), sem duplicar lógica de auth no teste.
- O guard de falhas client (console.error/pageerror) permanece exclusivo das specs de browser — o modo HTTP não finge cobrir o que ele não observa.
## Dados (intenção)
Dados: N/A — não há superfície de dados de produto; métrica de processo = wall time por família migrada.
## Direção no codebase (hipótese)
- **Áreas prováveis:** `tests/e2e/` (nova família ou projeto no `playwright.config.ts`), fixtures existentes `tests/e2e/fixtures/campaignE2EFixtures.ts` (reuso do ownership/cleanup), `tests/e2e/setup.e2e.spec.ts` (precedente da fixture `request`), rotas JSON `/campanha/...` já criadas por `campaignJsonMutationRoute`.
- **Precedente a olhar:** `setup.e2e.spec.ts` (request fixture sem browser); `tests/helpers/login.ts`/`seedUser.ts` (auth reutilizável); docs/plans `porta-e2e-isolada-por-worktree.md` (contrato do job).
- **Risco de acoplamento:** a sessão precisa chegar ao `request` context (storageState/cookie); fixtures de dados continuam sendo criados via Payload in-process (o mesmo padrão atual) — não criar um caminho de seed paralelo.
## Dependências
- Nenhuma dura. Beneficia-se de OPS34 (job e2e mais rápido) e da existência das rotas JSON (já presentes).
## Fora de escopo
- Migrar famílias de interação/hidratação (WebAuthn, PWA, viewports, Sollinha/chat, sticky header, colunas/saved filters, autosave) — continuam em browser.
- Chamar server actions via HTTP cru (ação Next criptografada).
- Criar um runner/modo de teste fora do Playwright (o paradigma é um modo Playwright sem browser, não um framework novo).
- Reduzir o que a suíte garante hoje (asserções migradas 1:1 em cobertura).
## Rabbit holes de produto
- **"Migrar tudo para HTTP"**: o guard de console e a hidratação são justamente o que o browser testa — migrar tudo perderia segurança. **Corte neste item:** apenas asserções de servidor, com medição por família.
- **"Reimplementar login sem browser"**: duplicaria a lógica de sessão do app no teste. **Corte:** sessão real via cookie/storageState produzida pelo fluxo existente.
- **"Aproveitar e criar um framework de teste próprio"**: o Playwright `request` já cobre servidor+DB+HTTP; framework paralelo seria custo sem ganho. **Corte:** paradigma dentro do Playwright.
## Questões em aberto (produto)
- **Como o teste sem browser obtém a sessão de campanha?** **Opções:** A) um login de browser real por run (setup) → `storageState` → `request.newContext` com o cookie; B) login via API REST do Payload e cópia manual do token/cookie. **Recomendação:** A — usa exatamente o fluxo real de login e o cookie canônico `campaign-token`, sem duplicar lógica. _(assumido — validar no gate)_
- **Convenção de nomenclatura e projeto?** **Opções:** nova família `*.http.e2e.spec.ts` no projeto `campaign` existente | projeto Playwright próprio sem dependências. **Recomendação:** família própria no projeto existente (menos configuração, mesmo servidor). _(assumido — validar no gate)_
- **Qual a primeira leva de famílias?** **Opções:** agenda/feed e atividades (asserções de render+persistência) | lideranças e municípios (as mais pesadas) | começar por uma família pequena para validar o paradigma. **Recomendação:** começar pequeno (uma família com poucas specs) para provar o paradigma com medição, depois escalar para as pesadas. _(assumido — validar no gate)_
## Referências
- GitHub Issue #600
- `tests/e2e/setup.e2e.spec.ts` (prova do paradigma), `tests/e2e/fixtures/campaignE2EFixtures.ts` (ownership/cleanup reutilizável), `playwright.config.ts`, `src/utilities/campaignJsonMutationRoute.ts` (rotas JSON HTTP-testáveis), `docs/CHANGELOG-AGENTS.md`

View file

@ -0,0 +1,70 @@
# E2E: journeys seriais nos arquivos pesados (amortizar overhead por teste)
Status: rascunho
Atualizado em: 2026-08-10
Issue: #601
Priority: P2
Model: composer-2.5
Impeccable: A — N/A (sem UI de produto; infra de testes)
Canvas UI: N/A — sem UI
Appetite: ~1 dia eng; um outcome verificável
Responsável: —
## Intenção
Três arquivos concentram uma fração grande da suite e2e: `campaignHomeActions` (17 testes), `campaignMunicipalities` (20 testes) e `campaignBottomNav` (9 testes). Cada teste paga sozinho o mesmo overhead fixo: inicializar o Payload in-process (`getPayload`), criar fixtures próprios, login + `waitForURL` no browser, e o cleanup com `discoverOwnedRows` (~10 queries + transação de delete). Esse overhead se repete 46 vezes onde poderia ser pago uma vez por jornada.
Queremos transformar esses arquivos em **journeys seriais**: um `beforeAll` que paga a inicialização e o login uma única vez, e testes sequenciais dentro do mesmo worker aproveitando a sessão — sem perder granularidade de diagnóstico nem reduzir asserções.
## Persona e fluxo
- **Persona / contexto:** agentes e humanos rodando/estendendo a suite e2e; CI no job e2e.
- **Job principal:** as mesmas 46 garantias, com o wall time dos três arquivos reduzido pela amortização do overhead repetido.
- **Fluxo desejado:** rodar `campaignHomeActions`/`campaignMunicipalities`/`campaignBottomNav` → cada arquivo inicializa payload + sessão uma vez → testes sequenciais reusam a sessão → falha continua apontando para o teste exato.
- **Anti-goals de produto:** não é "um mega-teste" (retry caro, falha no meio perde diagnóstico); não é aplicar a todos os 32 arquivos (perda de paralelismo sem ganho).
## Objetivo e aceite
- Wall time dos três arquivos cai de forma mensurável (registrar antes/depois no changelog).
- Cada teste continua sendo um teste separado no relatório — falha localizada, retry por teste preservado.
- Nenhuma asserção removida nem enfraquecida; nenhum caminho deixa de ser testado.
- A classe de flake por interação não piora: a ordem serial é estável e o retry continua por teste.
- Arquivos com testes de viewport/estado variado não são forçados ao padrão (decisão por arquivo, com medição).
## Dados (intenção)
Dados: N/A — não há superfície de dados de produto; métrica de processo = wall time por arquivo.
## Direção no codebase (hipótese)
- **Áreas prováveis:** `tests/e2e/campaignHomeActions.e2e.spec.ts`, `tests/e2e/campaignMunicipalities.e2e.spec.ts`, `tests/e2e/campaignBottomNav.e2e.spec.ts`, fixtures `tests/e2e/fixtures/campaignE2EFixtures.ts` + `e2eTest.ts` (sessão/storageState compartilhado).
- **Precedente a olhar:** `tests/e2e/fixtures/campaignE2EFixtures.ts` (ownership por runID — já tolerante a reuso de worker), `playwright.config.ts` (fullyParallel por arquivo), padrões de `test.describe.configure({ mode: 'serial' })` no repo.
- **Risco de acoplamento:** testes hoje paralelos dentro do arquivo passam a ser seriais — a sessão compartilhada muda o isolamento (cookies por contexto); o cleanup de fixtures continua necessário por teste (ownership por runID já cobre).
## Dependências
- Nenhuma dura. Composição com OPS35 (specs sem browser) e OPS34 (job CI): cada um é independente e mensurável à parte.
## Fora de escopo
- Migrar os outros ~29 arquivos para o padrão serial (decisão por medição, não por princípio).
- Mesclar testes num journey único (perde granularidade de retry/diagnóstico).
- Mudar asserções ou caminhos cobertos.
- Corrigir flakes pré-existentes (ledger separado).
## Rabbit holes de produto
- **"Fazer tudo serial para ganhar mais"**: paralelismo entre arquivos é o que mantém a suite dentro do budget — serializar tudo inverte o ganho. **Corte neste item:** só os três arquivos medidos, com decisão por arquivo.
- **"Compartilhar fixtures entre testes para economizar mais"**: acopla estado e transforma falha isolada em falha em cascata. **Corte:** fixtures continuam por teste; só payload+sessão são amortizados.
- **"Aproveitar para 'limpar' asserts redundantes"**: reduzir asserções muda o contrato de segurança. **Corte:** zero asserções removidas.
## Questões em aberto (produto)
- **Login compartilhado por arquivo ou por grupo de testes?** **Opções:** A) um login no `beforeAll` do arquivo inteiro (máximo de amortização, jornada longa); B) um login por `describe` (granularidade média, falha contida). **Recomendação:** B — um login por grupo de jornada coeso, balanceando amortização e isolamento de falha. _(assumido — validar no gate)_
- **Sessão via storageState ou login reutilizado no worker?** **Opções:** A) `storageState` salvo no primeiro login; B) reutilizar a página/contexto do `beforeAll` em modo serial. **Recomendação:** B — no modo serial do mesmo worker, reusar o contexto do grupo é o padrão Playwright mais simples e determinístico. _(assumido — validar no gate)_
- **Ordem de ataque?** **Opções:** começar pelo arquivo de maior overhead absoluto (medir primeiro) | começar pelo menor risco (homeActions/BottomNav) | todos juntos. **Recomendação:** medir os três e começar pelo que paga mais cedo — o executor decide com a medição na mão. _(assumido — validar no gate)_
## Referências
- GitHub Issue #601
- `tests/e2e/campaignHomeActions.e2e.spec.ts`, `tests/e2e/campaignMunicipalities.e2e.spec.ts`, `tests/e2e/campaignBottomNav.e2e.spec.ts`, `tests/e2e/fixtures/campaignE2EFixtures.ts`, `playwright.config.ts`, `docs/CHANGELOG-AGENTS.md`