chore: Fix types in tests 38/N.

This commit is contained in:
cpojer
2026-02-17 15:47:23 +09:00
parent 238718c1d8
commit 084e39b519
19 changed files with 85 additions and 40 deletions

View File

@@ -7,10 +7,15 @@ function createLimiterSpy(): AuthRateLimiter & {
recordFailure: ReturnType<typeof vi.fn>; recordFailure: ReturnType<typeof vi.fn>;
reset: ReturnType<typeof vi.fn>; reset: ReturnType<typeof vi.fn>;
} { } {
const check = vi.fn<AuthRateLimiter["check"]>(
(_ip, _scope) => ({ allowed: true, remaining: 10, retryAfterMs: 0 }) as const,
);
const recordFailure = vi.fn<AuthRateLimiter["recordFailure"]>((_ip, _scope) => {});
const reset = vi.fn<AuthRateLimiter["reset"]>((_ip, _scope) => {});
return { return {
check: vi.fn(() => ({ allowed: true, remaining: 10, retryAfterMs: 0 })), check,
recordFailure: vi.fn(), recordFailure,
reset: vi.fn(), reset,
size: () => 0, size: () => 0,
prune: () => {}, prune: () => {},
dispose: () => {}, dispose: () => {},

View File

@@ -2,6 +2,7 @@ import fs from "node:fs/promises";
import os from "node:os"; import os from "node:os";
import path from "node:path"; import path from "node:path";
import { beforeEach, describe, expect, it, vi } from "vitest"; import { beforeEach, describe, expect, it, vi } from "vitest";
import type { SessionScope } from "../config/sessions/types.js";
const agentCommand = vi.fn(); const agentCommand = vi.fn();
@@ -14,7 +15,12 @@ const { resolveStorePath } = await import("../config/sessions/paths.js");
const { loadSessionStore, saveSessionStore } = await import("../config/sessions/store.js"); const { loadSessionStore, saveSessionStore } = await import("../config/sessions/store.js");
describe("runBootOnce", () => { describe("runBootOnce", () => {
const resolveMainStore = (cfg: { session?: { store?: string } } = {}) => { const resolveMainStore = (
cfg: {
session?: { store?: string; scope?: SessionScope; mainKey?: string };
agents?: { list?: Array<{ id?: string; default?: boolean }> };
} = {},
) => {
const sessionKey = resolveMainSessionKey(cfg); const sessionKey = resolveMainSessionKey(cfg);
const agentId = resolveAgentIdFromSessionKey(sessionKey); const agentId = resolveAgentIdFromSessionKey(sessionKey);
const storePath = resolveStorePath(cfg.session?.store, { agentId }); const storePath = resolveStorePath(cfg.session?.store, { agentId });

View File

@@ -7,6 +7,7 @@ import { parseModelRef } from "../agents/model-selection.js";
import { loadConfig } from "../config/config.js"; import { loadConfig } from "../config/config.js";
import { isTruthyEnvValue } from "../infra/env.js"; import { isTruthyEnvValue } from "../infra/env.js";
import { getFreePortBlockWithPermissionFallback } from "../test-utils/ports.js"; import { getFreePortBlockWithPermissionFallback } from "../test-utils/ports.js";
import { GATEWAY_CLIENT_NAMES } from "../utils/message-channel.js";
import { GatewayClient } from "./client.js"; import { GatewayClient } from "./client.js";
import { renderCatNoncePngBase64 } from "./live-image-probe.js"; import { renderCatNoncePngBase64 } from "./live-image-probe.js";
import { startGatewayServer } from "./server.js"; import { startGatewayServer } from "./server.js";
@@ -144,7 +145,7 @@ async function connectClient(params: { url: string; token: string }) {
const client = new GatewayClient({ const client = new GatewayClient({
url: params.url, url: params.url,
token: params.token, token: params.token,
clientName: "vitest-live-cli-backend", clientName: GATEWAY_CLIENT_NAMES.TEST,
clientVersion: "dev", clientVersion: "dev",
mode: "test", mode: "test",
onHelloOk: () => stop(undefined, client), onHelloOk: () => stop(undefined, client),

View File

@@ -36,11 +36,10 @@ describe("GatewayClient", () => {
wsMockState.last = null; wsMockState.last = null;
const client = new GatewayClient({ url: "ws://127.0.0.1:1" }); const client = new GatewayClient({ url: "ws://127.0.0.1:1" });
client.start(); client.start();
const last = wsMockState.last as { url: unknown; opts: unknown } | null;
expect(wsMockState.last?.url).toBe("ws://127.0.0.1:1"); expect(last?.url).toBe("ws://127.0.0.1:1");
expect(wsMockState.last?.opts).toEqual( expect(last?.opts).toEqual(expect.objectContaining({ maxPayload: 25 * 1024 * 1024 }));
expect.objectContaining({ maxPayload: 25 * 1024 * 1024 }),
);
}); });
}); });
@@ -153,7 +152,8 @@ describe("late-arriving invoke results", () => {
context, context,
}); });
const [ok, payload, error] = respond.mock.lastCall ?? []; const [ok, rawPayload, error] = respond.mock.lastCall ?? [];
const payload = rawPayload as { ok?: boolean; ignored?: boolean } | undefined;
// Late-arriving results return success instead of error to reduce log noise. // Late-arriving results return success instead of error to reduce log noise.
expect(ok).toBe(true); expect(ok).toBe(true);

View File

@@ -22,7 +22,7 @@ import { getApiKeyForModel } from "../agents/model-auth.js";
import { ensureOpenClawModelsJson } from "../agents/models-config.js"; import { ensureOpenClawModelsJson } from "../agents/models-config.js";
import { discoverAuthStorage, discoverModels } from "../agents/pi-model-discovery.js"; import { discoverAuthStorage, discoverModels } from "../agents/pi-model-discovery.js";
import { loadConfig } from "../config/config.js"; import { loadConfig } from "../config/config.js";
import type { OpenClawConfig, ModelProviderConfig } from "../config/types.js"; import type { ModelsConfig, OpenClawConfig, ModelProviderConfig } from "../config/types.js";
import { isTruthyEnvValue } from "../infra/env.js"; import { isTruthyEnvValue } from "../infra/env.js";
import { DEFAULT_AGENT_ID } from "../routing/session-key.js"; import { DEFAULT_AGENT_ID } from "../routing/session-key.js";
import { GATEWAY_CLIENT_MODES, GATEWAY_CLIENT_NAMES } from "../utils/message-channel.js"; import { GATEWAY_CLIENT_MODES, GATEWAY_CLIENT_NAMES } from "../utils/message-channel.js";
@@ -401,6 +401,7 @@ function buildLiveGatewayConfig(params: {
...providerOverrides, ...providerOverrides,
}; };
const providers = Object.keys(nextProviders).length > 0 ? nextProviders : baseProviders; const providers = Object.keys(nextProviders).length > 0 ? nextProviders : baseProviders;
const baseModels = params.cfg.models;
return { return {
...params.cfg, ...params.cfg,
agents: { agents: {
@@ -418,7 +419,9 @@ function buildLiveGatewayConfig(params: {
}, },
}, },
models: models:
Object.keys(providers).length > 0 ? { ...params.cfg.models, providers } : params.cfg.models, Object.keys(providers).length > 0
? ({ ...baseModels, providers } as ModelsConfig)
: baseModels,
}; };
} }
@@ -1149,10 +1152,10 @@ describeLive("gateway live (dev agent, profile keys)", () => {
await fs.writeFile(toolProbePath, `nonceA=${nonceA}\nnonceB=${nonceB}\n`); await fs.writeFile(toolProbePath, `nonceA=${nonceA}\nnonceB=${nonceB}\n`);
const port = await getFreeGatewayPort(); const port = await getFreeGatewayPort();
const server = await startGatewayServer({ const server = await startGatewayServer(port, {
configPath: cfg.__meta?.path, bind: "loopback",
port, auth: { mode: "token", token },
token, controlUiEnabled: false,
}); });
const client = await connectClient({ const client = await connectClient({

View File

@@ -109,7 +109,7 @@ describe("hooks mapping", () => {
], ],
}); });
expect(result?.ok).toBe(true); expect(result?.ok).toBe(true);
if (result?.ok && result.action.kind === "agent") { if (result?.ok && result.action && result.action.kind === "agent") {
expect(result.action.model).toBe("openai/gpt-4.1-mini"); expect(result.action.model).toBe("openai/gpt-4.1-mini");
} }
}); });

View File

@@ -126,7 +126,8 @@ function createErrnoError(code: string) {
} }
function mockWorkspaceStateRead(params: { onboardingCompletedAt?: string; errorCode?: string }) { function mockWorkspaceStateRead(params: { onboardingCompletedAt?: string; errorCode?: string }) {
mocks.fsReadFile.mockImplementation(async (filePath: string | URL | number) => { mocks.fsReadFile.mockImplementation(async (...args: unknown[]) => {
const filePath = args[0];
if (String(filePath).endsWith("workspace-state.json")) { if (String(filePath).endsWith("workspace-state.json")) {
if (params.errorCode) { if (params.errorCode) {
throw createErrnoError(params.errorCode); throw createErrnoError(params.errorCode);

View File

@@ -107,6 +107,9 @@ describe("chat abort transcript persistence", () => {
params: { sessionKey: "main", runId }, params: { sessionKey: "main", runId },
respond, respond,
context: context as never, context: context as never,
req: {} as never,
client: null,
isWebchatConnect: () => false,
}); });
const [ok1, payload1] = respond.mock.calls.at(-1) ?? []; const [ok1, payload1] = respond.mock.calls.at(-1) ?? [];
@@ -121,6 +124,9 @@ describe("chat abort transcript persistence", () => {
params: { sessionKey: "main", runId }, params: { sessionKey: "main", runId },
respond, respond,
context: context as never, context: context as never,
req: {} as never,
client: null,
isWebchatConnect: () => false,
}); });
const lines = await readTranscriptLines(transcriptPath); const lines = await readTranscriptLines(transcriptPath);
@@ -178,6 +184,9 @@ describe("chat abort transcript persistence", () => {
params: { sessionKey: "main" }, params: { sessionKey: "main" },
respond, respond,
context: context as never, context: context as never,
req: {} as never,
client: null,
isWebchatConnect: () => false,
}); });
const [ok, payload] = respond.mock.calls.at(-1) ?? []; const [ok, payload] = respond.mock.calls.at(-1) ?? [];
@@ -235,7 +244,9 @@ describe("chat abort transcript persistence", () => {
}, },
respond, respond,
context: context as never, context: context as never,
client: undefined, req: {} as never,
client: null,
isWebchatConnect: () => false,
}); });
const [ok, payload] = respond.mock.calls.at(-1) ?? []; const [ok, payload] = respond.mock.calls.at(-1) ?? [];

View File

@@ -26,7 +26,7 @@ describe("gateway chat.inject transcript writes", () => {
); );
vi.doMock("../session-utils.js", async (importOriginal) => { vi.doMock("../session-utils.js", async (importOriginal) => {
const original = await importOriginal(); const original = await importOriginal<typeof import("../session-utils.js")>();
return { return {
...original, ...original,
loadSessionEntry: () => ({ loadSessionEntry: () => ({
@@ -50,7 +50,10 @@ describe("gateway chat.inject transcript writes", () => {
await chatHandlers["chat.inject"]({ await chatHandlers["chat.inject"]({
params: { sessionKey: "k1", message: "hello" }, params: { sessionKey: "k1", message: "hello" },
respond, respond,
context, req: {} as never,
client: null as never,
isWebchatConnect: () => false,
context: context as unknown as GatewayRequestContext,
}); });
expect(respond).toHaveBeenCalled(); expect(respond).toHaveBeenCalled();

View File

@@ -28,6 +28,10 @@ describe("skills.update", () => {
skillKey: "brave-search", skillKey: "brave-search",
apiKey: "abc\r\ndef", apiKey: "abc\r\ndef",
}, },
req: {} as never,
client: null as never,
isWebchatConnect: () => false,
context: {} as never,
respond: (success, _result, err) => { respond: (success, _result, err) => {
ok = success; ok = success;
error = err; error = err;

View File

@@ -106,7 +106,9 @@ describe("sessions.usage", () => {
expect(respond).toHaveBeenCalledTimes(1); expect(respond).toHaveBeenCalledTimes(1);
expect(respond.mock.calls[0]?.[0]).toBe(true); expect(respond.mock.calls[0]?.[0]).toBe(true);
const result = respond.mock.calls[0]?.[1] as unknown as { sessions: Array<unknown> }; const result = respond.mock.calls[0]?.[1] as unknown as {
sessions: Array<{ key: string; agentId: string }>;
};
expect(result.sessions).toHaveLength(2); expect(result.sessions).toHaveLength(2);
// Sorted by most recent first (mtime=200 -> opus first). // Sorted by most recent first (mtime=200 -> opus first).

View File

@@ -1,4 +1,5 @@
import { beforeEach, describe, expect, it, vi } from "vitest"; import { beforeEach, describe, expect, it, vi } from "vitest";
import type { OpenClawConfig } from "../../config/config.js";
vi.mock("../../infra/session-cost-usage.js", async () => { vi.mock("../../infra/session-cost-usage.js", async () => {
const actual = await vi.importActual<typeof import("../../infra/session-cost-usage.js")>( const actual = await vi.importActual<typeof import("../../infra/session-cost-usage.js")>(
@@ -63,7 +64,7 @@ describe("gateway usage helpers", () => {
vi.useFakeTimers(); vi.useFakeTimers();
vi.setSystemTime(new Date("2026-02-05T00:00:00.000Z")); vi.setSystemTime(new Date("2026-02-05T00:00:00.000Z"));
const config = {} as unknown as ReturnType<import("../../config/config.js").loadConfig>; const config = {} as OpenClawConfig;
const a = await __test.loadCostUsageSummaryCached({ const a = await __test.loadCostUsageSummaryCached({
startMs: 1, startMs: 1,
endMs: 2, endMs: 2,

View File

@@ -15,6 +15,7 @@ const createRegistry = (diagnostics: PluginDiagnostic[]): PluginRegistry => ({
hooks: [], hooks: [],
typedHooks: [], typedHooks: [],
channels: [], channels: [],
commands: [],
providers: [], providers: [],
gatewayHandlers: {}, gatewayHandlers: {},
httpHandlers: [], httpHandlers: [],

View File

@@ -306,9 +306,14 @@ describe("gateway server agent", () => {
const ack = await ackP; const ack = await ackP;
const final = await finalP; const final = await finalP;
expect(ack.payload.runId).toBeDefined(); const ackPayload = ack.payload;
expect(final.payload.runId).toBe(ack.payload.runId); const finalPayload = final.payload;
expect(final.payload.status).toBe("ok"); if (!ackPayload || !finalPayload) {
throw new Error("missing websocket payload");
}
expect(ackPayload.runId).toBeDefined();
expect(finalPayload.runId).toBe(ackPayload.runId);
expect(finalPayload.status).toBe("ok");
}); });
test("agent dedupes by idempotencyKey after completion", async () => { test("agent dedupes by idempotencyKey after completion", async () => {

View File

@@ -63,6 +63,7 @@ async function withCanvasGatewayHarness(params: {
const canvasWss = new WebSocketServer({ noServer: true }); const canvasWss = new WebSocketServer({ noServer: true });
const canvasHost: CanvasHostHandler = { const canvasHost: CanvasHostHandler = {
rootDir: "test", rootDir: "test",
basePath: "/canvas",
close: async () => {}, close: async () => {},
handleUpgrade: (req, socket, head) => { handleUpgrade: (req, socket, head) => {
const url = new URL(req.url ?? "/", "http://localhost"); const url = new URL(req.url ?? "/", "http://localhost");

View File

@@ -270,7 +270,7 @@ describe("gateway server chat", () => {
test("smoke: supports abort and idempotent completion", async () => { test("smoke: supports abort and idempotent completion", async () => {
const tempDirs: string[] = []; const tempDirs: string[] = [];
const { server, ws } = await startServerWithClient(); const { server, ws } = await startServerWithClient();
const spy = vi.mocked(getReplyFromConfig); const spy = vi.mocked(getReplyFromConfig) as unknown as ReturnType<typeof vi.fn>;
let aborted = false; let aborted = false;
try { try {

View File

@@ -44,10 +44,10 @@ describe("gateway config.apply", () => {
}, },
}), }),
); );
const res = await onceMessage<{ ok: boolean; error?: { message?: string } }>( const res = await onceMessage<{ ok: boolean; error?: { message?: string } }>(ws, (o) => {
ws, const msg = o as { type?: string; id?: string };
(o) => o.type === "res" && o.id === id, return msg.type === "res" && msg.id === id;
); });
expect(res.ok).toBe(false); expect(res.ok).toBe(false);
expect(res.error?.message ?? "").toMatch(/invalid|SyntaxError/i); expect(res.error?.message ?? "").toMatch(/invalid|SyntaxError/i);
} finally { } finally {
@@ -69,10 +69,10 @@ describe("gateway config.apply", () => {
}, },
}), }),
); );
const res = await onceMessage<{ ok: boolean; error?: { message?: string } }>( const res = await onceMessage<{ ok: boolean; error?: { message?: string } }>(ws, (o) => {
ws, const msg = o as { type?: string; id?: string };
(o) => o.type === "res" && o.id === id, return msg.type === "res" && msg.id === id;
); });
expect(res.ok).toBe(false); expect(res.ok).toBe(false);
expect(res.error?.message ?? "").toContain("raw"); expect(res.error?.message ?? "").toContain("raw");
} finally { } finally {

View File

@@ -561,7 +561,7 @@ describe("gateway server cron", () => {
await yieldToEventLoop(); await yieldToEventLoop();
expect(fetchMock).toHaveBeenCalledTimes(2); expect(fetchMock).toHaveBeenCalledTimes(2);
cronIsolatedRun.mockResolvedValueOnce({ status: "ok" }); cronIsolatedRun.mockResolvedValueOnce({ status: "ok", summary: "" });
const noSummaryRes = await rpcReq(ws, "cron.add", { const noSummaryRes = await rpcReq(ws, "cron.add", {
name: "webhook no summary", name: "webhook no summary",
enabled: true, enabled: true,

View File

@@ -268,10 +268,11 @@ describe("node.invoke approval bypass", () => {
}); });
expect(invoke.ok).toBe(true); expect(invoke.ok).toBe(true);
expect(lastInvokeParams).toBeTruthy(); const invokeParams = lastInvokeParams as Record<string, unknown> | null;
expect(lastInvokeParams?.approved).toBe(true); expect(invokeParams).toBeTruthy();
expect(lastInvokeParams?.approvalDecision).toBe("allow-once"); expect(invokeParams?.["approved"]).toBe(true);
expect(lastInvokeParams?.injected).toBeUndefined(); expect(invokeParams?.["approvalDecision"]).toBe("allow-once");
expect(invokeParams?.["injected"]).toBeUndefined();
ws.close(); ws.close();
ws2.close(); ws2.close();