chore: Fix types in tests 28/N.

This commit is contained in:
cpojer
2026-02-17 14:32:18 +09:00
parent 97c8f4999e
commit 03e6acd051
14 changed files with 104 additions and 55 deletions

View File

@@ -255,8 +255,12 @@ describe("commands registry args", () => {
});
it("resolves function-based choices with a default provider/model context", () => {
let seen: { provider: string; model: string; commandKey: string; argName: string } | null =
null;
let seen: {
provider?: string;
model?: string;
commandKey: string;
argName: string;
} | null = null;
const command: ChatCommandDefinition = {
key: "think",
@@ -284,10 +288,16 @@ describe("commands registry args", () => {
{ label: "low", value: "low" },
{ label: "high", value: "high" },
]);
expect(seen?.commandKey).toBe("think");
expect(seen?.argName).toBe("level");
expect(seen?.provider).toBeTruthy();
expect(seen?.model).toBeTruthy();
const seenChoice = seen as {
provider?: string;
model?: string;
commandKey: string;
argName: string;
} | null;
expect(seenChoice?.commandKey).toBe("think");
expect(seenChoice?.argName).toBe("level");
expect(seenChoice?.provider).toBeTruthy();
expect(seenChoice?.model).toBeTruthy();
});
it("does not show menus when args were provided as raw text only", () => {

View File

@@ -1,6 +1,7 @@
import "./reply.directive.directive-behavior.e2e-mocks.js";
import path from "node:path";
import { describe, expect, it, vi } from "vitest";
import type { OpenClawConfig } from "../config/config.js";
import {
installDirectiveBehaviorE2EHooks,
loadModelCatalog,
@@ -18,7 +19,7 @@ function makeThinkConfig(home: string) {
},
},
session: { store: path.join(home, "sessions.json") },
} as const;
} as unknown as OpenClawConfig;
}
function makeWhatsAppConfig(home: string) {
@@ -31,7 +32,7 @@ function makeWhatsAppConfig(home: string) {
},
channels: { whatsapp: { allowFrom: ["*"] } },
session: { store: path.join(home, "sessions.json") },
} as const;
} as unknown as OpenClawConfig;
}
async function runReplyToCurrentCase(home: string, text: string) {

View File

@@ -1,6 +1,7 @@
import "./reply.directive.directive-behavior.e2e-mocks.js";
import path from "node:path";
import { describe, expect, it } from "vitest";
import type { OpenClawConfig } from "../config/config.js";
import {
assertModelSelection,
installDirectiveBehaviorE2EHooks,
@@ -9,6 +10,18 @@ import {
} from "./reply.directive.directive-behavior.e2e-harness.js";
import { getReplyFromConfig } from "./reply.js";
function makeModelDefinition(id: string, name: string) {
return {
id,
name,
reasoning: false,
input: ["text"],
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
contextWindow: 200_000,
maxTokens: 8192,
};
}
function makeMoonshotConfig(home: string, storePath: string) {
return {
agents: {
@@ -28,12 +41,12 @@ function makeMoonshotConfig(home: string, storePath: string) {
baseUrl: "https://api.moonshot.ai/v1",
apiKey: "sk-test",
api: "openai-completions",
models: [{ id: "kimi-k2-0905-preview", name: "Kimi K2" }],
models: [makeModelDefinition("kimi-k2-0905-preview", "Kimi K2")],
},
},
},
session: { store: storePath },
};
} as unknown as OpenClawConfig;
}
describe("directive behavior", () => {
@@ -129,18 +142,18 @@ describe("directive behavior", () => {
baseUrl: "https://api.minimax.io/anthropic",
apiKey: "sk-test",
api: "anthropic-messages",
models: [{ id: "MiniMax-M2.1", name: "MiniMax M2.1" }],
models: [makeModelDefinition("MiniMax-M2.1", "MiniMax M2.1")],
},
lmstudio: {
baseUrl: "http://127.0.0.1:1234/v1",
apiKey: "lmstudio",
api: "openai-responses",
models: [{ id: "minimax-m2.1-gs32", name: "MiniMax M2.1 GS32" }],
models: [makeModelDefinition("minimax-m2.1-gs32", "MiniMax M2.1 GS32")],
},
},
},
session: { store: storePath },
},
} as unknown as OpenClawConfig,
);
assertModelSelection(storePath);
@@ -173,17 +186,14 @@ describe("directive behavior", () => {
apiKey: "sk-test",
api: "anthropic-messages",
models: [
{ id: "MiniMax-M2.1", name: "MiniMax M2.1" },
{
id: "MiniMax-M2.1-lightning",
name: "MiniMax M2.1 Lightning",
},
makeModelDefinition("MiniMax-M2.1", "MiniMax M2.1"),
makeModelDefinition("MiniMax-M2.1-lightning", "MiniMax M2.1 Lightning"),
],
},
},
},
session: { store: storePath },
},
} as unknown as OpenClawConfig,
);
assertModelSelection(storePath);

View File

@@ -1,6 +1,7 @@
import fs from "node:fs/promises";
import { join } from "node:path";
import { beforeAll, describe, expect, it } from "vitest";
import type { OpenClawConfig } from "../config/config.js";
import { loadSessionStore } from "../config/sessions.js";
import {
getAbortEmbeddedPiRunMock,
@@ -23,10 +24,14 @@ describe("trigger handling", () => {
it("targets the active session for native /stop", async () => {
await withTempHome(async (home) => {
const cfg = makeCfg(home);
const storePath = cfg.session?.store;
if (!storePath) {
throw new Error("missing session store path");
}
const targetSessionKey = "agent:main:telegram:group:123";
const targetSessionId = "session-target";
await fs.writeFile(
cfg.session!.store,
storePath,
JSON.stringify(
{
[targetSessionKey]: {
@@ -85,7 +90,7 @@ describe("trigger handling", () => {
const text = Array.isArray(res) ? res[0]?.text : res?.text;
expect(text).toBe("⚙️ Agent was aborted.");
expect(getAbortEmbeddedPiRunMock()).toHaveBeenCalledWith(targetSessionId);
const store = loadSessionStore(cfg.session!.store);
const store = loadSessionStore(storePath);
expect(store[targetSessionKey]?.abortedLastRun).toBe(true);
expect(getFollowupQueueDepth(targetSessionKey)).toBe(0);
});
@@ -93,12 +98,16 @@ describe("trigger handling", () => {
it("applies native /model to the target session", async () => {
await withTempHome(async (home) => {
const cfg = makeCfg(home);
const storePath = cfg.session?.store;
if (!storePath) {
throw new Error("missing session store path");
}
const slashSessionKey = "telegram:slash:111";
const targetSessionKey = MAIN_SESSION_KEY;
// Seed the target session to ensure the native command mutates it.
await fs.writeFile(
cfg.session!.store,
storePath,
JSON.stringify(
{
[targetSessionKey]: {
@@ -131,7 +140,7 @@ describe("trigger handling", () => {
const text = Array.isArray(res) ? res[0]?.text : res?.text;
expect(text).toContain("Model set to openai/gpt-4.1-mini");
const store = loadSessionStore(cfg.session!.store);
const store = loadSessionStore(storePath);
expect(store[targetSessionKey]?.providerOverride).toBe("openai");
expect(store[targetSessionKey]?.modelOverride).toBe("gpt-4.1-mini");
expect(store[slashSessionKey]).toBeUndefined();
@@ -183,7 +192,7 @@ describe("trigger handling", () => {
},
},
session: { store: join(home, "sessions.json") },
};
} as unknown as OpenClawConfig;
const res = await getReplyFromConfig(
{

View File

@@ -2,6 +2,7 @@ import fs from "node:fs/promises";
import os from "node:os";
import path from "node:path";
import { afterEach, describe, expect, it, vi } from "vitest";
import type { SubagentRunRecord } from "../../agents/subagent-registry.js";
import type { OpenClawConfig } from "../../config/config.js";
import {
getAbortMemory,
@@ -28,7 +29,9 @@ const commandQueueMocks = vi.hoisted(() => ({
vi.mock("../../process/command-queue.js", () => commandQueueMocks);
const subagentRegistryMocks = vi.hoisted(() => ({
listSubagentRunsForRequester: vi.fn(() => []),
listSubagentRunsForRequester: vi.fn<(requesterSessionKey: string) => SubagentRunRecord[]>(
() => [],
),
markSubagentRunTerminated: vi.fn(() => 1),
}));

View File

@@ -233,7 +233,7 @@ async function runReplyAgentWithBase(params: {
baseRun: ReturnType<typeof createBaseRun>;
storePath: string;
sessionKey: string;
sessionEntry: Record<string, unknown>;
sessionEntry: SessionEntry;
commandBody: string;
typingMode?: "instant";
}): Promise<void> {
@@ -303,7 +303,7 @@ describe("runReplyAgent typing (heartbeat)", () => {
persistStore: boolean;
}) {
const storePath = path.join(params.stateDir, "sessions", "sessions.json");
const sessionEntry = { sessionId: params.sessionId, updatedAt: Date.now() };
const sessionEntry: SessionEntry = { sessionId: params.sessionId, updatedAt: Date.now() };
const sessionStore = { main: sessionEntry };
await fs.mkdir(path.dirname(storePath), { recursive: true });
@@ -490,7 +490,7 @@ describe("runReplyAgent typing (heartbeat)", () => {
it("announces auto-compaction in verbose mode and tracks count", async () => {
await withTempStateDir(async (stateDir) => {
const storePath = path.join(stateDir, "sessions", "sessions.json");
const sessionEntry = { sessionId: "session", updatedAt: Date.now() };
const sessionEntry: SessionEntry = { sessionId: "session", updatedAt: Date.now() };
const sessionStore = { main: sessionEntry };
state.runEmbeddedPiAgentMock.mockImplementationOnce(async (params: AgentRunParams) => {
@@ -549,6 +549,9 @@ describe("runReplyAgent typing (heartbeat)", () => {
expect(payload).toMatchObject({
text: expect.stringContaining("Context limit exceeded during compaction"),
});
if (!payload) {
throw new Error("expected payload");
}
expect(payload.text?.toLowerCase()).toContain("reset");
expect(sessionStore.main.sessionId).not.toBe(sessionId);
@@ -594,6 +597,9 @@ describe("runReplyAgent typing (heartbeat)", () => {
expect(payload).toMatchObject({
text: expect.stringContaining("Context limit exceeded"),
});
if (!payload) {
throw new Error("expected payload");
}
expect(payload.text?.toLowerCase()).toContain("reset");
expect(sessionStore.main.sessionId).not.toBe(sessionId);
@@ -638,6 +644,9 @@ describe("runReplyAgent typing (heartbeat)", () => {
expect(payload).toMatchObject({
text: expect.stringContaining("Message ordering conflict"),
});
if (!payload) {
throw new Error("expected payload");
}
expect(payload.text?.toLowerCase()).toContain("reset");
expect(sessionStore.main.sessionId).not.toBe(sessionId);
await expect(fs.access(transcriptPath)).rejects.toBeDefined();

View File

@@ -128,7 +128,8 @@ describe("createFollowupRunner compaction", () => {
await runner(queued);
expect(onBlockReply).toHaveBeenCalled();
expect(onBlockReply.mock.calls[0][0].text).toContain("Auto-compaction complete");
const firstCall = (onBlockReply.mock.calls as unknown as Array<Array<{ text?: string }>>)[0];
expect(firstCall?.[0]?.text).toContain("Auto-compaction complete");
expect(sessionStore.main.compactionCount).toBe(1);
});

View File

@@ -16,7 +16,7 @@ import { SILENT_REPLY_TOKEN } from "../tokens.js";
const mocks = vi.hoisted(() => ({
sendMessageDiscord: vi.fn(async () => ({ messageId: "m1", channelId: "c1" })),
sendMessageIMessage: vi.fn(async () => ({ messageId: "ok" })),
sendMessageMSTeams: vi.fn(async () => ({
sendMessageMSTeams: vi.fn(async (_params: unknown) => ({
messageId: "m1",
conversationId: "c1",
})),

View File

@@ -5,6 +5,7 @@ import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, it, vi }
import { buildModelAliasIndex } from "../../agents/model-selection.js";
import type { OpenClawConfig } from "../../config/config.js";
import { saveSessionStore } from "../../config/sessions.js";
import type { SessionEntry } from "../../config/sessions.js";
import { formatZonedTimestamp } from "../../infra/format-time/format-datetime.ts";
import { enqueueSystemEvent, resetSystemEventsForTest } from "../../infra/system-events.js";
import { applyResetModelOverride } from "./session-reset-model.js";
@@ -866,11 +867,11 @@ describe("applyResetModelOverride", () => {
it("selects a model hint and strips it from the body", async () => {
const cfg = {} as OpenClawConfig;
const aliasIndex = buildModelAliasIndex({ cfg, defaultProvider: "openai" });
const sessionEntry = {
const sessionEntry: SessionEntry = {
sessionId: "s1",
updatedAt: Date.now(),
};
const sessionStore = { "agent:main:dm:1": sessionEntry };
const sessionStore: Record<string, SessionEntry> = { "agent:main:dm:1": sessionEntry };
const sessionCtx = { BodyStripped: "minimax summarize" };
const ctx = { ChatType: "direct" };
@@ -896,14 +897,14 @@ describe("applyResetModelOverride", () => {
it("clears auth profile overrides when reset applies a model", async () => {
const cfg = {} as OpenClawConfig;
const aliasIndex = buildModelAliasIndex({ cfg, defaultProvider: "openai" });
const sessionEntry = {
const sessionEntry: SessionEntry = {
sessionId: "s1",
updatedAt: Date.now(),
authProfileOverride: "anthropic:default",
authProfileOverrideSource: "user",
authProfileOverrideCompactionCount: 2,
};
const sessionStore = { "agent:main:dm:1": sessionEntry };
const sessionStore: Record<string, SessionEntry> = { "agent:main:dm:1": sessionEntry };
const sessionCtx = { BodyStripped: "minimax summarize" };
const ctx = { ChatType: "direct" };
@@ -929,11 +930,11 @@ describe("applyResetModelOverride", () => {
it("skips when resetTriggered is false", async () => {
const cfg = {} as OpenClawConfig;
const aliasIndex = buildModelAliasIndex({ cfg, defaultProvider: "openai" });
const sessionEntry = {
const sessionEntry: SessionEntry = {
sessionId: "s1",
updatedAt: Date.now(),
};
const sessionStore = { "agent:main:dm:1": sessionEntry };
const sessionStore: Record<string, SessionEntry> = { "agent:main:dm:1": sessionEntry };
const sessionCtx = { BodyStripped: "minimax summarize" };
const ctx = { ChatType: "direct" };

View File

@@ -48,7 +48,7 @@ describe("buildStatusMessage", () => {
},
},
},
} as OpenClawConfig,
} as unknown as OpenClawConfig,
agent: {
model: "anthropic/pi:opus",
contextTokens: 32_000,
@@ -99,7 +99,7 @@ describe("buildStatusMessage", () => {
{ id: "discord", sandbox: { mode: "all" } },
],
},
} as OpenClawConfig,
} as unknown as OpenClawConfig,
agent: {},
sessionKey: "agent:discord:discord:channel:1456350065223270435",
sessionScope: "per-sender",
@@ -314,7 +314,7 @@ describe("buildStatusMessage", () => {
},
},
},
} as OpenClawConfig,
} as unknown as OpenClawConfig,
agent: { model: "anthropic/claude-opus-4-5" },
sessionEntry: { sessionId: "c1", updatedAt: 0, inputTokens: 10 },
sessionKey: "agent:main:main",
@@ -491,7 +491,7 @@ describe("buildCommandsMessage", () => {
it("lists commands with aliases and hints", () => {
const text = buildCommandsMessage({
commands: { config: false, debug: false },
} as OpenClawConfig);
} as unknown as OpenClawConfig);
expect(text).toContain(" Slash commands");
expect(text).toContain("Status");
expect(text).toContain("/commands - List all slash commands.");
@@ -506,7 +506,7 @@ describe("buildCommandsMessage", () => {
const text = buildCommandsMessage(
{
commands: { config: false, debug: false },
} as OpenClawConfig,
} as unknown as OpenClawConfig,
[
{
name: "demo_skill",
@@ -523,7 +523,7 @@ describe("buildHelpMessage", () => {
it("hides config/debug when disabled", () => {
const text = buildHelpMessage({
commands: { config: false, debug: false },
} as OpenClawConfig);
} as unknown as OpenClawConfig);
expect(text).toContain("Skills");
expect(text).toContain("/skill <name> [input]");
expect(text).not.toContain("/config");
@@ -536,7 +536,7 @@ describe("buildCommandsMessagePaginated", () => {
const result = buildCommandsMessagePaginated(
{
commands: { config: false, debug: false },
} as OpenClawConfig,
} as unknown as OpenClawConfig,
undefined,
{ surface: "telegram", page: 1 },
);
@@ -552,7 +552,7 @@ describe("buildCommandsMessagePaginated", () => {
const result = buildCommandsMessagePaginated(
{
commands: { config: false, debug: false },
} as OpenClawConfig,
} as unknown as OpenClawConfig,
undefined,
{ surface: "telegram", page: 99 },
);

View File

@@ -86,7 +86,7 @@ describe("attachChildProcessBridge", () => {
if (!addedSigterm) {
throw new Error("expected SIGTERM listener");
}
addedSigterm();
addedSigterm("SIGTERM");
await new Promise<void>((resolve, reject) => {
const timeout = setTimeout(() => reject(new Error("timeout waiting for child exit")), 10_000);

View File

@@ -98,7 +98,7 @@ describe("command queue", () => {
await Promise.all([first, second]);
expect(waited).not.toBeNull();
expect(waited as number).toBeGreaterThanOrEqual(5);
expect(waited as unknown as number).toBeGreaterThanOrEqual(5);
expect(queuedAhead).toBe(0);
});

View File

@@ -45,9 +45,10 @@ describe("signal createSignalEventHandler inbound contract", () => {
expect(capturedCtx).toBeTruthy();
expectInboundContextContract(capturedCtx!);
const contextWithBody = capturedCtx as unknown as { Body?: string };
// Sender should appear as prefix in group messages (no redundant [from:] suffix)
expect(String(capturedCtx?.Body ?? "")).toContain("Alice");
expect(String(capturedCtx?.Body ?? "")).toMatch(/Alice.*:/);
expect(String(capturedCtx?.Body ?? "")).not.toContain("[from:");
expect(String(contextWithBody.Body ?? "")).toContain("Alice");
expect(String(contextWithBody.Body ?? "")).toMatch(/Alice.*:/);
expect(String(contextWithBody.Body ?? "")).not.toContain("[from:");
});
});

View File

@@ -4,13 +4,17 @@ import type { MsgContext } from "../../auto-reply/templating.js";
import type { OpenClawConfig } from "../../config/types.js";
import { createBaseSignalEventHandlerDeps } from "./event-handler.test-harness.js";
type SignalMsgContext = MsgContext & {
type SignalMsgContext = Pick<MsgContext, "Body" | "WasMentioned"> & {
Body?: string;
WasMentioned?: boolean;
};
let capturedCtx: SignalMsgContext | undefined;
function getCapturedCtx() {
return capturedCtx as SignalMsgContext;
}
vi.mock("../../auto-reply/dispatch.js", async (importOriginal) => {
const actual = await importOriginal<typeof import("../../auto-reply/dispatch.js")>();
return buildDispatchInboundCaptureMock(actual, (ctx) => {
@@ -113,7 +117,7 @@ describe("signal mention gating", () => {
await handler(makeGroupEvent({ message: "hey @bot what's up" }));
expect(capturedCtx).toBeTruthy();
expect(capturedCtx?.WasMentioned).toBe(true);
expect(getCapturedCtx()?.WasMentioned).toBe(true);
});
it("sets WasMentioned=false for group messages without mention when requireMention is off", async () => {
@@ -126,7 +130,7 @@ describe("signal mention gating", () => {
await handler(makeGroupEvent({ message: "hello everyone" }));
expect(capturedCtx).toBeTruthy();
expect(capturedCtx?.WasMentioned).toBe(false);
expect(getCapturedCtx()?.WasMentioned).toBe(false);
});
it("records pending history for skipped group messages", async () => {
@@ -187,7 +191,7 @@ describe("signal mention gating", () => {
);
expect(capturedCtx).toBeTruthy();
const body = String(capturedCtx?.Body ?? "");
const body = String(getCapturedCtx()?.Body ?? "");
expect(body).toContain("@123e4567 hi @+15550002222");
expect(body).not.toContain(placeholder);
});
@@ -212,8 +216,8 @@ describe("signal mention gating", () => {
);
expect(capturedCtx).toBeTruthy();
expect(String(capturedCtx?.Body ?? "")).toContain("@123e4567");
expect(capturedCtx?.WasMentioned).toBe(true);
expect(String(getCapturedCtx()?.Body ?? "")).toContain("@123e4567");
expect(getCapturedCtx()?.WasMentioned).toBe(true);
});
});