Pular para o conteúdo principal
O @zapo-js/fake-server é um servidor WhatsApp Web falso in-process que fala Noise XX/IK, X3DH + Double Ratchet, SenderKey de grupo, sincronização de app-state e upload/download de mídia sobre HTTPS auto-assinado — tudo o que um client conforme real vê no wire. Ele existe para você testar seu app end-to-end sem tocar no WhatsApp: sem número real ocupado, sem risco pra conta de produção, sem flakiness de rede real, CI-friendly.
Este guia tem como público autores de app — bots, CRMs, serviços de notificação, integrações — independente de a lib WhatsApp abaixo ser zapo-js, Baileys, whatsmeow ou outro client conforme. Para o uso interno da lib (suíte cross-check, benchmarks) veja Dev tools.

Funciona com qualquer lib WhatsApp

O fake server é uma implementação em nível de wire do protocolo WhatsApp. Não tem dependência de runtime em zapo-js — fala os mesmos bytes de Noise / Signal / app-state que os edges do próprio WhatsApp. Clients estritos (forks de Baileys, whatsmeow, entre outros) funcionam contra ele de saída:
  • A cadeia de cert Noise define notBefore / notAfter para clients que validam validade aceitarem como não-expirado.
  • O IQ de passive-set e a query <count> de prekey no encrypt são respondidos por default (clients não-zapo bloqueiam nos dois).
  • Após todo login o server envia <ib><offline count="0"/></ib> para clients de evento bufferizado darem flush.
  • Ids de mensagem do FakePeer são hex WA-style — sem @ que decoders binários estritos tokenizariam como JID.
Se seu app roda contra um edge WA real, deve rodar contra o falso trocando só URL / cert.

Instalação

zapo-js é peer dependency do fake server (importa as primitivas de Noise / Signal / crypto / proto por baixo em runtime), então precisa estar presente na árvore mesmo quando seu app é construído em cima de outra lib WhatsApp. Node.js >= 20.9.0.

Início rápido (programático)

A ligação zapo-js abaixo é como um app zapo-js se parece; para Baileys / whatsmeow, mantenha seu setup de client existente e sobrescreva apenas a URL do socket, o proxy de mídia e o Noise root CA para apontar pro fake server.
testHooks.noiseRootCa confia no certificado do fake server sem burlar a verificação — a checagem completa da cadeia continua rodando.
Desde o @zapo-js/fake-server 1.3.1 o servidor envia stream:error 515 depois que um companion responde o pair-success, exatamente como o real faz, então um client sob teste reinicia pelo sinal do servidor em vez de por timeout. Testes de pareamento escritos contra um fake server mais antigo podem ter passado pelo motivo errado — veja Depois que o telefone aceita.

Pinning entre processos

listen() gera um root CA aleatório novo a cada start e expõe a metade pública em server.noiseRootCa — perfeito pro teste in-process acima, onde o client é construído depois que o server já está escutando. Um client num processo separado precisa pinnar o trust anchor antes de discar, e nesse ponto o server ainda não está escutando: não tem nada pra ler. Passe um noiseRootCa derivado de uma seed compartilhada, então os dois lados calculam o mesmo CA de antemão:
FakeNoiseRootCa carrega a metade de assinatura — o server precisa dela pra assinar a cert chain — enquanto o client sempre só segura a contraparte pública. Ela assina uma chain que nenhum client WhatsApp real confia, então é material de teste e não segredo de verdade, mas uma seed commitada junto dos testes é a fonte pretendida, não uma compartilhada com algo que importe. Omitir a opção mantém o comportamento anterior: um CA aleatório novo por listen().

Simulando um peer

createFakePeer te dá um contato simulado com cripto Signal real. Empurre mensagens pro seu client com sendConversation / sendGroupConversation, e capture o que o seu client envia com expectMessage:
O peer executa um handshake X3DH real, faz ratchet a cada mensagem e valida MACs — então um bug no seu handling Signal aparece do mesmo jeito que apareceria contra o próprio WhatsApp.

Validando stanzas outbound

Para asserts que não precisam do ponto de vista de um peer (um <presence> que seu app enviou, um <receipt> que ele ackeou, um IQ que ele fez) inscreva-se em stanzas capturadas:
onCapturedStanza retorna uma função de unsubscribe; chame em afterEach para manter os testes isolados.

Isolamento multi-sessão

Rode muitas instâncias de app contra um único server sem cross-talk. Forneça um resolver sessionKey — o fake server roteia cada conexão autenticada pro próprio FakeServerSession (peers, grupos, prekeys, app-state, stanzas capturadas isolados):
O resolver roda uma vez por conexão, logo após a autenticação, então info.clientPayload está disponível para chavear por identidade de login. Handlers server-wide (server.registerIqHandler) continuam aplicando a toda sessão; os escopados por sessão (session.registerIqHandler) ficam locais.

Mobile-primary + companion hosting

Para testar uma sessão mobile-primary — o papel do client.mobile, onde o zapo hospeda companions — inicie o server com o listener TCP raw que o transporte mobile disca, e semeie um primary registrado no store. O registro mobile acontece out-of-band contra os endpoints HTTP do WhatsApp, então o fake server não consegue rodá-lo; o seedFakeMobilePrimary grava as credentials diretamente.
Para dirigir o link de companion contra esse primary, conecte um segundo client como companion e entregue as refs via server.offerCompanionPairing(companionPipeline) — o inverso do runPairing. O primary assina a identidade de verdade; o server só relay:
Isso exercita o handshake completo client.mobile.linkCompanion / linkCompanionByCode — assinatura, ADV epoch, republish da key-index list, evento companion_host_linked — end to end.
Pareamento por código roda entre os dois clients com o server no meio; o fake server relay cada estágio. parseClientPayload no pipeline classifica o login como web ou mobile e expõe a identidade do phone (manufacturer, model, os, app version, phone id) pra asserts.

Config programática

FakeWaServer.start(options) aceita: onPipeline(listener) fanout pra múltiplos subscribers e retorna uma função de unsubscribe; o clientPayload parseado da pipeline é exposto em WaFakeConnectionPipeline pra checagens de identidade. IQ handlers podem retornar null pra fall through pro próximo handler que casa (observe-then-delegate).

CLI standalone

O pacote traz um binário fake-wa-server. Rode uma vez que a dev dep estiver instalada:
O modo --pair <jid> conduz o pareamento QR pedindo no stdin o payload QR que o client mostra — pareie uma vez, depois reconecte contra o mesmo fake server para iterar.

Recipe de CI

Suba um server por arquivo de teste (ou por suíte), derrube no final. Providers memory no client mantêm cada teste hermético:
Combine o fake server com stores in-memory no client. Cada teste reseta — sem estado de pareamento vazando, sem malabarismo de fixture em disco.

Veja também

Dev tools

Benchmarks, suíte cross-check e o MCP dev-server — tooling interno da lib.

README do fake server

Referência completa de CLI e superfície de API.
Última modificação em 28 de outubro de 2026