mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-02 04:50:21 +00:00
test: fix typing and test fixture issues
This commit is contained in:
@@ -2,7 +2,6 @@ import fs from "node:fs/promises";
|
||||
import path from "node:path";
|
||||
import { describe, expect, it } from "vitest";
|
||||
import type { OpenClawConfig } from "../config/config.js";
|
||||
import type { ModelProviderConfig } from "../config/types.models.js";
|
||||
import { validateConfigObject } from "../config/validation.js";
|
||||
import { resolveOpenClawAgentDir } from "./agent-paths.js";
|
||||
import {
|
||||
@@ -42,7 +41,7 @@ async function writeAgentModelsJson(content: unknown): Promise<void> {
|
||||
}
|
||||
|
||||
function createMergeConfigProvider() {
|
||||
const provider: ModelProviderConfig = {
|
||||
return {
|
||||
baseUrl: "https://config.example/v1",
|
||||
apiKey: "CONFIG_KEY",
|
||||
api: "openai-responses",
|
||||
@@ -58,7 +57,6 @@ function createMergeConfigProvider() {
|
||||
},
|
||||
],
|
||||
};
|
||||
return provider;
|
||||
}
|
||||
|
||||
async function runCustomProviderMergeTest(seedProvider: {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import type { MockInstance } from "vitest";
|
||||
import { createTypingCallbacks } from "./typing.js";
|
||||
|
||||
const flushMicrotasks = async () => {
|
||||
@@ -16,10 +17,16 @@ async function withFakeTimers(run: () => Promise<void>) {
|
||||
}
|
||||
|
||||
function createTypingHarness(overrides: Partial<Parameters<typeof createTypingCallbacks>[0]> = {}) {
|
||||
const start = vi.fn(overrides.start ?? (async () => {}));
|
||||
const stop = vi.fn(overrides.stop ?? (async () => {}));
|
||||
const onStartError = vi.fn(overrides.onStartError ?? (() => {}));
|
||||
const onStopError = vi.fn(overrides.onStopError ?? (() => {}));
|
||||
const start = (overrides.start ?? vi.fn().mockResolvedValue(undefined)) as MockInstance<
|
||||
[],
|
||||
Promise<void>
|
||||
>;
|
||||
const stop = (overrides.stop ?? vi.fn().mockResolvedValue(undefined)) as MockInstance<
|
||||
[],
|
||||
Promise<void>
|
||||
>;
|
||||
const onStartError = (overrides.onStartError ?? vi.fn()) as MockInstance<[unknown], void>;
|
||||
const onStopError = (overrides.onStopError ?? vi.fn()) as MockInstance<[unknown], void>;
|
||||
const callbacks = createTypingCallbacks({
|
||||
start,
|
||||
stop,
|
||||
|
||||
@@ -30,17 +30,15 @@ export function patchChannelOnboardingAdapter(
|
||||
if (!adapter) {
|
||||
throw new Error(`missing onboarding adapter for ${channel}`);
|
||||
}
|
||||
const keys = Object.keys(patch);
|
||||
const adapterRecord = adapter as unknown as Record<string, unknown>;
|
||||
const patchRecord = patch as Record<string, unknown>;
|
||||
const previous = new Map<string, unknown>();
|
||||
const keys = Object.keys(patch) as Array<keyof ChannelOnboardingAdapter>;
|
||||
const previous: Partial<ChannelOnboardingAdapter> = {};
|
||||
for (const key of keys) {
|
||||
previous.set(key, adapterRecord[key]);
|
||||
adapterRecord[key] = patchRecord[key];
|
||||
previous[key] = adapter[key];
|
||||
adapter[key] = patch[key] as ChannelOnboardingAdapter[typeof key];
|
||||
}
|
||||
return () => {
|
||||
for (const key of keys) {
|
||||
adapterRecord[key] = previous.get(key);
|
||||
adapter[key] = previous[key] as ChannelOnboardingAdapter[typeof key];
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import type { ChannelOnboardingAdapter } from "../channels/plugins/onboarding-types.js";
|
||||
import type { OpenClawConfig } from "../config/config.js";
|
||||
import { createEmptyPluginRegistry } from "../plugins/registry.js";
|
||||
import { setActivePluginRegistry } from "../plugins/runtime.js";
|
||||
@@ -83,17 +82,16 @@ function createTelegramCfg(botToken: string, enabled?: boolean): OpenClawConfig
|
||||
} as OpenClawConfig;
|
||||
}
|
||||
|
||||
function patchTelegramAdapter(overrides: Partial<ChannelOnboardingAdapter>) {
|
||||
const getStatus =
|
||||
overrides.getStatus ??
|
||||
vi.fn(async ({ cfg }: { cfg: OpenClawConfig }) => ({
|
||||
channel: "telegram",
|
||||
configured: Boolean(cfg.channels?.telegram?.botToken),
|
||||
statusLines: [],
|
||||
}));
|
||||
function patchTelegramAdapter(overrides: Parameters<typeof patchChannelOnboardingAdapter>[1]) {
|
||||
return patchChannelOnboardingAdapter("telegram", {
|
||||
...overrides,
|
||||
getStatus,
|
||||
getStatus:
|
||||
overrides.getStatus ??
|
||||
vi.fn(async ({ cfg }: { cfg: OpenClawConfig }) => ({
|
||||
channel: "telegram",
|
||||
configured: Boolean(cfg.channels?.telegram?.botToken),
|
||||
statusLines: [],
|
||||
})),
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
import { afterEach, describe, expect, it, vi } from "vitest";
|
||||
import { CONTEXT_WINDOW_HARD_MIN_TOKENS } from "../agents/context-window-guard.js";
|
||||
import type { OpenClawConfig } from "../config/config.js";
|
||||
import type { ModelProviderConfig } from "../config/types.models.js";
|
||||
import { defaultRuntime } from "../runtime.js";
|
||||
import {
|
||||
applyCustomApiConfig,
|
||||
@@ -78,32 +76,31 @@ function expectOpenAiCompatResult(params: {
|
||||
expect(params.result.config.models?.providers?.custom?.api).toBe("openai-completions");
|
||||
}
|
||||
|
||||
function buildCustomProviderConfig(contextWindow?: number): OpenClawConfig {
|
||||
function buildCustomProviderConfig(contextWindow?: number) {
|
||||
if (contextWindow === undefined) {
|
||||
return {};
|
||||
return {} as OpenClawConfig;
|
||||
}
|
||||
const customProvider = {
|
||||
api: "openai-completions",
|
||||
baseUrl: "https://llm.example.com/v1",
|
||||
models: [
|
||||
{
|
||||
id: "foo-large",
|
||||
name: "foo-large",
|
||||
contextWindow,
|
||||
maxTokens: contextWindow > CONTEXT_WINDOW_HARD_MIN_TOKENS ? 4096 : 1024,
|
||||
input: ["text"],
|
||||
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
|
||||
reasoning: false,
|
||||
},
|
||||
],
|
||||
} satisfies ModelProviderConfig;
|
||||
return {
|
||||
models: {
|
||||
providers: {
|
||||
custom: customProvider,
|
||||
custom: {
|
||||
api: "openai-completions" as const,
|
||||
baseUrl: "https://llm.example.com/v1",
|
||||
models: [
|
||||
{
|
||||
id: "foo-large",
|
||||
name: "foo-large",
|
||||
contextWindow,
|
||||
maxTokens: contextWindow > CONTEXT_WINDOW_HARD_MIN_TOKENS ? 4096 : 1024,
|
||||
input: ["text"],
|
||||
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
|
||||
reasoning: false,
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
} as OpenClawConfig;
|
||||
}
|
||||
|
||||
function applyCustomModelConfigWithContextWindow(contextWindow?: number) {
|
||||
|
||||
@@ -81,7 +81,7 @@ describe("runCronIsolatedAgentTurn — cron model override (#21057)", () => {
|
||||
// Hold onto the cron session *object* — the code may reassign its
|
||||
// `sessionEntry` property (e.g. during skills snapshot refresh), so
|
||||
// checking a stale reference would give a false negative.
|
||||
let cronSession: { sessionEntry: ReturnType<typeof makeFreshSessionEntry>; [k: string]: unknown };
|
||||
let cronSession: ReturnType<typeof makeCronSession>;
|
||||
|
||||
beforeEach(() => {
|
||||
previousFastTestEnv = clearFastTestEnv();
|
||||
@@ -103,7 +103,7 @@ describe("runCronIsolatedAgentTurn — cron model override (#21057)", () => {
|
||||
|
||||
cronSession = makeCronSession({
|
||||
sessionEntry: makeFreshSessionEntry(),
|
||||
}) as { sessionEntry: ReturnType<typeof makeFreshSessionEntry>; [k: string]: unknown };
|
||||
});
|
||||
resolveCronSessionMock.mockReturnValue(cronSession);
|
||||
});
|
||||
|
||||
|
||||
@@ -4,11 +4,8 @@ import path from "node:path";
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import { saveExecApprovals } from "../infra/exec-approvals.js";
|
||||
import type { ExecHostResponse } from "../infra/exec-host.js";
|
||||
import {
|
||||
handleSystemRunInvoke,
|
||||
formatSystemRunAllowlistMissMessage,
|
||||
type HandleSystemRunInvokeOptions,
|
||||
} from "./invoke-system-run.js";
|
||||
import { handleSystemRunInvoke, formatSystemRunAllowlistMissMessage } from "./invoke-system-run.js";
|
||||
import type { HandleSystemRunInvokeOptions } from "./invoke-system-run.js";
|
||||
|
||||
describe("formatSystemRunAllowlistMissMessage", () => {
|
||||
it("returns legacy allowlist miss message by default", () => {
|
||||
@@ -150,23 +147,30 @@ describe("handleSystemRunInvoke mac app exec host routing", () => {
|
||||
security?: "full" | "allowlist";
|
||||
ask?: "off" | "on-miss" | "always";
|
||||
approved?: boolean;
|
||||
runCommand?: ReturnType<typeof vi.fn>;
|
||||
runViaMacAppExecHost?: ReturnType<typeof vi.fn>;
|
||||
sendInvokeResult?: ReturnType<typeof vi.fn>;
|
||||
sendExecFinishedEvent?: ReturnType<typeof vi.fn>;
|
||||
sendNodeEvent?: ReturnType<typeof vi.fn>;
|
||||
runCommand?: HandleSystemRunInvokeOptions["runCommand"];
|
||||
runViaMacAppExecHost?: HandleSystemRunInvokeOptions["runViaMacAppExecHost"];
|
||||
sendInvokeResult?: HandleSystemRunInvokeOptions["sendInvokeResult"];
|
||||
sendExecFinishedEvent?: HandleSystemRunInvokeOptions["sendExecFinishedEvent"];
|
||||
sendNodeEvent?: HandleSystemRunInvokeOptions["sendNodeEvent"];
|
||||
skillBinsCurrent?: () => Promise<Array<{ name: string; resolvedPath: string }>>;
|
||||
}) {
|
||||
const runCommand =
|
||||
params.runCommand ??
|
||||
vi.fn(async (_command: string[], _cwd?: string, _env?: Record<string, string>) =>
|
||||
(vi.fn(async (_command: string[], _cwd?: string, _env?: Record<string, string>) =>
|
||||
createLocalRunResult(),
|
||||
);
|
||||
) as HandleSystemRunInvokeOptions["runCommand"]);
|
||||
const runViaMacAppExecHost =
|
||||
params.runViaMacAppExecHost ?? vi.fn(async () => params.runViaResponse ?? null);
|
||||
const sendInvokeResult = params.sendInvokeResult ?? vi.fn(async () => {});
|
||||
const sendExecFinishedEvent = params.sendExecFinishedEvent ?? vi.fn(async () => {});
|
||||
const sendNodeEvent = params.sendNodeEvent ?? vi.fn(async () => {});
|
||||
params.runViaMacAppExecHost ??
|
||||
(vi.fn(async () => params.runViaResponse ?? null) as HandleSystemRunInvokeOptions["runViaMacAppExecHost"]);
|
||||
const sendInvokeResult =
|
||||
params.sendInvokeResult ??
|
||||
(vi.fn(async () => {}) as HandleSystemRunInvokeOptions["sendInvokeResult"]);
|
||||
const sendExecFinishedEvent =
|
||||
params.sendExecFinishedEvent ??
|
||||
(vi.fn(async () => {}) as HandleSystemRunInvokeOptions["sendExecFinishedEvent"]);
|
||||
const sendNodeEvent =
|
||||
params.sendNodeEvent ??
|
||||
(vi.fn(async () => {}) as HandleSystemRunInvokeOptions["sendNodeEvent"]);
|
||||
|
||||
await handleSystemRunInvoke({
|
||||
client: {} as never,
|
||||
@@ -185,14 +189,12 @@ describe("handleSystemRunInvoke mac app exec host routing", () => {
|
||||
resolveExecAsk: () => params.ask ?? "off",
|
||||
isCmdExeInvocation: () => false,
|
||||
sanitizeEnv: () => undefined,
|
||||
runCommand: runCommand as HandleSystemRunInvokeOptions["runCommand"],
|
||||
runViaMacAppExecHost:
|
||||
runViaMacAppExecHost as HandleSystemRunInvokeOptions["runViaMacAppExecHost"],
|
||||
sendNodeEvent: sendNodeEvent as HandleSystemRunInvokeOptions["sendNodeEvent"],
|
||||
runCommand,
|
||||
runViaMacAppExecHost,
|
||||
sendNodeEvent,
|
||||
buildExecEventPayload: (payload) => payload,
|
||||
sendInvokeResult: sendInvokeResult as HandleSystemRunInvokeOptions["sendInvokeResult"],
|
||||
sendExecFinishedEvent:
|
||||
sendExecFinishedEvent as HandleSystemRunInvokeOptions["sendExecFinishedEvent"],
|
||||
sendInvokeResult,
|
||||
sendExecFinishedEvent,
|
||||
preferMacAppExecHost: params.preferMacAppExecHost,
|
||||
});
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ import os from "node:os";
|
||||
import path from "node:path";
|
||||
import { afterEach, describe, expect, it, vi } from "vitest";
|
||||
import type { OpenClawConfig } from "../config/config.js";
|
||||
import type { ExecSecretProviderConfig, SecretProviderConfig } from "../config/types.secrets.js";
|
||||
import type { SecretProviderConfig } from "../config/types.secrets.js";
|
||||
import { resolveSecretRefString, resolveSecretRefValue } from "./resolve.js";
|
||||
|
||||
async function writeSecureFile(filePath: string, content: string, mode = 0o600): Promise<void> {
|
||||
@@ -52,14 +52,14 @@ describe("secret ref resolver", () => {
|
||||
|
||||
function createExecProvider(
|
||||
command: string,
|
||||
overrides?: Partial<ExecSecretProviderConfig>,
|
||||
): ExecSecretProviderConfig {
|
||||
overrides?: Record<string, unknown>,
|
||||
): SecretProviderConfig {
|
||||
return {
|
||||
source: "exec",
|
||||
command,
|
||||
passEnv: ["PATH"],
|
||||
...overrides,
|
||||
};
|
||||
} as SecretProviderConfig;
|
||||
}
|
||||
|
||||
async function expectExecResolveRejects(
|
||||
|
||||
@@ -113,7 +113,6 @@ describe("registerSlackMemberEvents", () => {
|
||||
calls: 0,
|
||||
},
|
||||
];
|
||||
|
||||
it.each(cases)("$name", async ({ args, calls }) => {
|
||||
await runMemberCase(args);
|
||||
expect(memberMocks.enqueue).toHaveBeenCalledTimes(calls);
|
||||
|
||||
@@ -131,7 +131,6 @@ describe("registerSlackMessageEvents", () => {
|
||||
calls: 0,
|
||||
},
|
||||
];
|
||||
|
||||
it.each(cases)("$name", async ({ input, calls }) => {
|
||||
await runMessageCase(input);
|
||||
expect(messageQueueMock).toHaveBeenCalledTimes(calls);
|
||||
|
||||
@@ -76,30 +76,16 @@ async function runPinCase(input: PinCase = {}): Promise<void> {
|
||||
|
||||
describe("registerSlackPinEvents", () => {
|
||||
const cases: Array<{ name: string; args: PinCase; expectedCalls: number }> = [
|
||||
{
|
||||
name: "enqueues DM pin system events when dmPolicy is open",
|
||||
args: { overrides: { dmPolicy: "open" } },
|
||||
expectedCalls: 1,
|
||||
},
|
||||
{
|
||||
name: "blocks DM pin system events when dmPolicy is disabled",
|
||||
args: { overrides: { dmPolicy: "disabled" } },
|
||||
expectedCalls: 0,
|
||||
},
|
||||
{ name: "enqueues DM pin system events when dmPolicy is open", args: { overrides: { dmPolicy: "open" } }, expectedCalls: 1 },
|
||||
{ name: "blocks DM pin system events when dmPolicy is disabled", args: { overrides: { dmPolicy: "disabled" } }, expectedCalls: 0 },
|
||||
{
|
||||
name: "blocks DM pin system events for unauthorized senders in allowlist mode",
|
||||
args: {
|
||||
overrides: { dmPolicy: "allowlist", allowFrom: ["U2"] },
|
||||
event: makePinEvent({ user: "U1" }),
|
||||
},
|
||||
args: { overrides: { dmPolicy: "allowlist", allowFrom: ["U2"] }, event: makePinEvent({ user: "U1" }) },
|
||||
expectedCalls: 0,
|
||||
},
|
||||
{
|
||||
name: "allows DM pin system events for authorized senders in allowlist mode",
|
||||
args: {
|
||||
overrides: { dmPolicy: "allowlist", allowFrom: ["U1"] },
|
||||
event: makePinEvent({ user: "U1" }),
|
||||
},
|
||||
args: { overrides: { dmPolicy: "allowlist", allowFrom: ["U1"] }, event: makePinEvent({ user: "U1" }) },
|
||||
expectedCalls: 1,
|
||||
},
|
||||
{
|
||||
@@ -115,7 +101,6 @@ describe("registerSlackPinEvents", () => {
|
||||
expectedCalls: 0,
|
||||
},
|
||||
];
|
||||
|
||||
it.each(cases)("$name", async ({ args, expectedCalls }) => {
|
||||
await runPinCase(args);
|
||||
expect(pinEnqueueMock).toHaveBeenCalledTimes(expectedCalls);
|
||||
|
||||
@@ -78,20 +78,20 @@ async function executeReactionCase(input: ReactionRunInput = {}) {
|
||||
}
|
||||
|
||||
describe("registerSlackReactionEvents", () => {
|
||||
const cases: Array<{ name: string; args: ReactionRunInput; expectedCalls: number }> = [
|
||||
const cases: Array<{ name: string; input: ReactionRunInput; expectedCalls: number }> = [
|
||||
{
|
||||
name: "enqueues DM reaction system events when dmPolicy is open",
|
||||
args: { overrides: { dmPolicy: "open" } },
|
||||
input: { overrides: { dmPolicy: "open" } },
|
||||
expectedCalls: 1,
|
||||
},
|
||||
{
|
||||
name: "blocks DM reaction system events when dmPolicy is disabled",
|
||||
args: { overrides: { dmPolicy: "disabled" } },
|
||||
input: { overrides: { dmPolicy: "disabled" } },
|
||||
expectedCalls: 0,
|
||||
},
|
||||
{
|
||||
name: "blocks DM reaction system events for unauthorized senders in allowlist mode",
|
||||
args: {
|
||||
input: {
|
||||
overrides: { dmPolicy: "allowlist", allowFrom: ["U2"] },
|
||||
event: buildReactionEvent({ user: "U1" }),
|
||||
},
|
||||
@@ -99,7 +99,7 @@ describe("registerSlackReactionEvents", () => {
|
||||
},
|
||||
{
|
||||
name: "allows DM reaction system events for authorized senders in allowlist mode",
|
||||
args: {
|
||||
input: {
|
||||
overrides: { dmPolicy: "allowlist", allowFrom: ["U1"] },
|
||||
event: buildReactionEvent({ user: "U1" }),
|
||||
},
|
||||
@@ -107,8 +107,8 @@ describe("registerSlackReactionEvents", () => {
|
||||
},
|
||||
{
|
||||
name: "enqueues channel reaction events regardless of dmPolicy",
|
||||
args: {
|
||||
handler: "removed" as const,
|
||||
input: {
|
||||
handler: "removed",
|
||||
overrides: { dmPolicy: "disabled", channelType: "channel" },
|
||||
event: {
|
||||
...buildReactionEvent({ channel: "C1" }),
|
||||
@@ -119,7 +119,7 @@ describe("registerSlackReactionEvents", () => {
|
||||
},
|
||||
{
|
||||
name: "blocks channel reaction events for users outside channel users allowlist",
|
||||
args: {
|
||||
input: {
|
||||
overrides: {
|
||||
dmPolicy: "open",
|
||||
channelType: "channel",
|
||||
@@ -131,8 +131,8 @@ describe("registerSlackReactionEvents", () => {
|
||||
},
|
||||
];
|
||||
|
||||
it.each(cases)("$name", async ({ args, expectedCalls }) => {
|
||||
await executeReactionCase(args);
|
||||
it.each(cases)("$name", async ({ input, expectedCalls }) => {
|
||||
await executeReactionCase(input);
|
||||
expect(reactionQueueMock).toHaveBeenCalledTimes(expectedCalls);
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user