@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 emzapo-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/notAfterpara 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-zapobloqueiam 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
FakePeersão hex WA-style — sem@que decoders binários estritos tokenizariam como JID.
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çãozapo-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.
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:
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 resolversessionKey — o fake server roteia cada conexão autenticada pro próprio FakeServerSession (peers, grupos, prekeys, app-state, stanzas capturadas isolados):
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 doclient.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.
server.offerCompanionPairing(companionPipeline) — o inverso do runPairing. O primary assina a identidade de verdade; o server só relay:
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áriofake-wa-server. Rode uma vez que a dev dep estiver instalada:
--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. Providersmemory 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.
