mirror of
https://github.com/openclaw/openclaw.git
synced 2026-04-19 21:21:10 +00:00
test(discord): proxy fetch regression coverage for REST, webhook, and stagger
This commit is contained in:
committed by
Peter Steinberger
parent
c8223606ca
commit
3a4fd62135
@@ -9,6 +9,15 @@ let setDiscordRuntime: typeof import("./runtime.js").setDiscordRuntime;
|
||||
const probeDiscordMock = vi.hoisted(() => vi.fn());
|
||||
const monitorDiscordProviderMock = vi.hoisted(() => vi.fn());
|
||||
const auditDiscordChannelPermissionsMock = vi.hoisted(() => vi.fn());
|
||||
const sleepWithAbortMock = vi.hoisted(() => vi.fn(async () => undefined));
|
||||
|
||||
vi.mock("openclaw/plugin-sdk/runtime-env", async (importOriginal) => {
|
||||
const actual = await importOriginal<typeof import("openclaw/plugin-sdk/runtime-env")>();
|
||||
return {
|
||||
...actual,
|
||||
sleepWithAbort: sleepWithAbortMock,
|
||||
};
|
||||
});
|
||||
|
||||
vi.mock("./probe.js", async (importOriginal) => {
|
||||
const actual = await importOriginal<typeof import("./probe.js")>();
|
||||
@@ -45,14 +54,14 @@ function createCfg(): OpenClawConfig {
|
||||
} as OpenClawConfig;
|
||||
}
|
||||
|
||||
function resolveAccount(cfg: OpenClawConfig): ResolvedDiscordAccount {
|
||||
return discordPlugin.config.resolveAccount(cfg, "default") as ResolvedDiscordAccount;
|
||||
function resolveAccount(cfg: OpenClawConfig, accountId = "default"): ResolvedDiscordAccount {
|
||||
return discordPlugin.config.resolveAccount(cfg, accountId) as ResolvedDiscordAccount;
|
||||
}
|
||||
|
||||
function startDiscordAccount(cfg: OpenClawConfig) {
|
||||
function startDiscordAccount(cfg: OpenClawConfig, accountId = "default") {
|
||||
return discordPlugin.gateway!.startAccount!(
|
||||
createStartAccountContext({
|
||||
account: resolveAccount(cfg),
|
||||
account: resolveAccount(cfg, accountId),
|
||||
cfg,
|
||||
}),
|
||||
);
|
||||
@@ -73,6 +82,8 @@ afterEach(() => {
|
||||
probeDiscordMock.mockReset();
|
||||
monitorDiscordProviderMock.mockReset();
|
||||
auditDiscordChannelPermissionsMock.mockReset();
|
||||
sleepWithAbortMock.mockReset();
|
||||
sleepWithAbortMock.mockResolvedValue(undefined);
|
||||
});
|
||||
|
||||
beforeEach(async () => {
|
||||
@@ -184,9 +195,46 @@ describe("discordPlugin outbound", () => {
|
||||
accountId: "default",
|
||||
}),
|
||||
);
|
||||
expect(sleepWithAbortMock).not.toHaveBeenCalled();
|
||||
expect(runtimeProbeDiscord).not.toHaveBeenCalled();
|
||||
expect(runtimeMonitorDiscordProvider).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("stagger starts later accounts in multi-bot setups", async () => {
|
||||
probeDiscordMock.mockResolvedValue({
|
||||
ok: true,
|
||||
bot: { username: "Cherry" },
|
||||
application: {
|
||||
intents: {
|
||||
messageContent: "limited",
|
||||
guildMembers: "disabled",
|
||||
presence: "disabled",
|
||||
},
|
||||
},
|
||||
elapsedMs: 1,
|
||||
});
|
||||
monitorDiscordProviderMock.mockResolvedValue(undefined);
|
||||
|
||||
const cfg = {
|
||||
channels: {
|
||||
discord: {
|
||||
accounts: {
|
||||
// "alpha" sorts before "zeta" so alpha is index 0, zeta is index 1
|
||||
alpha: { token: "Bot alpha-token", enabled: true },
|
||||
zeta: { token: "Bot zeta-token", enabled: true },
|
||||
},
|
||||
},
|
||||
},
|
||||
} as OpenClawConfig;
|
||||
|
||||
// First account (index 0) — no delay
|
||||
await startDiscordAccount(cfg, "alpha");
|
||||
expect(sleepWithAbortMock).not.toHaveBeenCalled();
|
||||
|
||||
// Second account (index 1) — 10s delay
|
||||
await startDiscordAccount(cfg, "zeta");
|
||||
expect(sleepWithAbortMock).toHaveBeenCalledWith(10_000, expect.any(Object));
|
||||
});
|
||||
});
|
||||
|
||||
describe("discordPlugin bindings", () => {
|
||||
|
||||
42
extensions/discord/src/client.proxy.test.ts
Normal file
42
extensions/discord/src/client.proxy.test.ts
Normal file
@@ -0,0 +1,42 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import type { OpenClawConfig } from "../../../src/config/config.js";
|
||||
import { createDiscordRestClient } from "./client.js";
|
||||
|
||||
describe("createDiscordRestClient proxy support", () => {
|
||||
it("injects a custom fetch into RequestClient when a Discord proxy is configured", () => {
|
||||
const cfg = {
|
||||
channels: {
|
||||
discord: {
|
||||
token: "Bot test-token",
|
||||
proxy: "http://proxy.test:8080",
|
||||
},
|
||||
},
|
||||
} as OpenClawConfig;
|
||||
|
||||
const { rest } = createDiscordRestClient({}, cfg);
|
||||
const requestClient = rest as unknown as {
|
||||
customFetch?: typeof fetch;
|
||||
options?: { fetch?: typeof fetch };
|
||||
};
|
||||
|
||||
expect(requestClient.options?.fetch).toEqual(expect.any(Function));
|
||||
expect(requestClient.customFetch).toBe(requestClient.options?.fetch);
|
||||
});
|
||||
|
||||
it("does not inject fetch when no proxy is configured", () => {
|
||||
const cfg = {
|
||||
channels: {
|
||||
discord: {
|
||||
token: "Bot test-token",
|
||||
},
|
||||
},
|
||||
} as OpenClawConfig;
|
||||
|
||||
const { rest } = createDiscordRestClient({}, cfg);
|
||||
const requestClient = rest as unknown as {
|
||||
options?: { fetch?: typeof fetch };
|
||||
};
|
||||
|
||||
expect(requestClient.options?.fetch).toBeUndefined();
|
||||
});
|
||||
});
|
||||
68
extensions/discord/src/send.webhook.proxy.test.ts
Normal file
68
extensions/discord/src/send.webhook.proxy.test.ts
Normal file
@@ -0,0 +1,68 @@
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import type { OpenClawConfig } from "../../../src/config/config.js";
|
||||
import { sendWebhookMessageDiscord } from "./send.outbound.js";
|
||||
|
||||
const makeProxyFetchMock = vi.hoisted(() => vi.fn());
|
||||
|
||||
vi.mock("openclaw/plugin-sdk/infra-runtime", async (importOriginal) => {
|
||||
const actual = await importOriginal<typeof import("openclaw/plugin-sdk/infra-runtime")>();
|
||||
return {
|
||||
...actual,
|
||||
makeProxyFetch: makeProxyFetchMock,
|
||||
};
|
||||
});
|
||||
|
||||
describe("sendWebhookMessageDiscord proxy support", () => {
|
||||
it("uses proxy fetch when a Discord proxy is configured", async () => {
|
||||
const proxiedFetch = vi
|
||||
.fn()
|
||||
.mockResolvedValue(new Response(JSON.stringify({ id: "msg-1" }), { status: 200 }));
|
||||
makeProxyFetchMock.mockReturnValue(proxiedFetch);
|
||||
|
||||
const cfg = {
|
||||
channels: {
|
||||
discord: {
|
||||
token: "Bot test-token",
|
||||
proxy: "http://proxy.test:8080",
|
||||
},
|
||||
},
|
||||
} as OpenClawConfig;
|
||||
|
||||
await sendWebhookMessageDiscord("hello", {
|
||||
cfg,
|
||||
accountId: "default",
|
||||
webhookId: "123",
|
||||
webhookToken: "abc",
|
||||
wait: true,
|
||||
});
|
||||
|
||||
expect(makeProxyFetchMock).toHaveBeenCalledWith("http://proxy.test:8080");
|
||||
expect(proxiedFetch).toHaveBeenCalledOnce();
|
||||
});
|
||||
|
||||
it("uses global fetch when no proxy is configured", async () => {
|
||||
makeProxyFetchMock.mockReturnValue(undefined);
|
||||
const globalFetchMock = vi
|
||||
.spyOn(globalThis, "fetch")
|
||||
.mockResolvedValue(new Response(JSON.stringify({ id: "msg-2" }), { status: 200 }));
|
||||
|
||||
const cfg = {
|
||||
channels: {
|
||||
discord: {
|
||||
token: "Bot test-token",
|
||||
},
|
||||
},
|
||||
} as OpenClawConfig;
|
||||
|
||||
await sendWebhookMessageDiscord("hello", {
|
||||
cfg,
|
||||
accountId: "default",
|
||||
webhookId: "123",
|
||||
webhookToken: "abc",
|
||||
wait: true,
|
||||
});
|
||||
|
||||
expect(globalFetchMock).toHaveBeenCalledOnce();
|
||||
globalFetchMock.mockRestore();
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user