Integra suíte de testes de integração (agente eden-qa)

Vitest + supertest + NestJS TestingModule contra o Postgres real (não
mockado, conforme Master Prompt §15.1). Bootstrap via SWC (não esbuild
puro) porque o esbuild não implementa emitDecoratorMetadata — sem isso,
toda injeção de dependência via decorator do Nest quebra silenciosamente
sob Vitest (achado real do agente, não bug de produto).

5 arquivos de teste, 15 casos, todos verdes contra a stack real:
- auth.spec.ts: anti-enumeração, rotação de refresh, detecção de replay
  fim-a-fim, logout idempotente.
- role-weight.spec.ts: guarda a regressão do bug #1 (papel inexistente
  → 400) e o peso hierárquico (ator fraco não atribui papel mais forte).
- companies.spec.ts: CNPJ inválido rejeitado, CNPJ válido aceito.
- mass-assignment.spec.ts: whitelist rejeita campo inesperado.
- reseller-assignment.spec.ts: guarda a regressão do bug #2 (resellerId
  do corpo não é mais aceito; :id da URL é a única fonte de verdade).

Cada fixture criada pela suíte é prefixada (qa-test-/qa_) e limpa em
afterAll (desativada onde não há endpoint DELETE, removida onde há) —
confirmado que nenhum dado pré-existente (super_admin seedado, Handix
Telecom LTDA, etc.) foi alterado.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-09-03 11:17:24 -03:00
parent 159a2964cc
commit 860c21df07

View File

@@ -94,9 +94,15 @@ Estratégia: 4 subagentes (`general-purpose`, atuando com a persona `eden-commer
- **Resellers** (`apps/api/src/resellers/` + migration `resellers`): CRUD operacional + vínculo usuário↔revenda (`users.reseller_id`). Leitura aberta, escrita via `resellers:edit` — desvio deliberado do legado (que travava em `requireAdmin`), documentado em comentário. - **Resellers** (`apps/api/src/resellers/` + migration `resellers`): CRUD operacional + vínculo usuário↔revenda (`users.reseller_id`). Leitura aberta, escrita via `resellers:edit` — desvio deliberado do legado (que travava em `requireAdmin`), documentado em comentário.
- **Adapters S3/SMTP** (`packages/integrations/`): portados fielmente do legado — S3 falha alto (erro claro se não configurado), SMTP falha silencioso (nunca quebra um fluxo de negócio por causa de e-mail). - **Adapters S3/SMTP** (`packages/integrations/`): portados fielmente do legado — S3 falha alto (erro claro se não configurado), SMTP falha silencioso (nunca quebra um fluxo de negócio por causa de e-mail).
**2 bugs reais encontrados em teste manual e corrigidos antes do commit final:** - **CRM** (`apps/api/src/leads/`, `apps/api/src/opportunities/` + migration `crm`): leads e oportunidades com conversão transacional (`convertLeadToOpportunity`, `FOR UPDATE` + `withTransaction`). Diferente de Companies/Products, leitura também exige permissão (`leads:view`/`opportunities:view`) por conter PII de prospect.
**4 bugs reais encontrados em teste manual e corrigidos antes do commit final:**
1. `assertCanAssignRole`/`assertCanActOnUser` pulavam a validação de existência do papel quando o ator era `super_admin` (o bypass de peso pulava a checagem inteira) — convidar usuário com `role_key` inexistente estourava a constraint FK do banco como 500 não tratado em vez de 400 claro. 1. `assertCanAssignRole`/`assertCanActOnUser` pulavam a validação de existência do papel quando o ator era `super_admin` (o bypass de peso pulava a checagem inteira) — convidar usuário com `role_key` inexistente estourava a constraint FK do banco como 500 não tratado em vez de 400 claro.
2. `PATCH /v1/resellers/:id/assign-user` aceitava um `resellerId` diferente no corpo, que na prática era o valor usado — o `:id` da URL virava só uma checagem de existência, não o alvo real da atribuição. Corrigido para o `:id` da URL ser a única fonte de verdade; desatribuição virou endpoint próprio (`POST /v1/resellers/unassign-user`). 2. `PATCH /v1/resellers/:id/assign-user` aceitava um `resellerId` diferente no corpo, que na prática era o valor usado — o `:id` da URL virava só uma checagem de existência, não o alvo real da atribuição. Corrigido para o `:id` da URL ser a única fonte de verdade; desatribuição virou endpoint próprio (`POST /v1/resellers/unassign-user`).
3. Os controllers de Leads/Opportunities liam `req.user?.id` em vez de usar `@CurrentUser()`/`request.edenUser` (o padrão real do `JwtAuthGuard`) — todo evento de auditoria de lead/oportunidade estaria gravando `actorId: null` sempre. O agente que os escreveu não tinha o módulo de auth disponível no worktree dele para testar isso de verdade (só stubou e reverteu antes de commitar).
4. As chamadas `appendAuditEvent()` de leads/opportunities omitiam o campo obrigatório `actorType` (6 ocorrências).
Também precisou renomear o timestamp da migration de CRM, que o agente gerou com valor anterior ao da migration de `resellers` (já aplicada por outro agente/merge) — `node-pg-migrate` recusa migration fora de ordem.
Todos os módulos testados ao vivo contra a stack real (não só typecheck): convite de usuário, papel inexistente (400 após o fix), CRUD de produto com preços NUMERIC corretos, CRUD de revenda, atribuição/desatribuição de usuário à revenda. Todos os módulos testados ao vivo contra a stack real (não só typecheck): convite de usuário, papel inexistente (400 após o fix), CRUD de produto com preços NUMERIC corretos, CRUD de revenda, atribuição/desatribuição de usuário à revenda.