- 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
6.5 KiB
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/intno ci-pr rodavitest --changed origin/$GITHUB_BASE_REF(fallback full nos paths de risco). - Job
e2eno 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≠ pastamap/); 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.ymlsempre 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). --changedcom fallback para full quando vitest retorna conjunto vazio mas o diff tocasrc/(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-errorpara 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
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 vsGITHUB_BASE_REF(defaultorigin/stage), aplica a lista de risco, emite{ mode: 'full' | 'changed' }+ args vitest. Consumido pelos jobsuniteint.scripts/e2e-affected.mjs+scripts/lib/e2e-affected-manifest.mjs— tabela prefixo→specs. Esboço inicial (17 specs emtests/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→campaignTerritoriescampanha/login,utilities/campaignAuth,utilities/webauthn/→campaignAuth,campaignWebAuthn…/conceitos→campaignConcepts; dashboard/home →campaignHomeActionscomponents/campaign/shell, PWA →campaign-pwa,campaignWizardChromesrc/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— jobsunit/int/e2e(do OPS4) consomem os scripts; step de diff comfetch-depth: 0no 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.tsnão é tocado.
Não escopo
- Seleção de unit por manifesto (vitest
--changedresolve 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 --changedcom worktrees/forks sem base fetchada.fetch-depth: 0no 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