























@@ -1,5 +1,14 @@
11import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
223+function resolveGatewayInfoFetch(resolve: ((value: Response) => void) | undefined): void {
4+expect(resolve).toBeDefined();
5+resolve!({
6+ok: true,
7+status: 200,
8+text: async () => JSON.stringify({ url: "wss://gateway.discord.gg" }),
9+} as Response);
10+}
11+312const {
413 GatewayIntents,
514 baseRegisterClientSpy,
@@ -42,9 +51,15 @@ const {
4251class GatewayPlugin {
4352options: unknown;
4453gatewayInfo: unknown;
54+client: unknown;
55+ws: unknown;
56+isConnecting: boolean;
4557constructor(options?: unknown, gatewayInfo?: unknown) {
4658this.options = options;
4759this.gatewayInfo = gatewayInfo;
60+this.client = undefined;
61+this.ws = undefined;
62+this.isConnecting = false;
4863}
4964async registerClient(client: unknown) {
5065baseRegisterClientSpy(client);
@@ -445,6 +460,89 @@ describe("createDiscordGatewayPlugin", () => {
445460);
446461});
447462463+it("sets client reference before the async gateway-info fetch resolves (regression for #52372)", async () => {
464+vi.useFakeTimers();
465+const runtime = createRuntime();
466+let fetchResolve: ((v: Response) => void) | undefined;
467+globalFetchMock.mockImplementation(
468+() =>
469+new Promise<Response>((resolve) => {
470+fetchResolve = resolve;
471+}),
472+);
473+const plugin = createDiscordGatewayPlugin({
474+discordConfig: {},
475+ runtime,
476+});
477+478+const clientArg = {
479+options: { token: "token-race" },
480+registerListener: baseRegisterClientSpy,
481+unregisterListener: vi.fn(),
482+};
483+const registerPromise = (
484+plugin as unknown as {
485+registerClient: (c: typeof clientArg) => Promise<void>;
486+}
487+).registerClient(clientArg);
488+489+// Before the metadata fetch resolves, this.client should already be set so
490+// that a concurrent identify() cannot observe an undefined client.
491+expect((plugin as unknown as { client: unknown }).client).toBe(clientArg);
492+493+resolveGatewayInfoFetch(fetchResolve);
494+await registerPromise;
495+496+expect(baseRegisterClientSpy).toHaveBeenCalledTimes(1);
497+});
498+499+it("skips super.registerClient when an external connect starts during the metadata fetch (regression for #52372)", async () => {
500+const runCase = async (markStarted: (plugin: unknown) => void) => {
501+vi.useFakeTimers();
502+const runtime = createRuntime();
503+let fetchResolve: ((v: Response) => void) | undefined;
504+globalFetchMock.mockImplementation(
505+() =>
506+new Promise<Response>((resolve) => {
507+fetchResolve = resolve;
508+}),
509+);
510+const plugin = createDiscordGatewayPlugin({
511+discordConfig: {},
512+ runtime,
513+});
514+515+const clientArg = {
516+options: { token: "token-race" },
517+registerListener: baseRegisterClientSpy,
518+unregisterListener: vi.fn(),
519+};
520+const registerPromise = (
521+plugin as unknown as {
522+registerClient: (c: typeof clientArg) => Promise<void>;
523+}
524+).registerClient(clientArg);
525+526+markStarted(plugin);
527+resolveGatewayInfoFetch(fetchResolve);
528+await registerPromise;
529+};
530+531+await runCase((plugin) => {
532+(plugin as { ws: unknown }).ws = { readyState: 1 };
533+});
534+expect(baseRegisterClientSpy).not.toHaveBeenCalled();
535+536+baseRegisterClientSpy.mockClear();
537+globalFetchMock.mockReset();
538+vi.useRealTimers();
539+540+await runCase((plugin) => {
541+(plugin as { isConnecting: boolean }).isConnecting = true;
542+});
543+expect(baseRegisterClientSpy).not.toHaveBeenCalled();
544+});
545+448546it("refreshes fallback gateway metadata on the next register attempt", async () => {
449547const runtime = createRuntime();
450548globalFetchMock
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。