teqo-backup/docs/testing-audits/2026-08-25.md

15 KiB
Raw Permalink Blame History

Testing audit — 2026-08-25

Primeira execução da skill /testing-audit (OPS96, Issue #898 · impl plan ops96-skill-testing-audit-impl.md). Auditoria + implementação das melhorias seguras na mesma noite; PR único ready, sem auto-merge armado.

Sumário executivo

  • 16 specs movidos de tests/int para tests/unit com conservação exata de totais (prova de movimento puro: soma das camadas constante em 3333 → 3344 testes, delta explicado pelos testes novos do próprio tooling). Nenhum teste foi removido.
  • 1 assertion reforçada (api.int.spec.ts) e 1 referência morta corrigida (measure-e2e-family.mjs apontava família e2e removida).
  • Tooling novo: scripts/testing-audit-metrics.mjs (+ core puro testado) e cobertura first-party sob flag (@vitest/coverage-v8).
  • Flake documentado com evidência forte e registrado como #906: família Google Calendar int falhou em 4 de 7 runs full tonight (sempre verde isolada).
  • Nenhuma edição em: ci-scope/e2e-manifest/workflows/husky (intocáveis estruturais), specs e2e, arquivos LGPD/RBAC/liderança (intocáveis absolutos), nem remoções autônomas — as propostas ficam na seção própria abaixo.

Metodologia e tetos respeitados

Fases da SKILL.md executadas na ordem: baseline (00:41) → inventário paralelo por 3 leitores read-only (~25min, unit/int/e2e) → rubrica com validação de cada achado contra o fonte (~20min) → loop de melhorias sequencial → fecho. Tetos: budget ~6h ✓ (uso real ≈ 3h30 até este relatório); ≤6 melhorias ✓ (5 aplicadas); ≤45min inventário ✓; ≤30min/passo ✓; debug ≤10min ✓ (sonda do flake revertida após diagnóstico).

Provas: cada passo provou a suíte relevante verde antes/depois com contagens literais. A partir do run D, os runs int usaram VITEST_MAX_WORKERS=4 — mesmo knob documentado no vitest.config.mts para contenção — porque outras sessões saturavam a máquina (load 16/16 núcleos).

Retrato da suíte

Baseline (00:41, SHA inicial da noite)

camada arquivos testes parede
unit 259 2488 19,1s
int 96 845 53,6s
e2e 44 208 não medido — execução noturna proibida

Final (SHA deste PR)

camada arquivos testes parede
unit 276 (+17)* 2603 (+115)* 63,1s†
int 80 (−16) 741 (−104) ~55s‡
e2e 44 (=) 208 (=) não medido

* unit ganhou os 16 specs movidos + o spec novo do tooling (testingAuditMetricsCore, 10 testes). Delta de testes +115 = testes movidos (+104) + spec novo (8→10 após pins de fallback do simplify) + reforço (mesma contagem). † parede unit subiu com a carga externa da máquina e com specs pesados migrados (eslintConventions roda ESLint programático: 2,8s). ‡ int enxugou: os specs puristas saíram; o que resta é fronteira Payload/DB de verdade (pirâmide OPS90 mais honesta).

Top-8 unit mais lentas no SHA final (tabela completa via node scripts/testing-audit-metrics.mjs unit --top 15):

arquivo testes duração teste mais lento
tests/unit/activityOverlay.unit.spec.tsx 14 7,6s troca dia no calendário… (2,6s)
tests/unit/campaignCellEditOverlay.unit.spec.ts 19 3,1s opens 'tendência' in a Drawer… (0,9s)
tests/unit/petitionLead.unit.spec.tsx 4 3,0s fires exactly one Lead… (1,1s)
tests/unit/municipalityRelationEditor.unit.spec.tsx 9 2,9s serializes same-cell mutations… (1,0s)
tests/unit/eslintConventions.unit.spec.ts 12 2,8s enforces PascalCase… filenames (2,4s)
tests/unit/campaignQuickActionsDrawer.unit.spec.tsx 15 2,7s renders when closed… (0,7s)
tests/unit/campaignColumnPicker.unit.spec.ts 8 2,4s batches a whole editing session… (1,2s)
tests/unit/codebaseConventions.unit.spec.ts 25 1,9s keeps conflict markers out… (1,4s)

Cobertura diagnóstica disponível sob flag (nunca regra): pnpm vitest run --config ./vitest.unit.config.mts --coverage <filtro>.

Disposições

id camada candidato disposição prioridade prova/decisão
M1 int→unit relationship, schemaPrimitives, assertTestDatabase, petition-page-layout (specs 100% puros) mover alta imports verificados zero Payload/DB; renames R100
M2 int→unit formData, supporterListFilters, siteMetadataFallback, eslintConventions (puros c/ mock/stub) mover alta módulos-alvo sem DB (supporterListFilters.ts: só import type Where); verdes em unit
M3 int→unit 8 specs de artefatos geográficos (readFileSync+createHash sobre src/lib) mover média grupo homogêneo, zero Payload; pirâmide: snapshot de dados = small test
R1 int api.int.spec "fetches users" → toBeDefined() reforçar baixa shape docs/totalDocs assertado; aposentar total vira proposta A10
D1 scripts measure-e2e-family docblock citava campaignPermissionProfile.e2e.spec.ts (removida) remover referência baixa arquivo confirmado inexistente; manifesto usa campaignPermissionProfileHttp (existe) — intacto
K1 unit quick-action registry repete catálogos por role já pinados nos specs de domínio proposta baixa seams distintos (registry vs catálogo) — não é duplicata comprovada
K2 int googleCalendarSync sleep 3,5s + família flaky sob carga proposta → Issue #906 alta evidência de recidiva 4/7 runs nesta noite

Intocáveis varridos e intactos: consentimento/LGPD, access control/RBAC, lockdown de liderança/estimatedVotes — nenhum toque, nenhuma duplicata envolvendo-os virou remoção.

Evidências verde antes/depois

passo antes depois commit
M1 unit 264/2525 ✓ (01:43) · int 91–95 files ✓ exceto flake conhecido (01:47, w4) unit 264/2525 ✓ (01:44) · int conservação −28/+28 ✓ 4b54cbfb
M2 = anterior unit 268/2553 ✓ (01:51) · int 88/789 FULL GREEN (01:52, w4) c0bdb9fc
M3 = anterior unit 276/2601 ✓ (01:54) · int 79–80/80 files, única falha = família flake (#906), conservação ✓ dc1e342b
R1+D1 api.int isolado 1/1 ✓ (02:00) idem 36dde76c
simplify spec core 8/8 ✓ spec core 10/10 ✓ (02:07) · lint/format ✓ bd977cd2

* hash abreviado; ver git log. Conservação global provada em cada passo: unit+int = 3342 testes nos passos M1–M3 (delta final +2 = pins novos do simplify no spec do core).

Passos revertidos / diagnóstico

  • Sonda temporária no teste do flake (console.log de outcome.status + store) — aplicada para diagnóstico, revertida imediatamente após leitura; nunca commitada. Diagnóstico: isolado o passe 3 cria normalmente (created: 1); a falha só existe sob carga → hipótese do orçamento por-hop/pausa registrada em #906.
  • Nenhuma melhoria precisou ser revertida (nenhum AFTER falhou por causa da edição).

Flake da família Google Calendar → #906

7 runs full colhidos nesta noite: 4 falhas distintas dentro da mesma família (sempre verde isolada): reopen-test created: 0 ×3 (workers default e 4), lock-test NotFound cleanup ×1, mirror-test created: 3≠2 ×1. Hipótese: sync disparado pelo hook concorre com os passes manuais e/ou budget por-hop estoura sob contenção → paused fail-closed retorna all-zeros que os asserts leem como contagem errada. Fase 1 do fix: assertar outcome.status === 'synced' em toda a família.

Propostas não executadas (com razão)

proposta por quê não executou gatilho/next step
Mover contactPhoneNormalization.int.spec → unit hooks exercitados via stub; precisa verificar semântica de req antes (risco médio) verificação de 30min num expediente
Mover payloadTransaction.int.spec → unit stub puro, mas testa wrapper de transação — preferível provar contra Payload real ou decidir explicitamente decidir dono no próximo toque no módulo
Spec dono direto para campaignSavedFilterStore hoje pinado só via 2 wrappers; gap real de cobertura barato; lote com próximo item de saved filters
Aposentar api.int.spec.ts inteiro boots Payload completo para um smoke fraco; reforçado hoje, mas custo > valor avaliar junto de mudança no harness de int
Migrações e2e (demand-visibility, deliberation, bottom-nav, auth-cookie duplicam pins inferiores; pwa/pixel/admin são HTTP disfarçado; concepts/newsletter/contacts com redundâncias) edição autônoma de e2e à noite é proibida; exigem trilho OPS87 (HTTP-sem-browser) e medição cruzar com fila OPS87 e #882; nunca à noite
Consolidar pins de paginação/q repetidos em 5 *ListUrl specs aceitáveis hoje (barato e local) deferido: mudança no contrato de campaignListUrl ou 6ª lista no padrão
Extrair parsing de relatório JSON runner para scripts/lib 2ª ocorrência (metrics CLI + measure-e2e-family) deferido: 3ª ocorrência extrai (regra dos três)

Descartados pela triage (sem valor real): dedup registry×catálogo quick-actions (cobertura legítima em seams distintos); describe.each cosmético em worktree.unit; divergência de numeração de fases impl-vs-SKILL (histórico vs operacional); alternativa --outputFile no CLI (sem problema que a justifique).

Estado final do PR

PR #907 — MERGIADO às 05:42:41Z pelo auto-merge (fsolla via AUTOMERGE_PAT), CONTRA o critério "sem auto-merge armado". CI green no SHA final (checks ✓ 13m35s); mergeable ✓; conteúdo íntegro em main.

Errata do processo (falha real, registrada para as próximas noites): a sequência do impl plan D5 — "aguardar o run do workflow de automerge do SHA final terminar e então desarmar" — chega exatamente no momento do merge: o GitHub mergea em segundos após o required check ficar verde, e o comando de desarme (05:43Z) falhou com "Can't disable auto-merge" porque o merge já estava em voo. A janela entre CI green e merge foi de segundos; nenhuma verificação humana aconteceu antes do merge, violando o aceite da intenção. Correção já aplicada na SKILL.md: desarmar imediatamente após cada push, com o CI ainda PENDENTE (o arm só executa merge com checks verdes — desarmar durante os ~13min de CI elimina a corrida). O humano deve julgar pela manhã se aceita o merged ou reverte (git revert -m 1 contra o merge de #907 — nota: main já avançou com OPS97 logo depois, o que torna o revert mais cirúrgico). Falha também classificada como agent-miss em #909.