---
title: 35. Qualidade e a cadeia de testes
slug: 35-qualidade-e-a-cadeia-de-testes
docTags: 
createdAt: 2026-10-13T14:47:24.858Z
---

*As doze categorias de teste que sustentam a plataforma, o que cada uma responde e como rodá-las.*

A plataforma tem uma cadeia de testes em doze categorias, com um executor único. Ela vive em testes/ e roda contra um ambiente de pé:

:::BlockQuote
cd testes && ./executar.sh            # a cadeia inteira
cd testes && ./executar.sh seguranca  # uma categoria
:::

A ordem não é estética. Unitário primeiro porque é o mais barato e o que localiza melhor: se a regra está errada na função, não adianta descobrir isso numa jornada de navegador quinze minutos depois. Aceitação vem perto do fim porque é o mais caro e o que menos informa sobre onde está o defeito. EHT vem depois dela porque é leitura — não derruba nada —, e failover fica por último porque é o único que deixa o ambiente diferente de como o encontrou por alguns segundos.

***

### <font color="#15803D">O que cada elo responde</font>

**Unitário** — cada peça isolada faz o que promete. Backend em JUnit dentro de contêiner, para que a máquina não precise ter Java; frontend em Vitest.

**Funcional** — o sistema cumpre a regra de negócio, e não apenas responde. A diferença aparece na segregação: o endpoint responde com alegria, e a regra diz que quem respondeu não aprova. Um teste que só olha o status nunca descobre isso.

**Regressão** — o que já quebrou não voltou a quebrar. Cada caso corresponde a um defeito real e traz o sintoma que foi relatado, e não só a asserção. Um teste de regressão escrito por adivinhação cobre o que nunca falhou.

**Integração** — os módulos conversam nas costuras. É onde o sistema realmente falha: cada módulo passa no próprio teste e a resposta que vira achado, que vira risco, que aparece no painel, que abre o registro, é uma corrente de quatro elos que ninguém testa inteira por acidente.

**Performance** — velocidade e estabilidade sob concorrência. Mede percentil e não média: a média esconde a consulta que responde em 80ms quase sempre e em 4s uma vez a cada vinte, e é da quarta que o usuário lembra.

**Segurança** — o que a plataforma promete ser impossível é impossível. Não é varredura genérica: são as promessas deste produto, que é o que um atacante interno testaria primeiro — ver dado de outro tenant, alcançar endpoint sem permissão, forjar token, ler segredo pela API.

**Usabilidade** — o piso do que torna uma tela utilizável: teclado, rótulo associado, idioma declarado, ausência de rolagem horizontal no celular.

**PWA** — o aplicativo instala e sobrevive à perda de rede, e o cache recusa a API.

**Aceitação** — a jornada real fecha. "Carreguei o meu ROPA; onde vejo se estou conforme, o que falta e quem responde por isso?" Se essa jornada não fecha numa sessão, o resto não importa.

**EHT — Engenharia de Homologação Técnica** — o produto foi *construído* direito, e não só funciona. Um sistema inteiramente vulnerável passa em todos os testes funcionais. São quatro frentes:

•     **SAST**, sobre o próprio código: segredo escrito no fonte, SQL montado com texto que pode vir de fora, verificação de certificado desligada, execução de processo do sistema, HTML cru na interface, pendência de segurança.

•     **Dependências com CVE**: usa o relatório do OWASP Dependency-Check quando ele existe — daí saem CVSS, KEV e EPSS — e, quando não existe, confere offline as versões resolvidas no jar e o npm audit. E **diz qual dos dois caminhos usou**: um teste que passa sem base de dados e sem avisar é pior que não existir, porque produz confiança falsa.

Um achado tem três destinos possíveis, e a diferença entre eles é o que separa gestão de vulnerabilidade de teatro:

\| Destino | Quando | Onde fica | |---|---|---| | corrigir | há versão corrigida na linha em uso | sobe a versão | | suprimir | é falso positivo demonstrável | supressoes-cve.xml, uma entrada por CVE, com o motivo | | registrar | é real e não tem correção na linha | riscos-conhecidos.json, com a análise de alcance e **data de revisão** |

Suprimir é a decisão de não olhar mais, então só entra o que se demonstra: o componente vulnerável não está no classpath, ou a versão presente já é posterior à corrigida e o casamento foi por CPE errada. O que é verdadeiro e apenas improvável vai para o registro, não para a supressão.

A suíte reprova quando aparece CVE alta **fora** do registro — é risco novo que ninguém olhou —, quando a data de revisão vence, e quando uma entrada do registro já não aparece na varredura, porque registro que só cresce deixa de ser lido. O relógio é o ponto: sem ele, "migramos depois" vira a decisão permanente que ninguém tomou.

•     **DAST**, sobre a aplicação de pé: cabeçalhos, exposição de tecnologia, endpoint de gestão aberto, e resposta a entrada hostil sem vazar detalhe interno.

•     **Práticas**: RLS ligada *e forçada* em toda tabela com tenant, gatilho de trilha, papel de conexão sem superusuário nem BYPASSRLS, esquema versionado.

**Failover (HA)** — o que acontece quando uma peça cai. Derruba o banco de verdade e verifica as três promessas que só aparecem sob falha: a saúde deixa de dizer UP em vez de fingir; a consulta responde erro em vez de **lista vazia com 200** — zero num painel não parece defeito, parece boa notícia; e a aplicação volta sozinha, com a escrita confirmada antes da queda ainda lá. Não testa cluster: testa o comportamento do processo diante da perda da dependência crítica, que é a metade do problema que o código controla.

***

### <font color="#15803D">O que a cadeia não faz</font>

Usabilidade não se prova por automação — quem prova é gente usando. O que está automatizado é o piso, não o teto. A aceitação cobre as jornadas conhecidas; jornada nova continua exigindo alguém do negócio olhando.

E nenhuma destas suítes substitui teste de penetração. Elas verificam as promessas específicas deste produto, não a superfície inteira.

***

### <font color="#15803D">Achados que a própria cadeia produziu</font>

A suíte de segurança encontrou duas tabelas com tenant\_id e **sem** Row-Level Security: iam.chave\_de\_acesso, que guarda credenciais de contas de serviço, e integration.indice\_de\_execucao, que revela quais fontes cada cliente monitora e desde quando. Corrigidas na migração V63.

A suíte de regressão fixou o trabalhador de serviço como público: ele nasceu devolvendo 401, e o registro falha em silêncio — o aplicativo simplesmente nunca se instalaria, sem erro na tela.

A suíte de segurança, ao ganhar o bloco da chave de integração, mostrou que **nenhuma chave de serviço autenticava**: a troca da chave por token lê iam.chave\_de\_acesso antes de haver tenant na sessão — o tenant é justamente o que a chave vai dizer —, e a política de leitura não casava com nada. Toda chamada com X-API-Key respondia 401 sem explicação. O login por senha já tinha a solução desde sempre: uma política válida só enquanto a janela grc.autenticando está aberta. A chave de serviço nunca ganhou a equivalente. Corrigido na migração V204.

A suíte de EHT, na primeira execução, reprovou seis vezes — e **as seis eram defeitos dela**, não do produto: contava visão como tabela sem RLS; cobrava gatilho de trilha da própria tabela de trilha, o que seria recursão; comparava f/f com o false/false que o psql imprime; media a versão do driver do PostgreSQL pelo número de flyway-database-postgresql; acusava concatenação de SQL num filtro montado só com literais; e, o mais instrutivo, acusava TODO pendente em dois comentários perfeitamente escritos, porque a busca sem distinção de caixa casava com a palavra portuguesa *todo*. Um verificador que acusa o certo é treinamento para ignorar verificador.
