

























@@ -1,4 +1,6 @@
1+import { EventEmitter } from "node:events";
12import { beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
3+import { DISCORD_GATEWAY_TRANSPORT_ACTIVITY_EVENT } from "./gateway-handle.js";
2435const { baseConnectSpy, GatewayIntents, GatewayPlugin } = vi.hoisted(() => {
46const baseConnectSpy = vi.fn<(resume: boolean) => void>();
@@ -14,12 +16,30 @@ const { baseConnectSpy, GatewayIntents, GatewayPlugin } = vi.hoisted(() => {
1416GuildMembers: 1 << 7,
1517} as const;
161819+class TestEmitter {
20+private readonly listenersByEvent = new Map<string, Array<(value: unknown) => void>>();
21+22+on(event: string, listener: (value: unknown) => void) {
23+const listeners = this.listenersByEvent.get(event) ?? [];
24+listeners.push(listener);
25+this.listenersByEvent.set(event, listeners);
26+}
27+28+emit(event: string, value: unknown) {
29+for (const listener of this.listenersByEvent.get(event) ?? []) {
30+listener(value);
31+}
32+}
33+}
34+1735class GatewayPlugin {
1836options: unknown;
1937gatewayInfo: unknown;
38+emitter = new TestEmitter();
2039heartbeatInterval: ReturnType<typeof setInterval> | undefined = undefined;
2140firstHeartbeatTimeout: ReturnType<typeof setTimeout> | undefined = undefined;
2241isConnecting: boolean = false;
42+ws?: unknown;
23432444constructor(options?: unknown) {
2545this.options = options;
@@ -64,14 +84,17 @@ describe("SafeGatewayPlugin.connect()", () => {
6484baseConnectSpy.mockClear();
6585});
668667-function createPlugin() {
87+function createPlugin(
88+testing?: NonNullable<Parameters<typeof createDiscordGatewayPlugin>[0]["__testing"]>,
89+) {
6890return createDiscordGatewayPlugin({
6991discordConfig: {},
7092runtime: {
7193log: vi.fn(),
7294error: vi.fn(),
7395exit: vi.fn(),
7496},
97+ ...(testing ? { __testing: testing } : {}),
7598});
7699}
77100@@ -112,4 +135,58 @@ describe("SafeGatewayPlugin.connect()", () => {
112135clearTimeout(staleTimeout);
113136}
114137});
138+139+it("emits transport activity for current gateway socket messages", () => {
140+const socket = new EventEmitter() as EventEmitter & { binaryType?: string };
141+const plugin = createPlugin({
142+webSocketCtor: function WebSocketCtor() {
143+return socket;
144+} as unknown as NonNullable<
145+Parameters<typeof createDiscordGatewayPlugin>[0]["__testing"]
146+>["webSocketCtor"],
147+});
148+const activitySpy = vi.fn();
149+(
150+plugin as unknown as {
151+emitter: { on: (event: string, listener: (value: unknown) => void) => void };
152+}
153+).emitter.on(DISCORD_GATEWAY_TRANSPORT_ACTIVITY_EVENT, activitySpy);
154+155+const createdSocket = (
156+plugin as unknown as { createWebSocket: (url: string) => typeof socket }
157+).createWebSocket("wss://gateway.discord.gg");
158+(plugin as unknown as { ws: unknown }).ws = createdSocket;
159+160+createdSocket.emit("message", Buffer.from("{}"));
161+162+expect(activitySpy).toHaveBeenCalledWith({ at: expect.any(Number) });
163+});
164+165+it("ignores messages from stale gateway sockets", () => {
166+const staleSocket = new EventEmitter() as EventEmitter & { binaryType?: string };
167+const currentSocket = new EventEmitter();
168+const plugin = createPlugin({
169+webSocketCtor: function WebSocketCtor() {
170+return staleSocket;
171+} as unknown as NonNullable<
172+Parameters<typeof createDiscordGatewayPlugin>[0]["__testing"]
173+>["webSocketCtor"],
174+});
175+const activitySpy = vi.fn();
176+(
177+plugin as unknown as {
178+emitter: { on: (event: string, listener: (value: unknown) => void) => void };
179+}
180+).emitter.on(DISCORD_GATEWAY_TRANSPORT_ACTIVITY_EVENT, activitySpy);
181+182+const createdSocket = (
183+plugin as unknown as { createWebSocket: (url: string) => typeof staleSocket }
184+).createWebSocket("wss://gateway.discord.gg");
185+expect(createdSocket).toBe(staleSocket);
186+(plugin as unknown as { ws: unknown }).ws = currentSocket;
187+188+staleSocket.emit("message", Buffer.from("{}"));
189+190+expect(activitySpy).not.toHaveBeenCalled();
191+});
115192});
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。