From 2c6ad258f0d745d4607698f21d5b813c577aa378 Mon Sep 17 00:00:00 2001 From: Fleny Date: Sat, 5 Oct 2024 19:55:05 +0200 Subject: [PATCH] chore(gateway): Refactor gateway integration test (#3850) --- packages/gateway/package.json | 2 +- .../tests/integration/connection.spec.ts | 254 ++++++++++-------- yarn.lock | 10 +- 3 files changed, 153 insertions(+), 113 deletions(-) diff --git a/packages/gateway/package.json b/packages/gateway/package.json index a7d19d2b4..685f570bd 100644 --- a/packages/gateway/package.json +++ b/packages/gateway/package.json @@ -52,6 +52,6 @@ "ts-node": "^10.9.2", "tsconfig": "*", "typescript": "^5.5.4", - "uWebSockets.js": "https://github.com/uNetworking/uWebSockets.js.git#commit=42c9c0d5d31f46ca4115dc75672b0037ec970f28" + "uWebSockets.js": "uNetworking/uWebSockets.js#54e5db370db746ed1c85021bdf215578a2164901" } } diff --git a/packages/gateway/tests/integration/connection.spec.ts b/packages/gateway/tests/integration/connection.spec.ts index e7f902119..359ce2752 100644 --- a/packages/gateway/tests/integration/connection.spec.ts +++ b/packages/gateway/tests/integration/connection.spec.ts @@ -1,4 +1,4 @@ -import { Intents } from '@discordeno/types' +import { type DiscordGatewayPayload, Intents } from '@discordeno/types' import uWS from 'uWebSockets.js' import { type GatewayManager, ShardSocketCloseCodes, createGatewayManager } from '../../src/index.js' @@ -29,143 +29,155 @@ function createGatewayManagerWithPort(port: number): GatewayManager { }) } -async function createUws(options: { - onOpen?: () => any - onMessage?: (message: any) => any - onClose?: (code: number, message: string) => any - closing?: boolean -}): Promise<{ port: number; uwsToken: uWS.us_listen_socket }> { +async function createUws(options: CreateUwsOptions) { options.onOpen ??= () => {} options.onMessage ??= (_message: any) => {} options.onClose ??= (_code: number, _message: string) => {} options.closing ??= false + let port: number - return await new Promise<{ port: number; uwsToken: uWS.us_listen_socket }>((resolve, reject) => { - let port = 0 + const { promise, resolve, reject } = promiseWithResolvers<{ port: number; uwsToken: uWS.us_listen_socket }>() - uWS - .App() - .ws('/*', { - compression: uWS.SHARED_COMPRESSOR, - maxPayloadLength: 16 * 1024 * 1024, - idleTimeout: 10, - open: async (ws) => { - if (options.closing) { - ws.end(3000) - return - } - ws.send( - JSON.stringify({ - op: 10, - d: { - heartbeat_interval: heartbeatInterval, + const app = uWS.App() + + app.ws('/*', { + compression: uWS.SHARED_COMPRESSOR, + maxPayloadLength: 16 * 1024 * 1024, + idleTimeout: 10, + open: async (ws) => { + if (options.closing) { + ws.end(ShardSocketCloseCodes.Shutdown) + return + } + + ws.send( + JSON.stringify({ + op: 10, + d: { + heartbeat_interval: heartbeatInterval, + }, + }), + ) + options.onOpen!() + }, + message: async (ws, message, _isBinary) => { + const msg = JSON.parse(Buffer.from(message).toString()) + options.onMessage!(msg) + + if (msg.op === 1) { + ws.send( + JSON.stringify({ + op: 11, + }), + ) + + return + } + if (msg.op === 2) { + ws.send( + JSON.stringify({ + t: 'READY', + s: 1, + op: 0, + d: { + v: 10, + user_settings: {}, + user: { + verified: true, + username: 'testing bot', + mfa_enabled: false, + id: '000000707882254000', + flags: 0, + email: null, + discriminator: '1687', + bot: true, + avatar: null, }, - }), - ) - options.onOpen!() - }, - message: async (ws, message, _isBinary) => { - const msg = JSON.parse(Buffer.from(message).toString()) - options.onMessage!(msg) - if (msg.op === 1) { - ws.send( - JSON.stringify({ - op: 11, - }), - ) - return - } - if (msg.op === 2) { - ws.send( - JSON.stringify({ - t: 'READY', - s: 1, - op: 0, - d: { - v: 10, - user_settings: {}, - user: { - verified: true, - username: 'testing bot', - mfa_enabled: false, - id: '000000707882254000', - flags: 0, - email: null, - discriminator: '1687', - bot: true, - avatar: null, - }, - shard: [0, 1], - session_type: 'normal', - session_id: '0dff79e1a6f2697388eb08924a0805c8', - resume_gateway_url: `ws://localhost:${port}`, - relationships: [], - private_channels: [], - presences: [], - guilds: [], - guild_join_requests: [], - geo_ordered_rtc_regions: [], - application: { id: '000000707882254000', flags: 27828224 }, - }, - }), - ) + shard: [0, 1], + session_type: 'normal', + session_id: '0dff79e1a6f2697388eb08924a0805c8', + resume_gateway_url: `ws://localhost:${port}`, + relationships: [], + private_channels: [], + presences: [], + guilds: [], + guild_join_requests: [], + geo_ordered_rtc_regions: [], + application: { id: '000000707882254000', flags: 27828224 }, + }, + }), + ) - return - } - if (msg.op === 6) { - // resume - } - }, - close: (_ws, code, message) => { - const msg = Buffer.from(message).toString() - options.onClose!(code, msg) - }, - }) - .listen(0, async (token) => { - if (!token) { - reject(new Error()) - } - - port = uWS.us_socket_local_port(token as uWS.us_listen_socket) - - resolve({ - port, - uwsToken: token, - }) - }) + return + } + if (msg.op === 6) { + // resume + } + }, + close: (_ws, code, message) => { + const msg = Buffer.from(message).toString() + options.onClose!(code, msg) + }, }) + + app.listen(0, (token) => { + if (!token) { + reject('Unable to get the socket token') + } + + port = uWS.us_socket_local_port(token) + + resolve({ + port, + uwsToken: token, + }) + }) + + return promise } describe('gateway', () => { it('can connect to server', async function () { this.timeout(15000) - let resolveConnected: () => void - const connected = new Promise((resolve) => (resolveConnected = resolve)) - const uwsOptions = { onOpen: resolveConnected!, closing: false } + + const { promise: connected, resolve: resolveConnected } = promiseWithResolvers() + + const uwsOptions: CreateUwsOptions = { + onOpen: resolveConnected, + closing: false, + } + const { port, uwsToken } = await createUws(uwsOptions) + const gateway = createGatewayManagerWithPort(port) await gateway.spawnShards() await connected + uwsOptions.closing = true + await gateway.shutdown(ShardSocketCloseCodes.Shutdown, 'User requested bot stop') + uWS.us_listen_socket_close(uwsToken) }) it('will heartbeat', async function () { this.timeout(15000) - let resolveHeartbeat: () => void - let resolveConnected: () => void - const connected = new Promise((resolve) => (resolveConnected = resolve)) - const heartbeated = new Promise((resolve) => (resolveHeartbeat = resolve)) - const uwsOptions = { - onOpen: resolveConnected!, - onMessage: (message: any) => { + + const { promise: connected, resolve: resolveConnected } = promiseWithResolvers() + const { promise: heartbeated, resolve: resolveHeartbeat } = promiseWithResolvers() + + const uwsOptions: CreateUwsOptions = { + onOpen: resolveConnected, + onMessage: (message) => { if (message.op !== 1) return + resolveHeartbeat() }, closing: false, } + const { port, uwsToken } = await createUws(uwsOptions) + const gateway = createGatewayManagerWithPort(port) await gateway.spawnShards() await connected @@ -175,9 +187,37 @@ describe('gateway', () => { }, heartbeatInterval) await heartbeated + clearTimeout(timeout) + uwsOptions.closing = true + await gateway.shutdown(ShardSocketCloseCodes.Shutdown, 'User requested bot stop') + uWS.us_listen_socket_close(uwsToken) }) }) + +// Polyfill for https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/withResolvers +function promiseWithResolvers() { + let resolve!: (value: T | PromiseLike) => void + let reject!: (reason?: any) => void + + const promise = new Promise((_resolve, _reject) => { + resolve = _resolve + reject = _reject + }) + + return { + promise, + resolve, + reject, + } +} + +interface CreateUwsOptions { + onOpen?: () => any + onMessage?: (message: DiscordGatewayPayload) => any + onClose?: (code: number, message: string) => any + closing?: boolean +} diff --git a/yarn.lock b/yarn.lock index 305820aae..d1c8372c6 100644 --- a/yarn.lock +++ b/yarn.lock @@ -159,7 +159,7 @@ __metadata: ts-node: "npm:^10.9.2" tsconfig: "npm:*" typescript: "npm:^5.5.4" - uWebSockets.js: "https://github.com/uNetworking/uWebSockets.js.git#commit=42c9c0d5d31f46ca4115dc75672b0037ec970f28" + uWebSockets.js: "uNetworking/uWebSockets.js#54e5db370db746ed1c85021bdf215578a2164901" ws: "npm:^8.18.0" dependenciesMeta: fzstd: @@ -4148,10 +4148,10 @@ __metadata: languageName: node linkType: hard -"uWebSockets.js@https://github.com/uNetworking/uWebSockets.js.git#commit=42c9c0d5d31f46ca4115dc75672b0037ec970f28": - version: 20.19.0 - resolution: "uWebSockets.js@https://github.com/uNetworking/uWebSockets.js.git#commit=42c9c0d5d31f46ca4115dc75672b0037ec970f28" - checksum: 099aabc375f4b667a3706c4120c124767fc57a43129217b8822bea8c5469eb54b4382ad742233d6a5f71538a8a023d02f5850e7e20e9a427ff9df112087097e7 +"uWebSockets.js@uNetworking/uWebSockets.js#54e5db370db746ed1c85021bdf215578a2164901": + version: 20.47.0 + resolution: "uWebSockets.js@https://github.com/uNetworking/uWebSockets.js.git#commit=54e5db370db746ed1c85021bdf215578a2164901" + checksum: c6ed6796b1ed86118e7ea32fa5e486a9aa8d2cc5e3757eb20e8a3120779b47130c318c5f84bbdfbb744c816af272ea7c0220e163ff1c7b85bcd848b04d521408 languageName: node linkType: hard