mirror of
https://github.com/openclaw/openclaw.git
synced 2026-03-26 01:11:37 +00:00
* refactor: move Discord channel implementation to extensions/discord/src/ Move all Discord source files from src/discord/ to extensions/discord/src/, following the extension migration pattern. Source files in src/discord/ are replaced with re-export shims. Channel-plugin files from src/channels/plugins/*/discord* are similarly moved and shimmed. - Copy all .ts source files preserving subdirectory structure (monitor/, voice/) - Move channel-plugin files (actions, normalize, onboarding, outbound, status-issues) - Fix all relative imports to use correct paths from new location - Create re-export shims at original locations for backward compatibility - Delete test files from shim locations (tests live in extension now) - Update tsconfig.plugin-sdk.dts.json rootDir from "src" to "." to accommodate extension files outside src/ - Update write-plugin-sdk-entry-dts.ts to match new declaration output paths * fix: add importOriginal to thread-bindings session-meta mock for extensions test * style: fix formatting in thread-bindings lifecycle test
136 lines
4.2 KiB
TypeScript
136 lines
4.2 KiB
TypeScript
import { EventEmitter } from "node:events";
|
|
import { describe, expect, it, vi } from "vitest";
|
|
import { waitForDiscordGatewayStop } from "./monitor.gateway.js";
|
|
|
|
function createGatewayWaitHarness() {
|
|
const emitter = new EventEmitter();
|
|
const disconnect = vi.fn();
|
|
const abort = new AbortController();
|
|
return { emitter, disconnect, abort };
|
|
}
|
|
|
|
function startGatewayWait(params?: {
|
|
onGatewayError?: (error: unknown) => void;
|
|
shouldStopOnError?: (error: unknown) => boolean;
|
|
registerForceStop?: (fn: (error: unknown) => void) => void;
|
|
}) {
|
|
const harness = createGatewayWaitHarness();
|
|
const promise = waitForDiscordGatewayStop({
|
|
gateway: { emitter: harness.emitter, disconnect: harness.disconnect },
|
|
abortSignal: harness.abort.signal,
|
|
...(params?.onGatewayError ? { onGatewayError: params.onGatewayError } : {}),
|
|
...(params?.shouldStopOnError ? { shouldStopOnError: params.shouldStopOnError } : {}),
|
|
...(params?.registerForceStop ? { registerForceStop: params.registerForceStop } : {}),
|
|
});
|
|
return { ...harness, promise };
|
|
}
|
|
|
|
async function expectAbortToResolve(params: {
|
|
emitter: EventEmitter;
|
|
disconnect: ReturnType<typeof vi.fn>;
|
|
abort: AbortController;
|
|
promise: Promise<void>;
|
|
expectedDisconnectBeforeAbort?: number;
|
|
}) {
|
|
if (params.expectedDisconnectBeforeAbort !== undefined) {
|
|
expect(params.disconnect).toHaveBeenCalledTimes(params.expectedDisconnectBeforeAbort);
|
|
}
|
|
expect(params.emitter.listenerCount("error")).toBe(1);
|
|
params.abort.abort();
|
|
await expect(params.promise).resolves.toBeUndefined();
|
|
expect(params.disconnect).toHaveBeenCalledTimes(1);
|
|
expect(params.emitter.listenerCount("error")).toBe(0);
|
|
}
|
|
|
|
describe("waitForDiscordGatewayStop", () => {
|
|
it("resolves on abort and disconnects gateway", async () => {
|
|
const { emitter, disconnect, abort, promise } = startGatewayWait();
|
|
await expectAbortToResolve({ emitter, disconnect, abort, promise });
|
|
});
|
|
|
|
it("rejects on gateway error and disconnects", async () => {
|
|
const onGatewayError = vi.fn();
|
|
const err = new Error("boom");
|
|
|
|
const { emitter, disconnect, abort, promise } = startGatewayWait({
|
|
onGatewayError,
|
|
});
|
|
|
|
emitter.emit("error", err);
|
|
|
|
await expect(promise).rejects.toThrow("boom");
|
|
expect(onGatewayError).toHaveBeenCalledWith(err);
|
|
expect(disconnect).toHaveBeenCalledTimes(1);
|
|
expect(emitter.listenerCount("error")).toBe(0);
|
|
|
|
abort.abort();
|
|
expect(disconnect).toHaveBeenCalledTimes(1);
|
|
});
|
|
|
|
it("ignores gateway errors when instructed", async () => {
|
|
const onGatewayError = vi.fn();
|
|
const err = new Error("transient");
|
|
|
|
const { emitter, disconnect, abort, promise } = startGatewayWait({
|
|
onGatewayError,
|
|
shouldStopOnError: () => false,
|
|
});
|
|
|
|
emitter.emit("error", err);
|
|
expect(onGatewayError).toHaveBeenCalledWith(err);
|
|
await expectAbortToResolve({
|
|
emitter,
|
|
disconnect,
|
|
abort,
|
|
promise,
|
|
expectedDisconnectBeforeAbort: 0,
|
|
});
|
|
});
|
|
|
|
it("resolves on abort without a gateway", async () => {
|
|
const abort = new AbortController();
|
|
|
|
const promise = waitForDiscordGatewayStop({
|
|
abortSignal: abort.signal,
|
|
});
|
|
|
|
abort.abort();
|
|
|
|
await expect(promise).resolves.toBeUndefined();
|
|
});
|
|
|
|
it("rejects via registerForceStop and disconnects gateway", async () => {
|
|
let forceStop: ((err: unknown) => void) | undefined;
|
|
|
|
const { emitter, disconnect, promise } = startGatewayWait({
|
|
registerForceStop: (fn) => {
|
|
forceStop = fn;
|
|
},
|
|
});
|
|
|
|
expect(forceStop).toBeDefined();
|
|
|
|
forceStop?.(new Error("reconnect watchdog timeout"));
|
|
|
|
await expect(promise).rejects.toThrow("reconnect watchdog timeout");
|
|
expect(disconnect).toHaveBeenCalledTimes(1);
|
|
expect(emitter.listenerCount("error")).toBe(0);
|
|
});
|
|
|
|
it("ignores forceStop after promise already settled", async () => {
|
|
let forceStop: ((err: unknown) => void) | undefined;
|
|
|
|
const { abort, disconnect, promise } = startGatewayWait({
|
|
registerForceStop: (fn) => {
|
|
forceStop = fn;
|
|
},
|
|
});
|
|
|
|
abort.abort();
|
|
await expect(promise).resolves.toBeUndefined();
|
|
|
|
forceStop?.(new Error("too late"));
|
|
expect(disconnect).toHaveBeenCalledTimes(1);
|
|
});
|
|
});
|