chore: Fix types in tests 41/N.

This commit is contained in:
cpojer
2026-02-17 15:48:16 +09:00
parent 3dc8d5656d
commit 6264c5e842
17 changed files with 81 additions and 43 deletions

View File

@@ -70,8 +70,11 @@ function mockGatewaySuccessReply(text = "hello") {
function mockLocalAgentReply(text = "local") {
vi.mocked(agentCommand).mockImplementationOnce(async (_opts, rt) => {
rt.log?.(text);
return { payloads: [{ text }], meta: { stub: true } };
rt?.log?.(text);
return {
payloads: [{ text }],
meta: { durationMs: 1, agentMeta: { sessionId: "s", provider: "p", model: "m" } },
} as unknown as Awaited<ReturnType<typeof agentCommand>>;
});
}

View File

@@ -40,7 +40,7 @@ describe("deliverAgentCommandResult", () => {
function createResult(text = "hi") {
return {
payloads: [{ text }],
meta: {},
meta: { durationMs: 1 },
};
}
@@ -207,7 +207,7 @@ describe("deliverAgentCommandResult", () => {
});
expect(runtime.log).toHaveBeenCalledTimes(1);
const line = String(runtime.log.mock.calls[0]?.[0]);
const line = String((runtime.log as ReturnType<typeof vi.fn>).mock.calls[0]?.[0]);
expect(line).toContain("[agent:nested]");
expect(line).toContain("session=agent:main:main");
expect(line).toContain("run=run-announce");

View File

@@ -171,7 +171,7 @@ describe("resolveSessionKeyForRequest", () => {
// loadSessionStore should be called twice: once for main, once for mybot
// (not twice for main)
const storePaths = mocks.loadSessionStore.mock.calls.map((call: [string]) => call[0]);
const storePaths = mocks.loadSessionStore.mock.calls.map((call) => String(call[0]));
expect(storePaths).toHaveLength(2);
expect(storePaths).toContain(MAIN_STORE_PATH);
expect(storePaths).toContain(MYBOT_STORE_PATH);

View File

@@ -366,7 +366,7 @@ describe("applyAuthChoice", () => {
if (previousIsTTYDescriptor) {
Object.defineProperty(stdin, "isTTY", previousIsTTYDescriptor);
} else if (!hadOwnIsTTY) {
delete stdin.isTTY;
delete (stdin as { isTTY?: boolean }).isTTY;
}
}
});
@@ -653,7 +653,8 @@ describe("applyAuthChoice", () => {
const runtime = createExitThrowingRuntime();
const text: WizardPrompter["text"] = vi.fn(async (params) => {
if (params.message === "Paste the redirect URL") {
const lastLog = runtime.log.mock.calls.at(-1)?.[0];
const runtimeLog = runtime.log as ReturnType<typeof vi.fn>;
const lastLog = runtimeLog.mock.calls.at(-1)?.[0];
const urlLine = typeof lastLog === "string" ? lastLog : String(lastLog ?? "");
const urlMatch = urlLine.match(/https?:\/\/\S+/)?.[0] ?? "";
const state = urlMatch ? new URL(urlMatch).searchParams.get("state") : null;
@@ -734,7 +735,7 @@ describe("applyAuthChoice", () => {
},
],
},
]);
] as never);
const prompter = createPrompter({});
const runtime = createExitThrowingRuntime();
@@ -806,7 +807,7 @@ describe("applyAuthChoice", () => {
},
],
},
]);
] as never);
const prompter = createPrompter({
select: vi.fn(async () => "oauth" as never) as WizardPrompter["select"],

View File

@@ -26,8 +26,12 @@ vi.mock("../../slack/scopes.js", () => ({
}));
const runtime = {
log: (value: string) => logs.push(value),
error: (value: string) => errors.push(value),
log: (...args: unknown[]) => {
logs.push(args.map(String).join(" "));
},
error: (...args: unknown[]) => {
errors.push(args.map(String).join(" "));
},
exit: (code: number) => {
throw new Error(`exit:${code}`);
},

View File

@@ -2,6 +2,7 @@ import fs from "node:fs";
import os from "node:os";
import path from "node:path";
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import type { OpenClawConfig } from "../config/config.js";
import { maybeRemoveDeprecatedCliAuthProfiles } from "./doctor-auth.js";
import type { DoctorPrompter } from "./doctor-prompter.js";
@@ -93,7 +94,10 @@ describe("maybeRemoveDeprecatedCliAuthProfiles", () => {
},
} as const;
const next = await maybeRemoveDeprecatedCliAuthProfiles(cfg, makePrompter(true));
const next = await maybeRemoveDeprecatedCliAuthProfiles(
cfg as unknown as OpenClawConfig,
makePrompter(true),
);
const raw = JSON.parse(fs.readFileSync(authPath, "utf8")) as {
profiles?: Record<string, unknown>;

View File

@@ -1,4 +1,4 @@
import { describe, expect, it } from "vitest";
import { describe, expect, it, vi } from "vitest";
import {
arrangeLegacyStateMigrationTest,
confirm,
@@ -15,7 +15,10 @@ describe("doctor command", () => {
const { doctorCommand, runtime, runLegacyStateMigrations } =
await arrangeLegacyStateMigrationTest();
await doctorCommand(runtime, { yes: true });
await (doctorCommand as (runtime: unknown, opts: Record<string, unknown>) => Promise<void>)(
runtime,
{ yes: true },
);
expect(runLegacyStateMigrations).toHaveBeenCalledTimes(1);
expect(confirm).not.toHaveBeenCalled();
@@ -25,7 +28,10 @@ describe("doctor command", () => {
const { doctorCommand, runtime, runLegacyStateMigrations } =
await arrangeLegacyStateMigrationTest();
await doctorCommand(runtime, { nonInteractive: true });
await (doctorCommand as (runtime: unknown, opts: Record<string, unknown>) => Promise<void>)(
runtime,
{ nonInteractive: true },
);
expect(runLegacyStateMigrations).toHaveBeenCalledTimes(1);
expect(confirm).not.toHaveBeenCalled();
@@ -35,7 +41,7 @@ describe("doctor command", () => {
mockDoctorConfigSnapshot();
const { healthCommand } = await import("./health.js");
healthCommand.mockRejectedValueOnce(new Error("gateway closed"));
vi.mocked(healthCommand).mockRejectedValueOnce(new Error("gateway closed"));
serviceIsLoaded.mockResolvedValueOnce(true);
serviceRestart.mockClear();

View File

@@ -10,7 +10,11 @@ const runtime = {
exit: vi.fn(),
};
const defaultSessions = { path: "/tmp/sessions.json", count: 0, recent: [] };
const defaultSessions: HealthSummary["sessions"] = {
path: "/tmp/sessions.json",
count: 0,
recent: [],
};
const createMainAgentSummary = (sessions = defaultSessions) => ({
agentId: "main",

View File

@@ -117,6 +117,10 @@ const createStubPlugin = (params: {
outbound: params.outbound,
});
type ChannelActionParams = Parameters<
NonNullable<NonNullable<ChannelPlugin["actions"]>["handleAction"]>
>[0];
const createDiscordPollPluginRegistration = () => ({
pluginId: "discord",
source: "test",
@@ -125,11 +129,12 @@ const createDiscordPollPluginRegistration = () => ({
label: "Discord",
actions: {
listActions: () => ["poll"],
handleAction: async ({ action, params, cfg, accountId }) =>
await handleDiscordAction(
handleAction: (async ({ action, params, cfg, accountId }: ChannelActionParams) => {
return await handleDiscordAction(
{ action, to: params.to, accountId: accountId ?? undefined },
cfg,
),
);
}) as unknown as NonNullable<ChannelPlugin["actions"]>["handleAction"],
},
}),
});
@@ -142,11 +147,12 @@ const createTelegramSendPluginRegistration = () => ({
label: "Telegram",
actions: {
listActions: () => ["send"],
handleAction: async ({ action, params, cfg, accountId }) =>
await handleTelegramAction(
handleAction: (async ({ action, params, cfg, accountId }: ChannelActionParams) => {
return await handleTelegramAction(
{ action, to: params.to, accountId: accountId ?? undefined },
cfg,
),
);
}) as unknown as NonNullable<ChannelPlugin["actions"]>["handleAction"],
},
}),
});

View File

@@ -24,7 +24,7 @@ const modelRegistryState = {
getAllError: undefined as unknown,
getAvailableError: undefined as unknown,
};
let previousExitCode: number | undefined;
let previousExitCode: typeof process.exitCode;
vi.mock("../config/config.js", () => ({
CONFIG_PATH: "/tmp/openclaw.json",
@@ -480,7 +480,10 @@ describe("models list/status", () => {
const { toModelRow } = await import("./models/list.registry.js");
const row = toModelRow({
model: makeGoogleAntigravityTemplate("claude-opus-4-6-thinking", "Claude Opus 4.6 Thinking"),
model: makeGoogleAntigravityTemplate(
"claude-opus-4-6-thinking",
"Claude Opus 4.6 Thinking",
) as never,
key: "google-antigravity/claude-opus-4-6-thinking",
tags: [],
availableKeys: undefined,

View File

@@ -23,9 +23,9 @@ describe("runInteractiveOnboarding", () => {
createClackPrompterMock.mockReset();
runOnboardingWizardMock.mockReset();
restoreTerminalStateMock.mockReset();
runtime.log.mockClear();
runtime.error.mockClear();
runtime.exit.mockClear();
(runtime.log as ReturnType<typeof vi.fn>).mockClear();
(runtime.error as ReturnType<typeof vi.fn>).mockClear();
(runtime.exit as ReturnType<typeof vi.fn>).mockClear();
createClackPrompterMock.mockReturnValue({});
runOnboardingWizardMock.mockResolvedValue(undefined);

View File

@@ -1,6 +1,7 @@
import fs from "node:fs/promises";
import path from "node:path";
import { afterAll, beforeAll, describe, expect, it, vi } from "vitest";
import type { GatewayAuthConfig } from "../config/config.js";
import { makeTempWorkspace } from "../test-helpers/workspace.js";
import { getFreePortBlockWithPermissionFallback } from "../test-utils/ports.js";
import {
@@ -61,7 +62,7 @@ async function getFreeGatewayPort(): Promise<number> {
const runtime = createThrowingRuntime();
async function expectGatewayTokenAuth(params: {
authConfig: unknown;
authConfig: GatewayAuthConfig | null | undefined;
token: string;
env: NodeJS.ProcessEnv;
}) {
@@ -161,7 +162,7 @@ describe("onboard (non-interactive): gateway and remote auth", () => {
const { resolveConfigPath } = await import("../config/paths.js");
const configPath = resolveConfigPath(process.env, stateDir);
const cfg = await readJsonFile<{
gateway?: { auth?: { mode?: string; token?: string } };
gateway?: { auth?: GatewayAuthConfig };
agents?: { defaults?: { workspace?: string } };
}>(configPath);
@@ -245,7 +246,7 @@ describe("onboard (non-interactive): gateway and remote auth", () => {
gateway?: {
bind?: string;
port?: number;
auth?: { mode?: string; token?: string };
auth?: GatewayAuthConfig;
};
}>(configPath);

View File

@@ -85,7 +85,7 @@ function mockMissingBrewStatus(skills: Array<ReturnType<typeof createBundledSkil
workspaceDir: "/tmp/ws",
managedSkillsDir: "/tmp/managed",
skills,
});
} as never);
}
function createPrompter(params: {
@@ -104,8 +104,10 @@ function createPrompter(params: {
note: vi.fn(async (message: string, title?: string) => {
notes.push({ title, message });
}),
select: vi.fn(async () => "npm"),
multiselect: vi.fn(async () => params.multiselect ?? ["__skip__"]),
select: vi.fn(async () => "npm") as unknown as WizardPrompter["select"],
multiselect: vi.fn(
async () => params.multiselect ?? ["__skip__"],
) as unknown as WizardPrompter["multiselect"],
text: vi.fn(async () => ""),
confirm: vi.fn(async ({ message }) => {
if (message === "Show Homebrew install command?") {

View File

@@ -52,8 +52,8 @@ function mockRepoLocalPathExists() {
async function runInitialValueForChannel(channel: "dev" | "beta") {
const runtime = makeRuntime();
const select = vi.fn(async () => "skip") as WizardPrompter["select"];
const prompter = makePrompter({ select });
const select = vi.fn((async <T extends string>() => "skip" as T) as WizardPrompter["select"]);
const prompter = makePrompter({ select: select as unknown as WizardPrompter["select"] });
const cfg: OpenClawConfig = { update: { channel } };
mockRepoLocalPathExists();
@@ -64,7 +64,8 @@ async function runInitialValueForChannel(channel: "dev" | "beta") {
runtime,
});
return select.mock.calls[0]?.[0]?.initialValue;
const call = select.mock.calls[0];
return call?.[0]?.initialValue;
}
function expectPluginLoadedFromLocalPath(

View File

@@ -25,8 +25,8 @@ function makePrompter(): WizardPrompter {
intro: async () => {},
outro: async () => {},
note: async () => {},
select: async () => "",
multiselect: async () => [],
select: (async <T>() => "" as T) as WizardPrompter["select"],
multiselect: (async <T>() => [] as T[]) as WizardPrompter["multiselect"],
text: async () => "",
confirm: async () => false,
progress: () => ({ update: () => {}, stop: () => {} }),
@@ -161,9 +161,9 @@ describe("applyOpenAIConfig", () => {
it("overrides model.primary when model object already exists", () => {
const next = applyOpenAIConfig({
agents: { defaults: { model: { primary: "anthropic/claude-opus-4-6", fallback: [] } } },
agents: { defaults: { model: { primary: "anthropic/claude-opus-4-6", fallbacks: [] } } },
});
expect(next.agents?.defaults?.model).toEqual({ primary: OPENAI_DEFAULT_MODEL, fallback: [] });
expect(next.agents?.defaults?.model).toEqual({ primary: OPENAI_DEFAULT_MODEL, fallbacks: [] });
});
});

View File

@@ -205,7 +205,7 @@ describe("sandboxRecreateCommand", () => {
mocks.listSandboxContainers.mockResolvedValue([match, noMatch]);
await sandboxRecreateCommand(
{ session: "target-session", browser: false, force: true },
{ session: "target-session", all: false, browser: false, force: true },
runtime as never,
);
@@ -220,7 +220,7 @@ describe("sandboxRecreateCommand", () => {
mocks.listSandboxContainers.mockResolvedValue([agent, agentSub, other]);
await sandboxRecreateCommand(
{ agent: "work", browser: false, force: true },
{ agent: "work", all: false, browser: false, force: true },
runtime as never,
);

View File

@@ -17,6 +17,7 @@ function makeMattermostPlugin(): ChannelPlugin {
docsPath: "/channels/mattermost",
blurb: "test",
},
capabilities: { chatTypes: ["direct"] },
config: {
listAccountIds: () => ["echo"],
defaultAccountId: () => "echo",
@@ -45,6 +46,7 @@ function makeSlackPlugin(params?: { botToken?: string; appToken?: string }): Cha
docsPath: "/channels/slack",
blurb: "test",
},
capabilities: { chatTypes: ["direct"] },
config: {
listAccountIds: () => ["primary"],
defaultAccountId: () => "primary",
@@ -73,6 +75,7 @@ function makeTokenPlugin(): ChannelPlugin {
docsPath: "/channels/token-only",
blurb: "test",
},
capabilities: { chatTypes: ["direct"] },
config: {
listAccountIds: () => ["primary"],
defaultAccountId: () => "primary",