mirror of
https://github.com/openclaw/openclaw.git
synced 2026-03-29 10:50:58 +00:00
fix(ci): repair extension and discord test gates
This commit is contained in:
@@ -623,6 +623,9 @@ describe("monitorDiscordProvider", () => {
|
||||
it("continues startup when Discord daily slash-command create quota is exhausted", async () => {
|
||||
const { RateLimitError } = await import("@buape/carbon");
|
||||
const runtime = baseRuntime();
|
||||
const request = new Request("https://discord.com/api/v10/applications/commands", {
|
||||
method: "PUT",
|
||||
});
|
||||
const rateLimitError = new RateLimitError(
|
||||
new Response(null, {
|
||||
status: 429,
|
||||
@@ -636,6 +639,7 @@ describe("monitorDiscordProvider", () => {
|
||||
retry_after: 193.632,
|
||||
global: false,
|
||||
},
|
||||
request,
|
||||
);
|
||||
rateLimitError.discordCode = 30034;
|
||||
clientHandleDeployRequestMock.mockRejectedValueOnce(rateLimitError);
|
||||
|
||||
@@ -413,6 +413,9 @@ describe("sendPollDiscord", () => {
|
||||
});
|
||||
|
||||
function createMockRateLimitError(retryAfter = 0.001): RateLimitError {
|
||||
const request = new Request("https://discord.com/api/v10/channels/789/messages", {
|
||||
method: "POST",
|
||||
});
|
||||
const response = new Response(null, {
|
||||
status: 429,
|
||||
headers: {
|
||||
@@ -420,11 +423,15 @@ function createMockRateLimitError(retryAfter = 0.001): RateLimitError {
|
||||
"X-RateLimit-Bucket": "test-bucket",
|
||||
},
|
||||
});
|
||||
return new RateLimitError(response, {
|
||||
message: "You are being rate limited.",
|
||||
retry_after: retryAfter,
|
||||
global: false,
|
||||
});
|
||||
return new RateLimitError(
|
||||
response,
|
||||
{
|
||||
message: "You are being rate limited.",
|
||||
retry_after: retryAfter,
|
||||
global: false,
|
||||
},
|
||||
request,
|
||||
);
|
||||
}
|
||||
|
||||
describe("retry rate limits", () => {
|
||||
|
||||
@@ -283,11 +283,15 @@ export async function sendDiscordVoiceMessage(
|
||||
retry_after?: number;
|
||||
global?: boolean;
|
||||
};
|
||||
throw new RateLimitError(res, {
|
||||
message: retryData.message ?? "You are being rate limited.",
|
||||
retry_after: retryData.retry_after ?? 1,
|
||||
global: retryData.global ?? false,
|
||||
});
|
||||
throw new RateLimitError(
|
||||
res,
|
||||
{
|
||||
message: retryData.message ?? "You are being rate limited.",
|
||||
retry_after: retryData.retry_after ?? 1,
|
||||
global: retryData.global ?? false,
|
||||
},
|
||||
uploadUrlRequest,
|
||||
);
|
||||
}
|
||||
const errorBody = (await res.json().catch(() => null)) as {
|
||||
code?: number;
|
||||
|
||||
@@ -1,6 +1,24 @@
|
||||
// Private runtime barrel for the bundled Feishu extension.
|
||||
// Keep this barrel thin and aligned with the local extension surface.
|
||||
|
||||
export type {
|
||||
ChannelMessageActionName,
|
||||
ChannelMeta,
|
||||
ChannelOutboundAdapter,
|
||||
OpenClawConfig as ClawdbotConfig,
|
||||
OpenClawConfig,
|
||||
OpenClawPluginApi,
|
||||
PluginRuntime,
|
||||
RuntimeEnv,
|
||||
} from "openclaw/plugin-sdk/feishu";
|
||||
export {
|
||||
DEFAULT_ACCOUNT_ID,
|
||||
PAIRING_APPROVED_MESSAGE,
|
||||
buildChannelConfigSchema,
|
||||
buildProbeChannelStatusSummary,
|
||||
createActionGate,
|
||||
createDefaultChannelRuntimeState,
|
||||
} from "openclaw/plugin-sdk/feishu";
|
||||
export * from "openclaw/plugin-sdk/feishu";
|
||||
export {
|
||||
isRequestBodyLimitError,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
const resolveIrcAccountMock = vi.hoisted(() => vi.fn());
|
||||
const buildIrcConnectOptionsMock = vi.hoisted(() => vi.fn());
|
||||
@@ -16,9 +16,21 @@ vi.mock("./client.js", () => ({
|
||||
connectIrcClient: connectIrcClientMock,
|
||||
}));
|
||||
|
||||
import { probeIrc } from "./probe.js";
|
||||
let probeIrc: typeof import("./probe.js").probeIrc;
|
||||
|
||||
describe("probeIrc", () => {
|
||||
beforeEach(async () => {
|
||||
vi.resetModules();
|
||||
resolveIrcAccountMock.mockReset();
|
||||
buildIrcConnectOptionsMock.mockReset();
|
||||
connectIrcClientMock.mockReset();
|
||||
({ probeIrc } = await import("./probe.js"));
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
vi.restoreAllMocks();
|
||||
});
|
||||
|
||||
it("returns a configuration error when the IRC account is incomplete", async () => {
|
||||
resolveIrcAccountMock.mockReturnValue({
|
||||
configured: false,
|
||||
|
||||
@@ -13,6 +13,7 @@ import {
|
||||
} from "../../../test/helpers/extensions/start-account-lifecycle.js";
|
||||
import type { ResolvedIrcAccount } from "./accounts.js";
|
||||
import { ircPlugin } from "./channel.js";
|
||||
import { setIrcRuntime } from "./runtime.js";
|
||||
import {
|
||||
ircSetupAdapter,
|
||||
parsePort,
|
||||
@@ -56,6 +57,26 @@ function buildAccount(): ResolvedIrcAccount {
|
||||
};
|
||||
}
|
||||
|
||||
function installIrcRuntime() {
|
||||
setIrcRuntime({
|
||||
logging: {
|
||||
shouldLogVerbose: vi.fn(() => false),
|
||||
getChildLogger: vi.fn(() => ({
|
||||
debug: vi.fn(),
|
||||
info: vi.fn(),
|
||||
warn: vi.fn(),
|
||||
error: vi.fn(),
|
||||
})),
|
||||
},
|
||||
channel: {
|
||||
activity: {
|
||||
record: vi.fn(),
|
||||
get: vi.fn(),
|
||||
},
|
||||
},
|
||||
} as never);
|
||||
}
|
||||
|
||||
describe("irc setup", () => {
|
||||
afterEach(() => {
|
||||
vi.clearAllMocks();
|
||||
@@ -329,10 +350,16 @@ describe("irc setup", () => {
|
||||
|
||||
it("keeps startAccount pending until abort, then stops the monitor", async () => {
|
||||
const stop = vi.fn();
|
||||
vi.resetModules();
|
||||
vi.doMock("../../../src/generated/bundled-channel-entries.generated.ts", () => ({
|
||||
GENERATED_BUNDLED_CHANNEL_ENTRIES: [],
|
||||
}));
|
||||
hoisted.monitorIrcProvider.mockResolvedValue({ stop });
|
||||
installIrcRuntime();
|
||||
const { ircPlugin: runtimeMockedPlugin } = await import("./channel.js");
|
||||
|
||||
const { abort, task, isSettled } = startAccountAndTrackLifecycle({
|
||||
startAccount: ircPlugin.gateway!.startAccount!,
|
||||
startAccount: runtimeMockedPlugin.gateway!.startAccount!,
|
||||
account: buildAccount(),
|
||||
});
|
||||
|
||||
|
||||
@@ -180,6 +180,7 @@ function collectRuntimeApiPreExports(runtimeApiPath: string): string[] {
|
||||
true,
|
||||
);
|
||||
const preExports = new Set<string>();
|
||||
let pluginSdkLineRuntimeSeen = false;
|
||||
|
||||
for (const statement of runtimeApiFile.statements) {
|
||||
if (!ts.isExportDeclaration(statement)) {
|
||||
@@ -193,6 +194,7 @@ function collectRuntimeApiPreExports(runtimeApiPath: string): string[] {
|
||||
continue;
|
||||
}
|
||||
if (moduleSpecifier === "openclaw/plugin-sdk/line-runtime") {
|
||||
pluginSdkLineRuntimeSeen = true;
|
||||
break;
|
||||
}
|
||||
const normalized = normalizeModuleSpecifier(moduleSpecifier);
|
||||
@@ -206,6 +208,10 @@ function collectRuntimeApiPreExports(runtimeApiPath: string): string[] {
|
||||
}
|
||||
}
|
||||
|
||||
if (!pluginSdkLineRuntimeSeen) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return Array.from(preExports).toSorted();
|
||||
}
|
||||
|
||||
|
||||
@@ -111,6 +111,10 @@ vi.mock("../../resolve-targets.js", () => ({
|
||||
resolveMatrixTargets: vi.fn(async () => []),
|
||||
}));
|
||||
|
||||
vi.mock("../../../../../src/generated/bundled-channel-entries.generated.ts", () => ({
|
||||
GENERATED_BUNDLED_CHANNEL_ENTRIES: [],
|
||||
}));
|
||||
|
||||
vi.mock("../../runtime.js", () => ({
|
||||
getMatrixRuntime: () => ({
|
||||
config: {
|
||||
@@ -460,7 +464,19 @@ describe("matrix plugin registration", () => {
|
||||
loadRuntimeApiExportTypesViaJiti({
|
||||
modulePath: runtimeApiPath,
|
||||
exportNames: [],
|
||||
realPluginSdkSpecifiers: [],
|
||||
realPluginSdkSpecifiers: [
|
||||
"openclaw/plugin-sdk/account-helpers",
|
||||
"openclaw/plugin-sdk/allow-from",
|
||||
"openclaw/plugin-sdk/channel-config-helpers",
|
||||
"openclaw/plugin-sdk/channel-policy",
|
||||
"openclaw/plugin-sdk/core",
|
||||
"openclaw/plugin-sdk/directory-runtime",
|
||||
"openclaw/plugin-sdk/extension-shared",
|
||||
"openclaw/plugin-sdk/irc",
|
||||
"openclaw/plugin-sdk/signal",
|
||||
"openclaw/plugin-sdk/status-helpers",
|
||||
"openclaw/plugin-sdk/text-runtime",
|
||||
],
|
||||
}),
|
||||
).toEqual({});
|
||||
}, 240_000);
|
||||
|
||||
@@ -1,14 +1,30 @@
|
||||
export {
|
||||
DEFAULT_ACCOUNT_ID,
|
||||
PAIRING_APPROVED_MESSAGE,
|
||||
buildChannelConfigSchema,
|
||||
buildProbeChannelStatusSummary,
|
||||
collectStatusIssuesFromLastError,
|
||||
createActionGate,
|
||||
formatZonedTimestamp,
|
||||
getChatChannelMeta,
|
||||
jsonResult,
|
||||
normalizeAccountId,
|
||||
normalizeOptionalAccountId,
|
||||
readNumberParam,
|
||||
readReactionParams,
|
||||
readStringArrayParam,
|
||||
readStringParam,
|
||||
} from "openclaw/plugin-sdk/matrix";
|
||||
export * from "openclaw/plugin-sdk/matrix";
|
||||
export {
|
||||
assertHttpUrlTargetsPrivateNetwork,
|
||||
buildTimeoutAbortSignal,
|
||||
closeDispatcher,
|
||||
createPinnedDispatcher,
|
||||
resolvePinnedHostnameWithPolicy,
|
||||
ssrfPolicyFromAllowPrivateNetwork,
|
||||
type LookupFn,
|
||||
type SsrFPolicy,
|
||||
} from "openclaw/plugin-sdk/infra-runtime";
|
||||
} from "openclaw/plugin-sdk/ssrf-runtime";
|
||||
export {
|
||||
dispatchReplyFromConfigWithSettledDispatcher,
|
||||
ensureConfiguredAcpBindingReady,
|
||||
@@ -17,3 +33,35 @@ export {
|
||||
} from "openclaw/plugin-sdk/matrix-runtime-heavy";
|
||||
// resolveMatrixAccountStringValues already comes from plugin-sdk/matrix.
|
||||
// Re-exporting auth-precedence here makes Jiti try to define the same export twice.
|
||||
|
||||
export function buildTimeoutAbortSignal(params: { timeoutMs?: number; signal?: AbortSignal }): {
|
||||
signal?: AbortSignal;
|
||||
cleanup: () => void;
|
||||
} {
|
||||
const { timeoutMs, signal } = params;
|
||||
if (!timeoutMs && !signal) {
|
||||
return { signal: undefined, cleanup: () => {} };
|
||||
}
|
||||
if (!timeoutMs) {
|
||||
return { signal, cleanup: () => {} };
|
||||
}
|
||||
|
||||
const controller = new AbortController();
|
||||
const timeoutId = setTimeout(controller.abort.bind(controller), timeoutMs);
|
||||
const onAbort = () => controller.abort();
|
||||
if (signal) {
|
||||
if (signal.aborted) {
|
||||
controller.abort();
|
||||
} else {
|
||||
signal.addEventListener("abort", onAbort, { once: true });
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
signal: controller.signal,
|
||||
cleanup: () => {
|
||||
clearTimeout(timeoutId);
|
||||
signal?.removeEventListener("abort", onAbort);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@@ -37,6 +37,7 @@ export {
|
||||
buildChannelKeyCandidates,
|
||||
resolveChannelEntryMatch,
|
||||
} from "../channels/plugins/channel-config.js";
|
||||
export { getChatChannelMeta } from "./channel-plugin-common.js";
|
||||
export { createAccountListHelpers } from "../channels/plugins/account-helpers.js";
|
||||
export {
|
||||
deleteAccountFromConfigSection,
|
||||
|
||||
Reference in New Issue
Block a user