chore(tsgo/format): fix CI errors

This commit is contained in:
Gustavo Madeira Santana
2026-02-21 17:51:56 -05:00
parent 6ac89757ba
commit 0e1aa77928
15 changed files with 133 additions and 59 deletions

View File

@@ -449,7 +449,7 @@ describe("parseLineDirectives", () => {
if (testCase.expectFooter) {
expect(flexMessage?.contents?.footer?.contents?.length, testCase.name).toBeGreaterThan(0);
}
if (testCase.expectBodyContents) {
if ("expectBodyContents" in testCase && testCase.expectBodyContents) {
expect(flexMessage?.contents?.body?.contents, testCase.name).toBeDefined();
}
}

View File

@@ -626,7 +626,7 @@ describe("initSessionState reset triggers in WhatsApp groups", () => {
});
const cfg = makeCfg({
storePath,
allowFrom: testCase.allowFrom,
allowFrom: [...testCase.allowFrom],
});
const result = await initSessionState({

View File

@@ -34,7 +34,7 @@ describe("program helpers", () => {
it("resolveActionArgs returns empty array for missing/invalid args", () => {
const command = new Command();
(command as Command & { args?: unknown }).args = "not-an-array";
(command as unknown as { args?: unknown }).args = "not-an-array";
expect(resolveActionArgs(command)).toEqual([]);
expect(resolveActionArgs(undefined)).toEqual([]);
});

View File

@@ -1,5 +1,6 @@
import { describe, expect, it } from "vitest";
import { migrateLegacyConfig, validateConfigObject } from "./config.js";
import type { OpenClawConfig } from "./config.js";
function getLegacyRouting(config: unknown) {
return (config as { routing?: Record<string, unknown> } | undefined)?.routing;
@@ -470,13 +471,16 @@ describe("legacy config detection", () => {
const res = validateConfigObject(testCase.input);
expect(res.ok, testCase.name).toBe(true);
if (res.ok) {
if (testCase.expectedTopLevel !== undefined) {
if ("expectedTopLevel" in testCase && testCase.expectedTopLevel !== undefined) {
expect(res.config.channels?.telegram?.streaming, testCase.name).toBe(
testCase.expectedTopLevel,
);
expect(res.config.channels?.telegram?.streamMode, testCase.name).toBeUndefined();
}
if (testCase.expectedAccountStreaming !== undefined) {
if (
"expectedAccountStreaming" in testCase &&
testCase.expectedAccountStreaming !== undefined
) {
expect(res.config.channels?.telegram?.accounts?.ops?.streaming, testCase.name).toBe(
testCase.expectedAccountStreaming,
);

View File

@@ -409,12 +409,12 @@ describe("redactConfigSnapshot", () => {
}),
assert: ({ redacted, restored }) => {
const cfg = redacted as Record<string, Record<string, unknown>>;
const cfgCustom2 = cfg.custom2 as unknown[];
const cfgCustom2 = cfg.custom2 as unknown as unknown[];
expect(cfgCustom2.length).toBeGreaterThan(0);
expect((cfg.custom1.anykey as Record<string, unknown>).mySecret).toBe(REDACTED_SENTINEL);
expect((cfgCustom2[0] as Record<string, unknown>).mySecret).toBe(REDACTED_SENTINEL);
const out = restored as Record<string, Record<string, unknown>>;
const outCustom2 = out.custom2 as unknown[];
const outCustom2 = out.custom2 as unknown as unknown[];
expect(outCustom2.length).toBeGreaterThan(0);
expect((out.custom1.anykey as Record<string, unknown>).mySecret).toBe(
"this-is-a-custom-secret-value",
@@ -436,12 +436,12 @@ describe("redactConfigSnapshot", () => {
}),
assert: ({ redacted, restored }) => {
const cfg = redacted as Record<string, Record<string, unknown>>;
const cfgCustom2 = cfg.custom2 as unknown[];
const cfgCustom2 = cfg.custom2 as unknown as unknown[];
expect(cfgCustom2.length).toBeGreaterThan(0);
expect((cfg.custom1.anykey as Record<string, unknown>).mySecret).toBe(REDACTED_SENTINEL);
expect((cfgCustom2[0] as Record<string, unknown>).mySecret).toBe(REDACTED_SENTINEL);
const out = restored as Record<string, Record<string, unknown>>;
const outCustom2 = out.custom2 as unknown[];
const outCustom2 = out.custom2 as unknown as unknown[];
expect(outCustom2.length).toBeGreaterThan(0);
expect((out.custom1.anykey as Record<string, unknown>).mySecret).toBe(
"this-is-a-custom-secret-value",

View File

@@ -9,7 +9,9 @@ vi.mock("../../config/sessions.js", () => ({
}));
vi.mock("../../infra/outbound/channel-selection.js", () => ({
resolveMessageChannelSelection: vi.fn().mockResolvedValue({ channel: "telegram" }),
resolveMessageChannelSelection: vi
.fn()
.mockResolvedValue({ channel: "telegram", configured: ["telegram"] }),
}));
vi.mock("../../pairing/pairing-store.js", () => ({
@@ -261,7 +263,10 @@ describe("resolveDeliveryTarget", () => {
it("uses channel selection result when no previous session target exists", async () => {
setMainSessionEntry(undefined);
vi.mocked(resolveMessageChannelSelection).mockResolvedValueOnce({ channel: "telegram" });
vi.mocked(resolveMessageChannelSelection).mockResolvedValueOnce({
channel: "telegram",
configured: ["telegram"],
});
const result = await resolveForAgent({
cfg: makeCfg({ bindings: [] }),

View File

@@ -705,7 +705,7 @@ describe("discord reaction notification gating", () => {
botId: "bot-1",
messageAuthorId: "user-1",
userId: "user-2",
allowlist: [],
allowlist: [] as string[],
},
expected: false,
},
@@ -717,7 +717,7 @@ describe("discord reaction notification gating", () => {
messageAuthorId: "user-1",
userId: "123",
userName: "steipete",
allowlist: ["123", "other"],
allowlist: ["123", "other"] as string[],
},
expected: true,
},
@@ -984,7 +984,7 @@ describe("discord reaction notification modes", () => {
{
name: "allowlist mode",
reactionNotifications: "allowlist" as const,
users: ["123"],
users: ["123"] as string[],
userId: "123",
channelType: ChannelType.GuildText,
channelId: undefined,

View File

@@ -24,6 +24,7 @@ import {
resolveExecApprovalsSocketPath,
resolveSafeBins,
type ExecAllowlistEntry,
type ExecApprovalsAgent,
type ExecApprovalsFile,
} from "./exec-approvals.js";
import { SAFE_BIN_PROFILE_FIXTURES, SAFE_BIN_PROFILES } from "./exec-safe-bin-policy.js";
@@ -204,7 +205,7 @@ describe("exec approvals command resolution", () => {
return {
command: "./scripts/run.sh --flag",
cwd,
envPath: undefined as string | undefined,
envPath: undefined as NodeJS.ProcessEnv | undefined,
expectedPath: script,
expectedExecutableName: undefined,
};
@@ -222,7 +223,7 @@ describe("exec approvals command resolution", () => {
return {
command: '"./bin/tool" --version',
cwd,
envPath: undefined as string | undefined,
envPath: undefined as NodeJS.ProcessEnv | undefined,
expectedPath: script,
expectedExecutableName: undefined,
};
@@ -258,7 +259,7 @@ describe("exec approvals shell parsing", () => {
for (const testCase of cases) {
const res = analyzeShellCommand({ command: testCase.command });
expect(res.ok, testCase.name).toBe(true);
if (testCase.expectedSegments) {
if ("expectedSegments" in testCase) {
expect(
res.segments.map((seg) => seg.argv[0]),
testCase.name,
@@ -1197,7 +1198,7 @@ describe("normalizeExecApprovals handles string allowlist entries (#9790)", () =
const patterns = getMainAllowlistPatterns({
version: 1,
agents: {
main: { allowlist: testCase.allowlist } as ExecApprovalsFile["agents"]["main"],
main: { allowlist: testCase.allowlist } as ExecApprovalsAgent,
},
});
expect(patterns, testCase.name).toEqual(testCase.expectedPatterns);
@@ -1205,7 +1206,7 @@ describe("normalizeExecApprovals handles string allowlist entries (#9790)", () =
const entries = normalizeExecApprovals({
version: 1,
agents: {
main: { allowlist: testCase.allowlist } as ExecApprovalsFile["agents"]["main"],
main: { allowlist: testCase.allowlist } as ExecApprovalsAgent,
},
}).agents?.main?.allowlist;
expectNoSpreadStringArtifacts(entries ?? []);

View File

@@ -17,6 +17,7 @@ import { buildAgentPeerSessionKey } from "../routing/session-key.js";
import { createOutboundTestPlugin, createTestRegistry } from "../test-utils/channel-plugins.js";
import {
isHeartbeatEnabledForAgent,
type HeartbeatDeps,
resolveHeartbeatIntervalMs,
resolveHeartbeatPrompt,
runHeartbeatOnce,
@@ -439,7 +440,10 @@ describe("resolveHeartbeatSenderContext", () => {
});
describe("runHeartbeatOnce", () => {
const createHeartbeatDeps = (sendWhatsApp: ReturnType<typeof vi.fn>, nowMs = 0) => ({
const createHeartbeatDeps = (
sendWhatsApp: NonNullable<HeartbeatDeps["sendWhatsApp"]>,
nowMs = 0,
): HeartbeatDeps => ({
sendWhatsApp,
getQueueSize: () => 0,
nowMs: () => nowMs,
@@ -516,7 +520,7 @@ describe("runHeartbeatOnce", () => {
);
replySpy.mockResolvedValue([{ text: "Let me check..." }, { text: "Final alert" }]);
const sendWhatsApp = vi.fn().mockResolvedValue({
const sendWhatsApp = vi.fn<NonNullable<HeartbeatDeps["sendWhatsApp"]>>().mockResolvedValue({
messageId: "m1",
toJid: "jid",
});
@@ -569,7 +573,7 @@ describe("runHeartbeatOnce", () => {
}),
);
replySpy.mockResolvedValue([{ text: "Final alert" }]);
const sendWhatsApp = vi.fn().mockResolvedValue({
const sendWhatsApp = vi.fn<NonNullable<HeartbeatDeps["sendWhatsApp"]>>().mockResolvedValue({
messageId: "m1",
toJid: "jid",
});
@@ -645,7 +649,7 @@ describe("runHeartbeatOnce", () => {
);
replySpy.mockResolvedValue([{ text: "Final alert" }]);
const sendWhatsApp = vi.fn().mockResolvedValue({
const sendWhatsApp = vi.fn<NonNullable<HeartbeatDeps["sendWhatsApp"]>>().mockResolvedValue({
messageId: "m1",
toJid: "jid",
});
@@ -749,7 +753,9 @@ describe("runHeartbeatOnce", () => {
replySpy.mockReset();
replySpy.mockResolvedValue([{ text: testCase.message }]);
const sendWhatsApp = vi.fn().mockResolvedValue({ messageId: "m1", toJid: "jid" });
const sendWhatsApp = vi
.fn<NonNullable<HeartbeatDeps["sendWhatsApp"]>>()
.mockResolvedValue({ messageId: "m1", toJid: "jid" });
await runHeartbeatOnce({
cfg,
@@ -811,7 +817,9 @@ describe("runHeartbeatOnce", () => {
);
replySpy.mockResolvedValue([{ text: "Final alert" }]);
const sendWhatsApp = vi.fn().mockResolvedValue({ messageId: "m1", toJid: "jid" });
const sendWhatsApp = vi
.fn<NonNullable<HeartbeatDeps["sendWhatsApp"]>>()
.mockResolvedValue({ messageId: "m1", toJid: "jid" });
await runHeartbeatOnce({
cfg,
@@ -827,7 +835,12 @@ describe("runHeartbeatOnce", () => {
it("handles reasoning payload delivery variants", async () => {
const replySpy = vi.spyOn(replyModule, "getReplyFromConfig");
try {
const cases = [
const cases: Array<{
name: string;
caseDir: string;
replies: Array<{ text: string }>;
expectedTexts: string[];
}> = [
{
name: "reasoning + final payload",
caseDir: "hb-reasoning",
@@ -840,7 +853,7 @@ describe("runHeartbeatOnce", () => {
replies: [{ text: "Reasoning:\n_Because it helps_" }, { text: "HEARTBEAT_OK" }],
expectedTexts: ["Reasoning:\n_Because it helps_"],
},
] as const;
];
for (const testCase of cases) {
const tmpDir = await createCaseDir(testCase.caseDir);
@@ -876,7 +889,9 @@ describe("runHeartbeatOnce", () => {
replySpy.mockReset();
replySpy.mockResolvedValue(testCase.replies);
const sendWhatsApp = vi.fn().mockResolvedValue({ messageId: "m1", toJid: "jid" });
const sendWhatsApp = vi
.fn<NonNullable<HeartbeatDeps["sendWhatsApp"]>>()
.mockResolvedValue({ messageId: "m1", toJid: "jid" });
await runHeartbeatOnce({
cfg,
@@ -934,7 +949,7 @@ describe("runHeartbeatOnce", () => {
);
replySpy.mockResolvedValue({ text: "Hello from heartbeat" });
const sendWhatsApp = vi.fn().mockResolvedValue({
const sendWhatsApp = vi.fn<NonNullable<HeartbeatDeps["sendWhatsApp"]>>().mockResolvedValue({
messageId: "m1",
toJid: "jid",
});
@@ -1020,7 +1035,9 @@ describe("runHeartbeatOnce", () => {
const replySpy = vi.spyOn(replyModule, "getReplyFromConfig");
replySpy.mockResolvedValue({ text: params.replyText ?? "Checked logs and PRs" });
const sendWhatsApp = vi.fn().mockResolvedValue({ messageId: "m1", toJid: "jid" });
const sendWhatsApp = vi
.fn<NonNullable<HeartbeatDeps["sendWhatsApp"]>>()
.mockResolvedValue({ messageId: "m1", toJid: "jid" });
const res = await runHeartbeatOnce({
cfg,
reason: params.reason,

View File

@@ -407,7 +407,7 @@ describe("DirectoryCache", () => {
] as const,
expected: { a: "value-a2", b: undefined, c: "value-c" },
},
] as const;
];
for (const testCase of cases) {
const cache = new DirectoryCache<string>(60_000, 2);
@@ -477,7 +477,7 @@ describe("buildOutboundResultEnvelope", () => {
input: { delivery: discordDelivery, flattenDelivery: false },
expected: { delivery: discordDelivery },
},
] as const;
];
for (const testCase of cases) {
expect(buildOutboundResultEnvelope(testCase.input), testCase.name).toEqual(testCase.expected);
}
@@ -519,7 +519,7 @@ describe("formatOutboundDeliverySummary", () => {
},
expected: "✅ Sent via Discord. Message ID: d1 (channel chan)",
},
] as const;
];
for (const testCase of cases) {
expect(formatOutboundDeliverySummary(testCase.channel, testCase.result), testCase.name).toBe(
@@ -581,7 +581,7 @@ describe("buildOutboundDeliveryJson", () => {
timestamp: 123,
},
},
] as const;
];
for (const testCase of cases) {
expect(buildOutboundDeliveryJson(testCase.input), testCase.name).toEqual(testCase.expected);
@@ -602,7 +602,7 @@ describe("formatGatewaySummary", () => {
input: { action: "Poll sent", channel: "discord", messageId: "p1" },
expected: "✅ Poll sent via gateway (discord). Message ID: p1",
},
] as const;
];
for (const testCase of cases) {
expect(formatGatewaySummary(testCase.input), testCase.name).toBe(testCase.expected);
@@ -844,7 +844,7 @@ describe("normalizeOutboundPayloadsForJson", () => {
},
],
},
] as const;
];
for (const testCase of cases) {
expect(normalizeOutboundPayloadsForJson(testCase.input)).toEqual(testCase.expected);
@@ -879,7 +879,7 @@ describe("formatOutboundPayloadLog", () => {
},
expected: "MEDIA:https://x.test/a.png",
},
] as const;
];
for (const testCase of cases) {
expect(formatOutboundPayloadLog(testCase.input), testCase.name).toBe(testCase.expected);

View File

@@ -48,9 +48,19 @@ describe("jaccardSimilarity", () => {
expected: 1,
},
{ name: "disjoint sets", left: new Set(["a", "b"]), right: new Set(["c", "d"]), expected: 0 },
{ name: "two empty sets", left: new Set(), right: new Set(), expected: 1 },
{ name: "left non-empty right empty", left: new Set(["a"]), right: new Set(), expected: 0 },
{ name: "left empty right non-empty", left: new Set(), right: new Set(["a"]), expected: 0 },
{ name: "two empty sets", left: new Set<string>(), right: new Set<string>(), expected: 1 },
{
name: "left non-empty right empty",
left: new Set(["a"]),
right: new Set<string>(),
expected: 0,
},
{
name: "left empty right non-empty",
left: new Set<string>(),
right: new Set(["a"]),
expected: 0,
},
{
name: "partial overlap",
left: new Set(["a", "b", "c"]),

View File

@@ -1249,7 +1249,7 @@ describe("QmdMemoryManager", () => {
for (const testCase of cases) {
const { manager } = await createManager();
const restoreOpen = testCase.installOpenSpy?.();
const restoreOpen = "installOpenSpy" in testCase ? testCase.installOpenSpy() : undefined;
try {
const result = await manager.readFile(testCase.request);
expect(result, testCase.name).toEqual({ text: "", path: testCase.expectedPath });

View File

@@ -237,11 +237,15 @@ describe("edge cases", () => {
] as const;
for (const testCase of cases) {
const result = markdownToTelegramHtml(testCase.input);
for (const expected of testCase.contains ?? []) {
expect(result, testCase.name).toContain(expected);
if ("contains" in testCase) {
for (const expected of testCase.contains) {
expect(result, testCase.name).toContain(expected);
}
}
for (const unexpected of testCase.notContains ?? []) {
expect(result, testCase.name).not.toContain(unexpected);
if ("notContains" in testCase) {
for (const unexpected of testCase.notContains) {
expect(result, testCase.name).not.toContain(unexpected);
}
}
}
});
@@ -297,8 +301,10 @@ describe("edge cases", () => {
if ("expectedExact" in testCase) {
expect(result, testCase.name).toBe(testCase.expectedExact);
}
for (const expected of testCase.contains ?? []) {
expect(result, testCase.name).toContain(expected);
if ("contains" in testCase) {
for (const expected of testCase.contains) {
expect(result, testCase.name).toContain(expected);
}
}
}
});

View File

@@ -166,7 +166,7 @@ describe("buildModelsKeyboard", () => {
for (const testCase of cases) {
const result = buildModelsKeyboard({
provider: "anthropic",
models: testCase.params.models,
models: [...testCase.params.models],
currentPage: testCase.params.currentPage,
totalPages: 3,
pageSize: 2,

View File

@@ -74,7 +74,11 @@ describe("sent-message-cache", () => {
describe("buildInlineKeyboard", () => {
it("normalizes keyboard inputs", () => {
const cases = [
const cases: Array<{
name: string;
input: Parameters<typeof buildInlineKeyboard>[0];
expected: ReturnType<typeof buildInlineKeyboard>;
}> = [
{
name: "empty input",
input: undefined,
@@ -141,7 +145,7 @@ describe("buildInlineKeyboard", () => {
inline_keyboard: [[{ text: "Ok", callback_data: "cmd:ok" }]],
},
},
] as const;
];
for (const testCase of cases) {
expect(buildInlineKeyboard(testCase.input), testCase.name).toEqual(testCase.expected);
}
@@ -539,7 +543,12 @@ describe("sendMessageTelegram", () => {
it("applies reply markup and thread options to split video-note sends", async () => {
const chatId = "123";
const cases = [
const cases: Array<{
text: string;
options: Partial<NonNullable<Parameters<typeof sendMessageTelegram>[2]>>;
expectedVideoNote: Record<string, unknown>;
expectedMessage: Record<string, unknown>;
}> = [
{
text: "Check this out",
options: {
@@ -564,7 +573,7 @@ describe("sendMessageTelegram", () => {
reply_to_message_id: 999,
},
},
] as const;
];
for (const testCase of cases) {
const sendVideoNote = vi.fn().mockResolvedValue({
@@ -681,7 +690,19 @@ describe("sendMessageTelegram", () => {
});
it("routes audio media to sendAudio/sendVoice based on voice compatibility", async () => {
const cases = [
const cases: Array<{
name: string;
chatId: string;
text: string;
mediaUrl: string;
contentType: string;
fileName: string;
asVoice?: boolean;
messageThreadId?: number;
replyToMessageId?: number;
expectedMethod: "sendAudio" | "sendVoice";
expectedOptions: Record<string, unknown>;
}> = [
{
name: "default audio send",
chatId: "123",
@@ -732,7 +753,7 @@ describe("sendMessageTelegram", () => {
expectedMethod: "sendVoice" as const,
expectedOptions: { caption: "caption", parse_mode: "HTML" },
},
] as const;
];
for (const testCase of cases) {
const sendAudio = vi.fn().mockResolvedValue({
@@ -1210,12 +1231,22 @@ describe("editMessageTelegram", () => {
});
it("handles button payload + parse fallback behavior", async () => {
const cases = [
const cases: Array<{
name: string;
setup: () => {
text: string;
buttons: Parameters<typeof buildInlineKeyboard>[0];
};
expectedCalls: number;
firstExpectNoReplyMarkup?: boolean;
firstExpectReplyMarkup?: Record<string, unknown>;
secondExpectReplyMarkup?: Record<string, unknown>;
}> = [
{
name: "buttons undefined keeps existing keyboard",
setup: () => {
botApi.editMessageText.mockResolvedValue({ message_id: 1, chat: { id: "123" } });
return { text: "hi", buttons: undefined as [] | undefined };
return { text: "hi", buttons: undefined };
},
expectedCalls: 1,
firstExpectNoReplyMarkup: true,
@@ -1224,7 +1255,7 @@ describe("editMessageTelegram", () => {
name: "buttons empty clears keyboard",
setup: () => {
botApi.editMessageText.mockResolvedValue({ message_id: 1, chat: { id: "123" } });
return { text: "hi", buttons: [] as [] };
return { text: "hi", buttons: [] };
},
expectedCalls: 1,
firstExpectReplyMarkup: { inline_keyboard: [] },
@@ -1235,13 +1266,13 @@ describe("editMessageTelegram", () => {
botApi.editMessageText
.mockRejectedValueOnce(new Error("400: Bad Request: can't parse entities"))
.mockResolvedValueOnce({ message_id: 1, chat: { id: "123" } });
return { text: "<bad> html", buttons: [] as [] };
return { text: "<bad> html", buttons: [] };
},
expectedCalls: 2,
firstExpectReplyMarkup: { inline_keyboard: [] },
secondExpectReplyMarkup: { inline_keyboard: [] },
},
] as const;
];
for (const testCase of cases) {
botApi.editMessageText.mockReset();