mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 06:20:43 +00:00
test: fix rebased local gates
This commit is contained in:
@@ -2,13 +2,13 @@ import type { ClawdbotConfig, RuntimeEnv } from "../runtime-api.js";
|
||||
import { resolveFeishuRuntimeAccount } from "./accounts.js";
|
||||
import { handleFeishuMessage, type FeishuMessageEvent } from "./bot.js";
|
||||
import { decodeFeishuCardAction, buildFeishuCardActionTextFallback } from "./card-interaction.js";
|
||||
import { createFeishuClient } from "./client.js";
|
||||
import {
|
||||
createApprovalCard,
|
||||
FEISHU_APPROVAL_CANCEL_ACTION,
|
||||
FEISHU_APPROVAL_CONFIRM_ACTION,
|
||||
FEISHU_APPROVAL_REQUEST_ACTION,
|
||||
} from "./card-ux-approval.js";
|
||||
import { createFeishuClient } from "./client.js";
|
||||
import { sendCardFeishu, sendMessageFeishu } from "./send.js";
|
||||
|
||||
export type FeishuCardActionEvent = {
|
||||
@@ -234,7 +234,10 @@ async function resolveCardActionChatType(params: {
|
||||
normalizeResolvedCardActionChatType(response.data?.chat_mode) ??
|
||||
normalizeResolvedCardActionChatType(response.data?.chat_type);
|
||||
if (resolvedChatType) {
|
||||
resolvedChatTypeCache.set(cacheKey, { value: resolvedChatType, expiresAt: now + CHAT_TYPE_CACHE_TTL_MS });
|
||||
resolvedChatTypeCache.set(cacheKey, {
|
||||
value: resolvedChatType,
|
||||
expiresAt: now + CHAT_TYPE_CACHE_TTL_MS,
|
||||
});
|
||||
return resolvedChatType;
|
||||
}
|
||||
params.log(
|
||||
|
||||
@@ -724,9 +724,7 @@ export function createTaskFlowWebhookRequestHandler(params: {
|
||||
return false;
|
||||
}
|
||||
const resolvedSecret = await resolveTargetSecret(candidate);
|
||||
return Boolean(
|
||||
resolvedSecret && timingSafeEquals(resolvedSecret, presentedSecret),
|
||||
);
|
||||
return Boolean(resolvedSecret && timingSafeEquals(resolvedSecret, presentedSecret));
|
||||
},
|
||||
});
|
||||
if (!target) {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { NON_ENV_SECRETREF_MARKER } from "openclaw/plugin-sdk/provider-auth-runtime";
|
||||
import { createNonExitingRuntime } from "openclaw/plugin-sdk/runtime-env";
|
||||
import { withEnv } from "openclaw/plugin-sdk/testing";
|
||||
import { withEnv, withEnvAsync } from "openclaw/plugin-sdk/testing";
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import { createWizardPrompter } from "../../test/helpers/wizard-prompter.js";
|
||||
import { resolveXaiCatalogEntry } from "./model-definitions.js";
|
||||
@@ -79,7 +79,7 @@ describe("xai web search config resolution", () => {
|
||||
});
|
||||
|
||||
it("treats unresolved non-env SecretRefs as missing credentials instead of throwing", async () => {
|
||||
await withEnv({ XAI_API_KEY: undefined }, async () => {
|
||||
await withEnvAsync({ XAI_API_KEY: undefined }, async () => {
|
||||
const provider = createXaiWebSearchProvider();
|
||||
const maybeTool = provider.createTool({
|
||||
config: {
|
||||
|
||||
@@ -45,12 +45,9 @@ const GROQ_TOO_MANY_REQUESTS_MESSAGE =
|
||||
"429 Too Many Requests: Too many requests were sent in a given timeframe.";
|
||||
const GROQ_SERVICE_UNAVAILABLE_MESSAGE =
|
||||
"503 Service Unavailable: The server is temporarily unable to handle the request due to overloading or maintenance."; // pragma: allowlist secret
|
||||
const PLAIN_INTERNAL_SERVER_ERROR_STATUS_SAMPLE =
|
||||
"Proxy notice: Status: Internal Server Error";
|
||||
const MIXED_INTERNAL_SERVER_ERROR_STATUS_SAMPLE =
|
||||
`${PLAIN_INTERNAL_SERVER_ERROR_STATUS_SAMPLE}; upstream connect error`;
|
||||
const INTERNAL_SERVER_ERROR_STATUS_WITH_500_SAMPLE =
|
||||
`${PLAIN_INTERNAL_SERVER_ERROR_STATUS_SAMPLE}; code:500`;
|
||||
const PLAIN_INTERNAL_SERVER_ERROR_STATUS_SAMPLE = "Proxy notice: Status: Internal Server Error";
|
||||
const MIXED_INTERNAL_SERVER_ERROR_STATUS_SAMPLE = `${PLAIN_INTERNAL_SERVER_ERROR_STATUS_SAMPLE}; upstream connect error`;
|
||||
const INTERNAL_SERVER_ERROR_STATUS_WITH_500_SAMPLE = `${PLAIN_INTERNAL_SERVER_ERROR_STATUS_SAMPLE}; code:500`;
|
||||
|
||||
function expectMessageMatches(
|
||||
matcher: (message: string) => boolean,
|
||||
|
||||
@@ -137,7 +137,10 @@ export function applyFinalEffectiveToolPolicy(
|
||||
isSubagentSessionKey(params.sessionKey) && params.sessionKey
|
||||
? resolveSubagentToolPolicyForSession(params.config, params.sessionKey)
|
||||
: undefined;
|
||||
const ownerFiltered = applyOwnerOnlyToolPolicy(params.bundledTools, params.senderIsOwner === true);
|
||||
const ownerFiltered = applyOwnerOnlyToolPolicy(
|
||||
params.bundledTools,
|
||||
params.senderIsOwner === true,
|
||||
);
|
||||
// Suppress unavailable-core-tool warnings on every step of this pass.
|
||||
// `applyToolPolicyPipeline` infers `coreToolNames` from the `tools` array
|
||||
// it's filtering, and this pass only sees the bundled MCP/LSP subset.
|
||||
|
||||
@@ -10,8 +10,8 @@ import { getGlobalHookRunner } from "../../plugins/hook-runner-global.js";
|
||||
import { enqueueCommandInLane } from "../../process/command-queue.js";
|
||||
import { normalizeOptionalString } from "../../shared/string-coerce.js";
|
||||
import { sanitizeForLog } from "../../terminal/ansi.js";
|
||||
import { isMarkdownCapableMessageChannel } from "../../utils/message-channel.js";
|
||||
import { resolveUserPath } from "../../utils.js";
|
||||
import { isMarkdownCapableMessageChannel } from "../../utils/message-channel.js";
|
||||
import { resolveOpenClawAgentDir } from "../agent-paths.js";
|
||||
import {
|
||||
hasConfiguredModelFallbacks,
|
||||
|
||||
@@ -15,7 +15,11 @@ import type { SessionEntry } from "../../config/sessions/types.js";
|
||||
import type { OpenClawConfig } from "../../config/types.openclaw.js";
|
||||
import { logVerbose } from "../../globals.js";
|
||||
import { clearCommandLane, getQueueSize } from "../../process/command-queue.js";
|
||||
import { isAcpSessionKey, isSubagentSessionKey, normalizeMainKey } from "../../routing/session-key.js";
|
||||
import {
|
||||
isAcpSessionKey,
|
||||
isSubagentSessionKey,
|
||||
normalizeMainKey,
|
||||
} from "../../routing/session-key.js";
|
||||
import { normalizeOptionalString } from "../../shared/string-coerce.js";
|
||||
import { isReasoningTagProvider } from "../../utils/provider-utils.js";
|
||||
import { hasControlCommand } from "../command-detection.js";
|
||||
|
||||
@@ -154,12 +154,15 @@ describe("buildGatewayCronService", () => {
|
||||
state.cron as unknown as {
|
||||
state?: {
|
||||
deps?: {
|
||||
enqueueSystemEvent?: (optsText: string, opts?: {
|
||||
agentId?: string;
|
||||
sessionKey?: string;
|
||||
contextKey?: string;
|
||||
trusted?: boolean;
|
||||
}) => void;
|
||||
enqueueSystemEvent?: (
|
||||
optsText: string,
|
||||
opts?: {
|
||||
agentId?: string;
|
||||
sessionKey?: string;
|
||||
contextKey?: string;
|
||||
trusted?: boolean;
|
||||
},
|
||||
) => void;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
@@ -72,9 +72,9 @@ vi.mock("../../agents/agent-scope.js", () => ({
|
||||
}));
|
||||
|
||||
vi.mock("../../auto-reply/reply/session-reset-prompt.js", async () => {
|
||||
const actual = await vi.importActual<typeof import("../../auto-reply/reply/session-reset-prompt.js")>(
|
||||
"../../auto-reply/reply/session-reset-prompt.js",
|
||||
);
|
||||
const actual = await vi.importActual<
|
||||
typeof import("../../auto-reply/reply/session-reset-prompt.js")
|
||||
>("../../auto-reply/reply/session-reset-prompt.js");
|
||||
return {
|
||||
...actual,
|
||||
resolveBareResetBootstrapFileAccess: mocks.resolveBareResetBootstrapFileAccess,
|
||||
@@ -1278,45 +1278,48 @@ describe("gateway agent handler", () => {
|
||||
});
|
||||
|
||||
it("uses request model override when resolving bare /new bootstrap file access", async () => {
|
||||
await withTempDir({ prefix: "openclaw-gateway-reset-model-override-" }, async (workspaceDir) => {
|
||||
await fs.writeFile(`${workspaceDir}/BOOTSTRAP.md`, "bootstrap ritual", "utf-8");
|
||||
mocks.loadConfigReturn = {
|
||||
agents: {
|
||||
defaults: {
|
||||
workspace: workspaceDir,
|
||||
await withTempDir(
|
||||
{ prefix: "openclaw-gateway-reset-model-override-" },
|
||||
async (workspaceDir) => {
|
||||
await fs.writeFile(`${workspaceDir}/BOOTSTRAP.md`, "bootstrap ritual", "utf-8");
|
||||
mocks.loadConfigReturn = {
|
||||
agents: {
|
||||
defaults: {
|
||||
workspace: workspaceDir,
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
mockSessionResetSuccess({ reason: "new" });
|
||||
primeMainAgentRun({ sessionId: "reset-session-id", cfg: mocks.loadConfigReturn });
|
||||
};
|
||||
mockSessionResetSuccess({ reason: "new" });
|
||||
primeMainAgentRun({ sessionId: "reset-session-id", cfg: mocks.loadConfigReturn });
|
||||
|
||||
await invokeAgent(
|
||||
{
|
||||
message: "/new",
|
||||
sessionKey: "agent:main:main",
|
||||
provider: "openai",
|
||||
model: "gpt-5.4-mini",
|
||||
idempotencyKey: "test-idem-new-bootstrap-model-override",
|
||||
},
|
||||
{
|
||||
reqId: "4-bootstrap-model-override",
|
||||
client: {
|
||||
connect: { scopes: ["operator.admin"] },
|
||||
internal: { allowModelOverride: true },
|
||||
} as AgentHandlerArgs["client"],
|
||||
},
|
||||
);
|
||||
await invokeAgent(
|
||||
{
|
||||
message: "/new",
|
||||
sessionKey: "agent:main:main",
|
||||
provider: "openai",
|
||||
model: "gpt-5.4-mini",
|
||||
idempotencyKey: "test-idem-new-bootstrap-model-override",
|
||||
},
|
||||
{
|
||||
reqId: "4-bootstrap-model-override",
|
||||
client: {
|
||||
connect: { scopes: ["operator.admin"] },
|
||||
internal: { allowModelOverride: true },
|
||||
} as AgentHandlerArgs["client"],
|
||||
},
|
||||
);
|
||||
|
||||
await waitForAssertion(() =>
|
||||
expect(mocks.resolveBareResetBootstrapFileAccess).toHaveBeenCalled(),
|
||||
);
|
||||
expect(mocks.resolveBareResetBootstrapFileAccess).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
modelProvider: "openai",
|
||||
modelId: "gpt-5.4-mini",
|
||||
}),
|
||||
);
|
||||
});
|
||||
await waitForAssertion(() =>
|
||||
expect(mocks.resolveBareResetBootstrapFileAccess).toHaveBeenCalled(),
|
||||
);
|
||||
expect(mocks.resolveBareResetBootstrapFileAccess).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
modelProvider: "openai",
|
||||
modelId: "gpt-5.4-mini",
|
||||
}),
|
||||
);
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
it("rejects malformed agent session keys early in agent handler", async () => {
|
||||
|
||||
@@ -991,9 +991,7 @@ describe("gateway send mirroring", () => {
|
||||
// forced to false so a non-admin scoped caller cannot unlock owner-only
|
||||
// channel actions.
|
||||
setActivePluginRegistry(
|
||||
createTestRegistry([
|
||||
{ pluginId: "whatsapp", source: "test", plugin: reactPlugin },
|
||||
]),
|
||||
createTestRegistry([{ pluginId: "whatsapp", source: "test", plugin: reactPlugin }]),
|
||||
"send-test-owner-derive-non-admin",
|
||||
);
|
||||
await runMessageActionRequest(
|
||||
@@ -1011,9 +1009,7 @@ describe("gateway send mirroring", () => {
|
||||
// Full operator (admin-scoped): the trusted runtime is allowed to
|
||||
// forward the real channel-sender ownership bit. Wire true → true.
|
||||
setActivePluginRegistry(
|
||||
createTestRegistry([
|
||||
{ pluginId: "whatsapp", source: "test", plugin: reactPlugin },
|
||||
]),
|
||||
createTestRegistry([{ pluginId: "whatsapp", source: "test", plugin: reactPlugin }]),
|
||||
"send-test-owner-derive-admin-true",
|
||||
);
|
||||
await runMessageActionRequest(
|
||||
@@ -1031,9 +1027,7 @@ describe("gateway send mirroring", () => {
|
||||
// Full operator forwarding a non-owner sender: wire false → false
|
||||
// (admin scope does not inflate ownership on its own).
|
||||
setActivePluginRegistry(
|
||||
createTestRegistry([
|
||||
{ pluginId: "whatsapp", source: "test", plugin: reactPlugin },
|
||||
]),
|
||||
createTestRegistry([{ pluginId: "whatsapp", source: "test", plugin: reactPlugin }]),
|
||||
"send-test-owner-derive-admin-false",
|
||||
);
|
||||
await runMessageActionRequest(
|
||||
|
||||
@@ -26,6 +26,7 @@ import {
|
||||
normalizeOptionalString,
|
||||
readStringValue,
|
||||
} from "../../shared/string-coerce.js";
|
||||
import { ADMIN_SCOPE } from "../method-scopes.js";
|
||||
import {
|
||||
ErrorCodes,
|
||||
errorShape,
|
||||
@@ -34,7 +35,6 @@ import {
|
||||
validatePollParams,
|
||||
validateSendParams,
|
||||
} from "../protocol/index.js";
|
||||
import { ADMIN_SCOPE } from "../method-scopes.js";
|
||||
import { formatForLog } from "../ws-log.js";
|
||||
import type { GatewayRequestContext, GatewayRequestHandlers } from "./types.js";
|
||||
|
||||
@@ -231,8 +231,7 @@ export const sendHandlers: GatewayRequestHandlers = {
|
||||
// from unlocking owner-only channel actions by setting
|
||||
// `senderIsOwner: true` on the request.
|
||||
const callerScopes = client?.connect?.scopes ?? [];
|
||||
const callerIsFullOperator =
|
||||
Array.isArray(callerScopes) && callerScopes.includes(ADMIN_SCOPE);
|
||||
const callerIsFullOperator = Array.isArray(callerScopes) && callerScopes.includes(ADMIN_SCOPE);
|
||||
const senderIsOwner = callerIsFullOperator && request.senderIsOwner === true;
|
||||
const idem = request.idempotencyKey;
|
||||
const dedupeKey = `message.action:${idem}`;
|
||||
|
||||
@@ -9,14 +9,8 @@ describe("sanitizeExecApprovalDisplayText", () => {
|
||||
["echo hi\u200Bthere", "echo hi\\u{200B}there"],
|
||||
["date\u3164\uFFA0\u115F\u1160가", "date\\u{3164}\\u{FFA0}\\u{115F}\\u{1160}가"],
|
||||
["echo safe\n\rcurl https://example.test", "echo safe\\u{A}\\u{D}curl https://example.test"],
|
||||
[
|
||||
"echo ok\u2028curl https://example.test",
|
||||
"echo ok\\u{2028}curl https://example.test",
|
||||
],
|
||||
[
|
||||
"echo ok\u2029curl https://example.test",
|
||||
"echo ok\\u{2029}curl https://example.test",
|
||||
],
|
||||
["echo ok\u2028curl https://example.test", "echo ok\\u{2028}curl https://example.test"],
|
||||
["echo ok\u2029curl https://example.test", "echo ok\\u{2029}curl https://example.test"],
|
||||
])("sanitizes exec approval display text for %j", (input, expected) => {
|
||||
expect(sanitizeExecApprovalDisplayText(input)).toBe(expected);
|
||||
});
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { listBundledChannelPlugins } from "../../../src/channels/plugins/bundled.js";
|
||||
import { normalizeChannelMeta } from "../../../src/channels/plugins/meta-normalization.js";
|
||||
import type { ChannelPlugin } from "../../../src/channels/plugins/types.js";
|
||||
|
||||
type PluginContractEntry = {
|
||||
@@ -11,10 +12,7 @@ export function getPluginContractRegistry(): PluginContractEntry[] {
|
||||
id: plugin.id,
|
||||
plugin: {
|
||||
...plugin,
|
||||
meta: {
|
||||
...plugin.meta,
|
||||
id: plugin.id,
|
||||
},
|
||||
meta: normalizeChannelMeta({ id: plugin.id, meta: plugin.meta }),
|
||||
},
|
||||
}));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user