0022 — A faxina do e2e e quatro specs falam com um container de banco que não existe mais¶
| Status | CORRIGIDO |
| Branch | fix/0022-faxina-e2e-container-inexistente |
| Aberto em | 2026-09-27 |
| Severidade | MÉDIA |
| Apps afetados | e2e |
| Revisão do diagnóstico | aprovado na rodada 1 |
| Revisão da correção | aprovado na rodada 1 |
Status:
RASCUNHO→DIAGNOSTICADO→EM CORREÇÃO→CORRIGIDO
O fix atravessa dois portões, cada um revisado por um subagente de contexto limpo, em até 3 rodadas. Máquina do plugin superpowers, régua em .claude/rubrics/. Qualquer Critical reprova; Important em aberto reprova salvo decisão registrada do usuário.
- O diagnóstico (seções 1 a 4) é revisado na seção 5 (reprodução × causa raiz).
- A correção (o diff e os testes) é revisada na seção 11 (correção × regressão).
1. Sintoma¶
A suíte e2e deixou de devolver o banco do dev-remote ao estado em que o encontrou. O globalTeardown que apaga o que a execução criou não apaga nada, e não diz isso no fim da execução: o único rastro é um aviso no começo, no globalSetup:
[limpeza] não foi possível marcar o início: Command failed: docker exec garantora-devremote-db-1 psql …
Error response from daemon: No such container: garantora-devremote-db-1
O efeito aparece no banco compartilhado. Imobiliárias criadas por dia no garantora_db do dev-remote (consulta de só leitura, feita em 2026-09-27):
| Período | Imobiliárias criadas |
|---|---|
| 2026-08-25 a 2026-09-05 (antes da troca do banco) | 6 em quatro dias com registro |
| 2026-09-10 a 2026-09-24 (depois da troca do banco) | 113 em oito dias com registro, até 34 num dia só |
É o mesmo acúmulo que a faxina foi criada para acabar (fixtures/limpeza.ts:4-13: 3870 imobiliárias, e a do seed caindo para fora da primeira página de uma busca).
2. Reprodução¶
- Na VPS do dev-remote — a máquina onde a suíte roda, o que torna válidos o
docker exece o127.0.0.1:8025—, com o.envdo e2e apontandoDB_CONTAINERpara o valor do.env.example(apps/e2e/.env.example:13): - O comando termina com
exit 1eError response from daemon: No such container: garantora-devremote-db-1. É exatamente o comando quemarcarInicio()executa (fixtures/limpeza.ts:71-76, chamado em:107). docker psnão lista container de banco do Garantora: os dele são sógarantora-devremote-{api,worker,web,docs}-1(a VPS também hospeda containers de outros projetos).- Depois de qualquer execução da suíte,
apps/e2e/.limpeza-inicionão existe, e as linhas criadas durante a execução continuam no banco.
Reproduz toda vez: é determinístico, e não depende de spec, papel nem viewport.
- Ambiente: dev-remote (o único alvo do e2e).
- Superfície: não se aplica — é infraestrutura de teste, sem tela.
- Dados/pré-condições: o
.envdo e2e comDB_CONTAINER=garantora-devremote-db-1, como manda o.env.example.
3. Esperado × Atual¶
- Esperado: o
globalSetupgrava o instante de início, e oglobalTeardownapaga, numa transação, o que nasceu depois dele, imprimindo a contagem do que removeu. Os specs que manipulam o banco direto (§4, alcance) alcançam ogarantora_dbdo dev-remote. - Atual: nenhum comando alcança o banco. A faxina não roda e só avisa no começo; os specs que dependem do banco lançam exceção ou engolem o erro.
4. Causa raiz¶
Onde. A variável e o comando:
apps/e2e/.env.example:13:DB_CONTAINER=garantora-devremote-db-1, o nome do container PostgreSQL que o compose do dev-remote tinha até 2026-09-07.apps/e2e/fixtures/limpeza.ts:73e:82:docker exec ${DB_CONTAINER} psql …. O acesso ao banco pressupõe que ele é um container.
Regressão, e de qual mudança: o commit c27fe88 (2026-09-07, "infra(dev-remote): db/redis/mailpit para serviços nativos da VPS, Caddy sai") migrou o PostgreSQL do dev-remote para um serviço nativo do host (systemd). O garantora_db passou a viver no PostgreSQL nativo, e o container garantora-devremote-db-1 deixou de existir (infra/dev-remote/docker-compose.yml:17-22). O volume antigo ficou como backup (garantora-devremote_postgres_data, segundo a mensagem do commit). O commit atualizou o compose, o README e a plataforma de documentação, mas não o e2e.
O mecanismo — por que isso produz este sintoma, e em silêncio no fim.
marcarInicio()(limpeza.ts:100) chamapsql('SELECT now()')(:107). OexecSyncdodocker execsai com código 1, e oexecSynclança.- O
catchde:109-113transforma a exceção emconsole.warne segue, de propósito: a suíte mede a aplicação, não a faxina. Por isso o arquivo de marca.limpeza-inicionunca é gravado (:108não chega a rodar). - No fim,
limparOQueASuiteCriou()(:117) encontra a marca ausente e retorna em:119(if (!fs.existsSync(MARCA)) return;) sem imprimir nada. O teardown termina limpo, sem apagar e sem avisar. - O único sinal é o aviso do passo 2, emitido no começo da execução e soterrado pela saída dos testes. O acúmulo da §1 começa logo depois de 2026-09-07.
O alcance — onde mais a mesma premissa morde. Procurei DB_CONTAINER e docker exec em todo o apps/e2e. Além da faxina, quatro specs falam com o banco pelo mesmo caminho, todos com default garantora-db-1 e lendo o mesmo DB_CONTAINER do .env:
| Arquivo | Linha | O que faz com o banco | Como reage à falha (lido no código, não executado) |
|---|---|---|---|
tests/e2e/password-reset.spec.ts | :18, :20-31 | apaga os códigos de recuperação antes do teste | catch vazio em :28 engole o erro; o teste segue e pode esbarrar no rate limit (o próprio comentário diz isso) |
tests/e2e/remocao-logica.spec.ts | :121, :123-128 | empurra deleted_at para o passado (RN5) | execSync sem catch: lança, e o teste falha nesse passo |
tests/e2e/documento-corrompido.spec.ts | :75, :85-92 | trunca o conteúdo de um documento para reproduzir o estado do bug | execSync sem catch: lança, e o teste falha nesse passo |
tests/e2e/onboarding-leva-ao-painel.spec.ts | :33, :63-71 | lê o código de verificação de e-mail | execSync sem catch: lança, e o teste falha nesse passo |
Não rodei esses quatro specs para medir a falha: o diagnóstico se apoia no comando reproduzido na §2, que é o mesmo nos cinco arquivos, e na leitura do código.
Fora do alcance, conferido. O MAIL_API (http://127.0.0.1:8025/mail/api/v1) continua certo: o Mailpit também virou nativo, mas na mesma porta do host, e quem fala com ele é HTTP, não docker exec. A API e o worker alcançam o banco nativo por host.docker.internal e não são afetados. Documentação que ainda cita o container antigo: apps/e2e/CLAUDE.md (Environment Setup), os comentários dos cinco arquivos e as specs 0046 e 0051, que são registro histórico e não se reescrevem.
O que já está descartado. Não é permissão do pg_hba, credencial nem o PostgreSQL fora do ar: o erro vem do daemon do Docker, antes de qualquer conexão com o banco. E a API do dev-remote responde normalmente contra o banco nativo (verificado no commit c27fe88).
5. Revisão do diagnóstico¶
| Rodada | Critical | Important | Minor | Veredicto |
|---|---|---|---|---|
| 1 | 0 | 0 | 3 | aprovado |
Resultado: aprovado na rodada 1. Os três Minor (citação uma linha fora em onboarding-leva-ao-painel.spec.ts, o docker ps descrito de forma não literal, a fonte do volume de backup) foram corrigidos no texto.
Pendências aceitas: nenhuma. Recomendação do revisor, levada para o implement: confirmar ao menos um dos quatro specs em execução, como prova do CA2.
6. Correção¶
O defeito era a premissa "o banco é um container", repetida em cinco arquivos. A correção tira a premissa: um acesso só, que conecta por URL e não supõe como o banco roda.
apps/api¶
Sem impacto.
apps/web¶
Sem impacto.
apps/e2e¶
fixtures/banco.ts(novo):consultar(sql)eexecutarEmTransacao(sql)chamam opsqldo host comexecFileSync(sem shell no meio). A URL deE2E_DATABASE_URLé decomposta nas variáveisPG*do processo filho: a senha não vai na linha de comando, onde apareceria empsnuma VPS compartilhada. Sem a variável, lançaBancoNaoConfiguradocom a mensagemE2E_DATABASE_URL não definido.fixtures/limpeza.ts: usa obanco.tsno lugar dodocker exec. Mantém a salvaguarda (sem alvo configurado, não limpa e diz por quê). O teardown sem marca de início agora avisa em vez de retornar calado (CA4).marcarInicioelimparOQueASuiteCriouaceitam o caminho da marca, com o de sempre como padrão, para o teste não disputar a marca real.E2E_SEM_LIMPEZApassou a ser lida na chamada, não na importação.- Os quatro specs da §4:
password-reset,remocao-logica,documento-corrompidoeonboarding-leva-ao-paineltrocam odocker execporconsultar. Cada um mantém a reação que tinha: opassword-resetsegue engolindo a falha; os outros três lançam. .env.exampleeCLAUDE.md:E2E_DATABASE_URLno lugar deDB_CONTAINER, com os dois formatos (dev-remote e local) e de onde vem a senha do dev-remote.- Teste de regressão:
tests/e2e/limpeza.spec.ts, contra o banco de verdade, um caso por comportamento: a marcação consulta o banco e grava a marca; o teardown sem marca avisa; sem URL, não marca e diz por quê.
7. Risco / impacto colateral¶
A faxina executa DELETE em doze tabelas. Qualquer correção que mude como ela chega ao banco precisa manter a salvaguarda de limpeza.ts:31-41: sem alvo explícito, não limpa. Apontar para o banco errado apaga trabalho do time. Não toca dinheiro nem regra de negócio.
8. Critérios de aceite¶
- [x] CA1. Seguindo a §2, o acesso configurado no
.envalcança ogarantora_dbdo dev-remote, e uma execução da suíte grava.limpeza-inicioe, no teardown, imprime[limpeza] removido: …, com as linhas criadas pela execução apagadas. - [x] CA2. Os quatro specs da §4 alcançam o banco pelo mesmo caminho da faxina.
- [x] CA3. Sem alvo configurado, a faxina continua não limpando e dizendo por quê.
- [x] CA4. Quando a marca de início não existe, o teardown diz isso em vez de retornar calado — o silêncio de
limpeza.ts:119é o que escondeu este defeito por três semanas. - [x] CA5.
.env.exampleeapps/e2e/CLAUDE.mddescrevem o acesso novo.
9. Plano¶
- [x] Escrever o teste que reproduz o bug e vê-lo falhar — antes de qualquer correção
- [x] Corrigir a causa raiz no
apps/e2e - [x] Red-green verificado: teste passa → reverter a correção → rodar → falha → restaurar → passa
- [x]
make preflightlimpo. Omake lintnão cobre oapps/e2e(éruffna API etscno web), e o e2e não temtscpróprio; a checagem de sintaxe foi onpx playwright test --listdos cinco arquivos, que transpila cada um
10. Decisões em aberto¶
- [x] Como o e2e alcança o PostgreSQL nativo. Decidido pelo usuário em 2026-09-27: uma URL no
.envdo e2e,E2E_DATABASE_URL, no lugar doDB_CONTAINER, e opsqldo host conecta por ela. O mesmo formato serve ao dev-remote (o PostgreSQL nativo aceita senha em127.0.0.1,pg_hba.conf) e à stack local (que publica a5432). Sem a variável, a faxina continua não limpando e diz por quê. A senha do dev-remote o usuário copia para oapps/e2e/.env, que o git ignora; a correção não lê oinfra/dev-remote/.env.
11. Revisão da correção¶
| Rodada | Critical | Important | Minor | Veredicto |
|---|---|---|---|---|
| 1 | 0 | 0 | 3 | aprovado |
O teste sobreviveria à reversão? Não. Revertidos fixtures/limpeza.ts (para o do HEAD) e fixtures/banco.ts (removido), com DB_CONTAINER=garantora-devremote-db-1 como o .env antigo mandava, npx playwright test tests/e2e/limpeza.spec.ts --project=chromium --no-deps deu 3 failed: o primeiro caso em a marcação não deveria ter avisado nada, recebendo No such container: garantora-devremote-db-1 — o defeito exato —; o segundo sem o aviso de marca ausente; o terceiro com a mensagem do docker exec no lugar de E2E_DATABASE_URL não definido. Restaurada a correção, 3 passed.
Uma primeira reversão rodada com E2E_SEM_LIMPEZA=1 também deu 3 failed, mas o primeiro caso falhou por outro motivo: o código revertido lê essa variável na importação, e a faxina já estava desligada quando o teste tentou religá-la. Foi descartada como prova e refeita sem a variável; é a que está acima.
Resultado: aprovado na rodada 1. Os três Minor foram corrigidos: a documentação diz que caractere especial da senha vai codificado na URL (.env.example, CLAUDE.md); o teste remove o diretório temporário de cada caso; e o E2E_SEM_LIMPEZA do processo do teste é restaurado depois de cada caso. Relançado, limpeza.spec.ts deu 3 passed, sem resíduo em /tmp.
Defeitos aceitos: nenhum.
12. Registro¶
- Decisão de acesso (§10): URL no
.envdo e2e. O usuário configurou a do dev-remote com a senha doPOSTGRES_PASSWORDdoinfra/dev-remote/.env, trocandohost.docker.internalpor127.0.0.1epostgresql+asyncpg://porpostgresql://. - Prova em execução (CA1 e CA2): os cinco arquivos tocados rodados juntos, contra o dev-remote, com a faxina ligada — 22 passed em 1,1 min. O teardown imprimiu
[limpeza] removido: credit_analysis_status_history=17 credit_analysis_participants=5 decisions=4 billings=48 guarantees=4 documents=8 credit_analyses=5 properties=5 renters=11 audit_logs=78eimobiliárias: 119 → 119. Não precisou de/devremote-rebuild: o fix não tocaapinemweb, e o e2e roda do host. - Divergiu do plano: os quatro specs perderam o default
garantora-db-1da stack local. Agora exigemE2E_DATABASE_URLem qualquer ambiente, e a URL local está no.env.example. O host passa a precisar do clientepsql, dito noCLAUDE.mddo e2e. - Fora do escopo, e fica com o usuário: o que acumulou entre 2026-09-07 e hoje continua no banco (as 113 imobiliárias da §1, entre outros registros). A faxina só apaga o que nasce durante a execução; limpar o acúmulo é decisão à parte.