Migrei para pool de conexão e levei um bug bizarro do Jest logo de cara
Estava refatorando minha aplicação Node.js para usar pg.Pool no lugar de conexões avulsas — o ganho de performance em queries concorrentes é real e vale a mudança. Mas logo nos primeiros testes, algo quebrou de um jeito que não fazia sentido nenhum.
O cenário
Tenho um teste que valida a expiração de um token de ativação. A lógica é simples: criar um token com expires_at no passado e verificar que a autenticação falha. Para isso, uso jest.useFakeTimers() para controlar o Date.now().
O teste funcionava perfeitamente antes do pool. Depois da migração, começou a falhar de formas aleatórias — às vezes um timeout, às vezes uma query retornando resultado errado, às vezes o teste travando sem terminar.
Por que jest.useFakeTimers() quebra o pool?
O problema é específico — e não óbvio.
jest.useFakeTimers() substitui todas as funções de timer por padrão: setTimeout, clearTimeout, setInterval etc. O pg.Pool usa esses timers internamente para gerenciar idleTimeoutMillis e connectionTimeoutMillis.
Quando você chama client.release() com fake timers ativos, o pool registra o idle timer usando o setTimeout falso. Quando jest.useRealTimers() é chamado depois, esse timer some — a conexão fica no pool sem idle timer. Na próxima query, coisas estranhas acontecem.
A solução
A solução correta é a que o próprio Jest oferece: doNotFake. Faça fake só do Date e deixe os timers reais:
jest.useFakeTimers({
doNotFake: [
'setTimeout',
'clearTimeout',
'setInterval',
'clearInterval',
'setImmediate',
'clearImmediate',
],
now: new Date(Date.now() - activation.EXPIRATION_IN_MILLISECONDS - 1000),
});
Assim, Date.now() retorna uma data no passado (o token é criado com expires_at já expirado), mas o pg.Pool continua usando timers reais sem interferência.
Resumo
| Abordagem | Problema |
|---|---|
jest.useFakeTimers() puro | Substitui os timers do pg.Pool → comportamento imprevisível |
jest.useFakeTimers({ doNotFake: [...] }) | Só faz fake do Date → pool funciona normalmente |
Se você usa pg.Pool e tem testes que dependem de controle de tempo, vai cair nessa armadilha. O doNotFake resolve limpo.