teqo-backup/docs/plans/testes-afetados-pr.md
Francisco Ignácio Fontoura Solla 2e0cc2c03e chore(ci): parallel PR jobs, affected tests, push gate, owner-of-red-CI policy
- ci-pr.yml: split the serial checks job into parallel lint/format/
  typecheck/knip/cycles + unit/int (vitest --changed vs PR base via
  scripts/test-affected.mjs, full fallback on high-risk paths) + e2e
  (manifest path->spec via scripts/e2e-affected.mjs, blocking) + build;
  migration-lock gains checkout + pull-requests:read (fixes the failures
  that blocked PR #50)
- ci.yml (main): full safety net — seed:minimal + int full + build + e2e
- package.json: gate:fast / gate:push; .husky/pre-push runs gate:push
- docs: "Dono do PR, dono do CI" policy in AGENT-OPS + work-issue +
  engineering-standards; changelog entry; plans for OPS3-OPS7
2026-07-30 17:37:20 -03:00

94 lines
6.5 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Testes afetados pelo diff no PR — vitest --changed + manifesto path→spec e2e
Status: entregue (2026-07-30 — executado em sessão única, fora do fluxo agent:register, a pedido do humano)
Atualizado em: 2026-07-30
Issue: —
Priority: P1
Model: composer-2.5
Impeccable: A — N/A (sem superfície UI)
Appetite: ~1–2 dias eng; script de seleção + manifesto + wiring nos jobs do OPS4 + pins
Responsável: —
## Dados → decisão → apresentação
Dados: N/A — infra de CI.
## Contexto
Com o ci-pr paralelizado (OPS4), o próximo corte é rodar **só o que o diff toca** em PRs de feature branch: hoje um PR de copy roda 523 testes int + 522 unit + build. Vitest suporta `--changed` contra uma base git; Playwright `--only-changed` **não** cobre mudanças em `src/` (specs e2e não importam o app), então a seleção e2e precisa de um **manifesto path → spec** curado no repo. Push em `stage`/`main` sempre roda suíte completa (decisão OPS4).
Decisão de produto (2026-07-30, brief do lote CI): fallback para suíte completa quando o diff tocar paths de alto risco — migrations, lockfile, configs de teste, seed manifest, collections/globals.
## Objetivos
- Job `unit`/`int` no ci-pr roda `vitest --changed origin/$GITHUB_BASE_REF` (fallback full nos paths de risco).
- Job `e2e` no ci-pr roda specs mapeadas pelo manifesto; fallback full idem.
- `scripts/e2e-affected.mjs` (ou equivalente) imprime a lista de specs e o motivo (decisão auditável no log do CI).
- Pins: unit test do manifesto (path→spec existe, spec existe no disco) + comportamento de fallback.
## Decisões travadas
- **Manifesto e2e = dados no repo, não inferência por import.** e2e não importa `src/`; a única fonte de verdade é uma tabela curada. **Rejeitado:** heurística por nome de spec (frágil — `campaignZoneMap` ≠ pasta `map/`); trace estático Playwright→app (não existe aresta de import).
- **Fallback full em paths de alto risco** (lista congelada no script): `src/migrations/`, `src/payload-types.ts`, `pnpm-lock.yaml`, `package.json`, `vitest*.config.*`, `playwright.config.ts`, `scripts/lib/seed-minimal-manifest.mjs`, `scripts/seed-minimal.mjs`, `src/collections/`, `src/globals/`, `src/payload.config.ts`, `tests/helpers/`, `tests/e2e/fixtures/`, `.env*`. **Rejeitado:** fallback por "mais de N arquivos" (tamanho ≠ risco); lista aberta por glob no workflow (regra fora de teste unitário).
- **Seleção só no ci-pr; `ci.yml`/`ci-stage.yml` sempre full.** Rede de segurança por alvo (decisão OPS4). **Rejeitado:** affected também no main (derrota o propósito do gate pós-merge).
- **`--changed` com fallback para full quando vitest retorna conjunto vazio mas o diff toca `src/`** (ex.: arquivo novo sem spec importando). **Rejeitado:** aceitar conjunto vazio como "nada a rodar" (falso verde em código novo sem teste ligado).
## Questões em aberto
- **e2e bloqueante no PR desde o dia 1?** **Decidido no gate (humano, 2026-07-30): bloqueante já.** A alternativa de 2 semanas em `continue-on-error` para calibrar o manifesto foi rejeitada pelo humano ("já resolve logo nessa sessão"). O manifesto fica visível nos logs (`unmapped`) para calibração contínua.
## Abordagem proposta
```mermaid
flowchart LR
diff["git diff origin/base...HEAD"] --> script["scripts/e2e-affected.mjs + scripts/test-affected.mjs"]
script -->|paths de risco| full[suites completas]
script -->|paths mapeados| sel["specs selecionadas + vitest --changed"]
sel --> jobs["jobs unit/int/e2e do ci-pr"]
full --> jobs
```
Componentes:
- **`scripts/test-affected.mjs`** — computa diff vs `GITHUB_BASE_REF` (default `origin/stage`), aplica a lista de risco, emite `{ mode: 'full' | 'changed' }` + args vitest. Consumido pelos jobs `unit` e `int`.
- **`scripts/e2e-affected.mjs` + `scripts/lib/e2e-affected-manifest.mjs`** — tabela prefixo→specs. Esboço inicial (17 specs em `tests/e2e/`):
- `src/app/(campaign)/campanha/(app)/municipios`, `src/components/campaign/municipality`, `src/utilities/municipality/` → `campaignMunicipalities`, `campaignZoneMap`, `campaignNearestMunicipality`, `campaignSavedFilters`, `campaignColumnPicker`
- `…/atividades`, `components/campaign/activity` → `campaignActivity`
- `…/liderancas`, `components/campaign/leadership` → `campaignLeaderships`
- `…/territorios`, `components/campaign/territory` → `campaignTerritories`
- `campanha/login`, `utilities/campaignAuth`, `utilities/webauthn/` → `campaignAuth`, `campaignWebAuthn`
- `…/conceitos` → `campaignConcepts`; dashboard/home → `campaignHomeActions`
- `components/campaign/shell`, PWA → `campaign-pwa`, `campaignWizardChrome`
- `src/app/(payload)` → `admin`; `src/app/(frontend)` → `frontend`
- qualquer outro `src/` não mapeado → log explícito "sem spec mapeada" (modo e2e: nenhuma; o gap fica visível no log para o humano julgar — decisão: não falhar, e2e é camada fina sobre int).
- **`.github/workflows/ci-pr.yml`** — jobs `unit`/`int`/`e2e` (do OPS4) consomem os scripts; step de diff com `fetch-depth: 0` no checkout.
- **Tests** — `tests/unit/testAffected.unit.spec.ts` + `tests/unit/e2eAffectedManifest.unit.spec.ts` (manifesto aponta para specs existentes; paths de risco disparam full).
Sem migration, sem código de app.
## Dependências
- **OPS4** (jobs paralelos existem para receber a seleção) — dura.
- Reusa: padrão `scripts/lib/` (B5 F2), `tests/helpers/assertTestDatabase.ts` não é tocado.
## Não escopo
- Seleção de unit por manifesto (vitest `--changed` resolve por import graph real).
- Cache de resultados de teste entre runs (Turborepo-style) — sem evidência de necessidade.
- Nightly full e2e → OPS4 (adiado lá).
## Rabbit holes
- **Manifesto vira segundo grafo de dependências.** Se alguém "só completar" granularidade por arquivo, vira manutenção morta. **Mitigação:** granularidade de prefixo de domínio; pin de unit test só na existência, não na completude.
- **`vitest --changed` com worktrees/forks sem base fetchada.** `fetch-depth: 0` no checkout; fallback full se o merge-base não resolver (log + exit code do script).
## Adiado com gatilho
- **Completude do manifesto enforced (falhar CI quando `src/` novo não mapeia).** Revisitar quando: 3º fallback full causado por prefixo ausente.
## Referências
- `tests/e2e/` (17 specs + fixtures), `playwright.config.ts`, `vitest.unit.config.mts`, `vitest.config.mts`
- `.github/workflows/ci-pr.yml` (jobs do OPS4)
- `docs/AGENT-OPS.md` — linha E2E da tabela "CI por alvo" (atualizar neste item)
- Vitest `--changed`: https://vitest.dev/guide/cli.html#changed