From 41c94b2bdc4ed1172ce392c7022ed14457a54277 Mon Sep 17 00:00:00 2001 From: Francisco Solla Date: Tue, 25 Aug 2026 02:22:57 -0300 Subject: [PATCH] =?UTF-8?q?docs(ops96):=20relat=C3=B3rio=20da=20primeira?= =?UTF-8?q?=20auditoria=20noturna=20+=20changelog?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../2026-08-25-ops96-testing-audit.md | 3 + docs/testing-audits/2026-08-25.md | 110 ++++++++++++++++++ 2 files changed, 113 insertions(+) create mode 100644 docs/changelog/2026-08-25-ops96-testing-audit.md create mode 100644 docs/testing-audits/2026-08-25.md diff --git a/docs/changelog/2026-08-25-ops96-testing-audit.md b/docs/changelog/2026-08-25-ops96-testing-audit.md new file mode 100644 index 00000000..86e867d9 --- /dev/null +++ b/docs/changelog/2026-08-25-ops96-testing-audit.md @@ -0,0 +1,3 @@ +# OPS96 — skill /testing-audit: auditoria autônoma da suíte com melhorias aplicadas + +**Recently resolved (2026-08-25):** **OPS96** Skill `/testing-audit` — auditoria noturna autônoma da suíte de testes com implementação das melhorias seguras. Novo comando `.opencode/commands/testing-audit.md` + `.agents/skills/testing-audit/SKILL.md` (autossuficiente: baseline → inventário paralelo read-only por camada → rubrica de disposições com prova forte para remoção → loop ≤6 melhorias com prova verde antes/depois e revert fail-closed → fecho PR ready SEM auto-merge armado — desarme `--disable-auto` como último passo, com fallback draft), pinado no guard `opencodeCommands.unit.spec.ts`. Tooling: `scripts/testing-audit-metrics.mjs` (+ core puro em `scripts/lib/`, spec próprio) gera o retrato por camada dos reporters nativos; cobertura first-party `@vitest/coverage-v8` instalada atrás de flag com whitelist knip e ignores do artefato `coverage/` (eslint/prettier). Primeira execução (relatório: `docs/testing-audits/2026-08-25.md`): **16 specs puros movidos int→unit** com conservação exata de totais (pirâmide OPS90 mais honesta — int ficou fronteira Payload/DB real), 1 assertion reforçada (`api.int.spec.ts`), referência morta corrigida (`measure-e2e-family.mjs`); flake da família Google Calendar documentado com evidência de recidiva (4 falhas distintas em 7 runs full, verde isolado) e registrado em #906; propostas não executadas (e2e HTTP-sem-browser, moves condicionais, gaps) vivem no relatório. Intocáveis preservados: zero toques em LGPD/RBAC/liderança, ci-scope/e2e-manifest/workflows/husky e specs e2e. diff --git a/docs/testing-audits/2026-08-25.md b/docs/testing-audits/2026-08-25.md new file mode 100644 index 00000000..e5b90e89 --- /dev/null +++ b/docs/testing-audits/2026-08-25.md @@ -0,0 +1,110 @@ +# Testing audit — 2026-08-25 + +Primeira execução da skill `/testing-audit` (OPS96, Issue #898 · impl plan [`ops96-skill-testing-audit-impl.md`](../plans/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](https://github.com/fsolla/teqo/issues/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 `. + +## 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](https://github.com/fsolla/teqo/issues/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 + +- Branch: `OPS96-skill-testing-audit-auditoria-autonoma-da-suite-de-tes` · PR ready base `main`. +- CI: ver seção do PR (aguardado green no SHA final; política de rerun 1× aplicável). +- Mergeable: verificado no fecho. +- **Auto-merge: DESARMADO deliberadamente às ** — `gh pr merge --disable-auto` após o run do safety-net do SHA final; verificado `autoMergeRequest: null`; fallback draft não foi necessário (registrar aqui a hora/saída real).