discord_luau/discord_stub
A local Discord stub, hosted on 127.0.0.1, for testing discord-luau bots.
discord_stub
A local Discord for tests and examples: an HTTP server and a websocket gateway on 127.0.0.1, with an optional
virtual clock.
Both servers listen on an ephemeral port on 127.0.0.1; stub:createHttpServer and stub:createWebsocketServer
return handles with url, port and stop(). An HTTP handler receives the request and may return a response or a
raw string body. A websocket handler receives the connection and the decoded payload and may return a payload to send
instead of the default, or false to send nothing. Returning nil falls through to the defaults (the
/api/v10/gateway/bot route, Hello, heartbeat ACK, READY on Identify, replay and RESUMED on Resume).
stub.restUrl is http://127.0.0.1:{port}/api/v10 once the HTTP server exists, and nil before it. ws:send(payload)
sends a payload to every open connection; ws:close(code?) closes them all, 1000 by default.
The websocket server records what happened for a test to assert on. ws.connections keeps every connection ever
opened, each with its index, upgrade path (/ for a fresh dial, /resume for a resume), sessionId, open,
closeCode and received (every payload with the clock time it arrived); ws:latestConnection(),
ws:openConnections() and ws:receivedOps(opcode) read them, and a connection can send(payload) and close(code?)
on its own. An error raised by the handler lands in ws.handlerErrors instead of vanishing into Zune.
Identify starts session stub-session-{n} and READY carries resume_gateway_url = "{ws.url}/resume".
ws:dispatch(event, data) sends a dispatch to the connections bound to that session; ws:missDispatch(event, data)
only logs one. Resume replays every logged dispatch after the client's seq, then RESUMED; any other session gets
{ op = 9, d = false }. ws.refuseUpgrades = n refuses the next n upgrades (the client fails with UpgradeFailed)
and ws.upgradeDelaySeconds holds each upgrade that long in clock time, with ws.handshaking true meanwhile.
discordStub.new({ clock = "virtual", polyfills = stdPolyfills }) runs the stub 100x faster than real time: task
and datetime on that table are accelerated for the life of the stub, and stub:stop() restores them. The table must
be the std_polyfills the code under test requires; without it only the stub's own copy is swapped and the code under
test keeps real time. stub.clock:waitFor(predicate, seconds) polls in clock time.
stub:runScenario(scenario, teardown?) runs a test body, then the teardown, then stub:stop() even when either
throws, and returns the body's error, else the teardown's error, else the first handler error, else nil.
discordStub.recordEmits(emitter) returns a list that gains the packed arguments of every call the emitter fires.
discord_stub is an independent implementation built from Discord's public developer documentation. It is not
affiliated with or endorsed by Discord, and it ships no Discord marks or logos. Use it only as a local test double.