Discord: handle early gateway startup errors

This commit is contained in:
Theo Tarr
2026-02-22 13:43:06 -05:00
committed by Peter Steinberger
parent 9b81a53016
commit 7af6849c2f
5 changed files with 205 additions and 36 deletions

View File

@@ -1,8 +1,11 @@
import { EventEmitter } from "node:events";
import { beforeEach, describe, expect, it, vi } from "vitest";
import type { OpenClawConfig } from "../../config/config.js";
import type { RuntimeEnv } from "../../runtime.js";
const {
clientFetchUserMock,
clientGetPluginMock,
createDiscordNativeCommandMock,
createNoopThreadBindingManagerMock,
createThreadBindingManagerMock,
@@ -17,6 +20,8 @@ const {
} = vi.hoisted(() => {
const createdBindingManagers: Array<{ stop: ReturnType<typeof vi.fn> }> = [];
return {
clientFetchUserMock: vi.fn(async () => ({ id: "bot-1" })),
clientGetPluginMock: vi.fn(() => undefined),
createDiscordNativeCommandMock: vi.fn(() => ({ name: "mock-command" })),
createNoopThreadBindingManagerMock: vi.fn(() => {
const manager = { stop: vi.fn() };
@@ -65,11 +70,11 @@ vi.mock("@buape/carbon", () => {
async handleDeployRequest() {
return undefined;
}
async fetchUser(_target: string) {
return { id: "bot-1" };
async fetchUser(target: string) {
return await clientFetchUserMock(target);
}
getPlugin(_name: string) {
return undefined;
getPlugin(name: string) {
return clientGetPluginMock(name);
}
}
return { Client, ReadyListener };
@@ -242,6 +247,8 @@ describe("monitorDiscordProvider", () => {
}) as OpenClawConfig;
beforeEach(() => {
clientFetchUserMock.mockClear().mockResolvedValue({ id: "bot-1" });
clientGetPluginMock.mockClear().mockReturnValue(undefined);
createDiscordNativeCommandMock.mockClear().mockReturnValue({ name: "mock-command" });
createNoopThreadBindingManagerMock.mockClear();
createThreadBindingManagerMock.mockClear();
@@ -290,4 +297,28 @@ describe("monitorDiscordProvider", () => {
expect(createdBindingManagers).toHaveLength(1);
expect(createdBindingManagers[0]?.stop).toHaveBeenCalledTimes(1);
});
it("captures gateway errors emitted before lifecycle wait starts", async () => {
const { monitorDiscordProvider } = await import("./provider.js");
const emitter = new EventEmitter();
clientGetPluginMock.mockImplementation((name: string) =>
name === "gateway" ? { emitter, disconnect: vi.fn() } : undefined,
);
clientFetchUserMock.mockImplementationOnce(async () => {
emitter.emit("error", new Error("Fatal Gateway error: 4014"));
return { id: "bot-1" };
});
await monitorDiscordProvider({
config: baseConfig(),
runtime: baseRuntime(),
});
expect(monitorLifecycleMock).toHaveBeenCalledTimes(1);
const lifecycleArgs = monitorLifecycleMock.mock.calls[0]?.[0] as {
pendingGatewayErrors?: unknown[];
};
expect(lifecycleArgs.pendingGatewayErrors).toHaveLength(1);
expect(String(lifecycleArgs.pendingGatewayErrors?.[0])).toContain("4014");
});
});