teqo-backup/docs/TESTING.md
Francisco Ignácio Fontoura Solla e8a440b837 Pass 2 W5: close-out de documentacao
- AGENTS.md: Known Gap #2 resolvido (D3), arvore de estrutura (+AllocationDecision, PrivacyPolicy, CampaignGoals; components por dominio), URLs novas (/campanha/assessores|conceitos|perfil), entrada 'Recently resolved' do Pass 2
- tracker: todos os workstreams done; secao de achados de drift de docs de produto (PRODUCT/DESIGN/CUSTOMER - reportados, nao aplicados)
- TESTING.md: 46/48/7 + linhas do safety net (formActions wrapper, voteTrend, submitWhatsapp)
- TECH-DEBT: smells fechados (municipalityUi split, detail RSCs, formActions, components subpastas, electionInsights)
- planos: status entregue nos 5 do Pass 2; escala-dry-pos-b16 absorvido; C8 F3/F4 fechados em escala-dry-pos-c6; notas de costura em B17/B18/cenario-junto; nota de conflito E4R em mapa-projecao
- roadmap: entrada Pass 2 no 'Ja entregue', B17/B18 marcados como triviais, B16+ riscado, header atualizado
- notebook do projeto: entrada Pass 2
- DESIGN.md: secao do sistema de listas (unico drift aplicado, conforme plano)
2026-07-25 16:08:38 -03:00

8.2 KiB

Testing

Test Strategy

Three layers, all local-database-only (see local-database skill; tests refuse any database not ending in _test):

  • Unit (tests/unit/**/*.unit.spec.ts, 46 specs as of 2026-07-25 (Pass 2)): pure logic; runs with a deliberately invalid DATABASE_URL so nothing can touch a database.
  • Integration (tests/int/**/*.int.spec.ts, 48 specs): real Payload + Postgres (teqo_test); campaign fixtures allocate seeded municipalities via a Postgres sequence so parallel spec files never share one, and purge residue on claim. Shared consent rows with stable keys are leased (tests/helpers/testDatabaseLease.ts), never owned.
  • E2E (tests/e2e, Playwright, 7 specs): smoke-level; most campaign behavior is pinned at the int layer.

CI (.github/workflows/ci.yml) runs lint (zero warnings), typecheck, knip, unit, migrate, int against a Postgres 17 service.

Safety Net Map

Modules the 2026-07 hardening phases touch, and where their behavior is pinned:

Module Pinned behaviors Test files Gaps
RBAC — src/utilities/access/* (split 2026-07-23; campaignAccess.ts is the re-export surface) per-role municipality/leadership/pledge/supporter/demand/plan scoping; leader lockdown; staff field asymmetry campaignMunicipalityAccess, campaignVotePledge, campaignSupporter, campaignUserPhone, campaignActionPlan, campaignDemandWorkflow, collectionAccessLockdown (int) no dedicated per-export suite; covered through collection behavior
Collection access lockdown campaign JWT denied on users/PII/CMS writes; anonymous read of public content; admin unaffected collectionAccessLockdown.int.spec.ts REST-transport variant untested (Local API with overrideAccess: false is equivalent)
municipalityMapData.ts bundle scoping by URL filters + advisor access; validVotesByYear incl. 2026 reuse of 2022; null on empty scope municipalityMapData.int.spec.ts zone breakdown shape
votePledgeData.ts estimated[scenario] ?? declared aggregation; role scoping; leader denial campaignVotePledge.int.spec.ts
Supporter import (token/bulk) preview→confirm flow, dedup by phone, consent fail-closed campaignSupporter.int.spec.ts supporterImportBulk/supporterImportToken internals only via flow
Leader supporter create linked-municipality scope; out-of-scope rejection; staff rejection collectionAccessLockdown.int.spec.ts
Posts visibility/caching isPostVisible fail-closed; cached listing (indirect via routes) no dedicated posts spec — backlog
List URL contracts (Pass 2 W0, pinned 2026-07-25) campaignListUrl canonicalize/clamp/redirect; supporter + actionPlan parse/serialize/href; 5 entity parsers campaignListUrl, supporterUi, actionPlanUi, campaignEntityListParsers (unit) municipality list URL pinned separately in municipalityUi.unit.spec.ts
Form-action ladders (Pass 2 W4d) runCampaignFormAction: success spread, boundary/zod/safe/generic mapping, values+revision echo campaignFormActions.unit.spec.ts per-route ladders are thin shells; behavior pinned at the wrapper
WhatsApp subscription consent (Pass 2 D3) fail-closed without whatsapp-inscricao; happy path links subscription → keyed consent submitWhatsapp.int.spec.ts
Vote trend + election number format (Pass 2 W4a survivors) computeVoteTrend pair priority/±10% band; formatElectionNumber E18 fraction defect voteTrend.unit.spec.ts
Entity list loaders (Pass 2 W0) scope (leader empty/denied), marker-narrowed q, kind/status filters, pagination shape campaignEntityListData.int.spec.ts advisor loader runs post-gate with overrideAccess: true (gate asserted upstream)

Characterization Backlog

  • src/utilities/posts.ts — dedicated spec for isPostVisible fail-closed matrix and cache wrappers.
  • actionPlanViewModels.ts + other actionPlan* utilities (~1k lines) — actionPlanUi.ts pinned 2026-07-25 (Pass 2 W0); view models still untested directly.
  • campaignDemandData.ts / leadershipData.ts / organizationData.ts / stateDeputyData.ts list loaders — smoked 2026-07-25 (campaignEntityListData.int.spec.ts, Pass 2 W0).
  • Election aggregate artifact (Phase 2): snapshot test pinning artifact ↔ municipalityCatalog key consistency — delivered as bahiaElectionAggregates.unit.spec.ts.