From 0c1a52307c2580e3378c9965871a53acbcd273e1 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Sun, 22 Feb 2026 08:03:05 +0000 Subject: [PATCH] fix: align draft/outbound typings and tests --- src/agents/openclaw-gateway-tool.e2e.test.ts | 2 +- src/browser/extension-relay-auth.test.ts | 4 ++-- src/channels/draft-stream-controls.test.ts | 2 +- src/channels/draft-stream-controls.ts | 5 ++++- src/cli/channel-auth.ts | 14 +++++++++++--- src/discord/draft-stream.ts | 4 +++- src/infra/npm-pack-install.test.ts | 8 ++------ src/infra/outbound/outbound.test.ts | 2 -- src/infra/outbound/targets.ts | 6 ++++-- .../bot-message-context.test-harness.ts | 19 ++++++++++--------- src/telegram/draft-stream.ts | 4 +++- src/tui/tui-command-handlers.test.ts | 12 ++++++++---- 12 files changed, 49 insertions(+), 33 deletions(-) diff --git a/src/agents/openclaw-gateway-tool.e2e.test.ts b/src/agents/openclaw-gateway-tool.e2e.test.ts index 768f0e9caac..ee09348a53f 100644 --- a/src/agents/openclaw-gateway-tool.e2e.test.ts +++ b/src/agents/openclaw-gateway-tool.e2e.test.ts @@ -31,7 +31,7 @@ function requireGatewayTool(agentSessionKey?: string) { function expectConfigMutationCall(params: { callGatewayTool: { mock: { - calls: Array<[string, unknown, Record]>; + calls: Array; }; }; action: "config.apply" | "config.patch"; diff --git a/src/browser/extension-relay-auth.test.ts b/src/browser/extension-relay-auth.test.ts index bf57226cb22..abc25765da1 100644 --- a/src/browser/extension-relay-auth.test.ts +++ b/src/browser/extension-relay-auth.test.ts @@ -1,4 +1,4 @@ -import { createServer } from "node:http"; +import { createServer, type IncomingMessage, type ServerResponse } from "node:http"; import type { AddressInfo } from "node:net"; import { afterEach, beforeEach, describe, expect, it } from "vitest"; import { @@ -8,7 +8,7 @@ import { import { getFreePort } from "./test-port.js"; async function withRelayServer( - handler: Parameters[0], + handler: (req: IncomingMessage, res: ServerResponse) => void, run: (params: { port: number }) => Promise, ) { const port = await getFreePort(); diff --git a/src/channels/draft-stream-controls.test.ts b/src/channels/draft-stream-controls.test.ts index a8ef3ebf3a6..aafae33bd7c 100644 --- a/src/channels/draft-stream-controls.test.ts +++ b/src/channels/draft-stream-controls.test.ts @@ -51,7 +51,7 @@ describe("draft-stream-controls", () => { it("clearFinalizableDraftMessage skips invalid message ids", async () => { const deleteMessage = vi.fn(async () => {}); - await clearFinalizableDraftMessage({ + await clearFinalizableDraftMessage({ stopForClear: async () => {}, readMessageId: () => 123, clearMessageId: () => {}, diff --git a/src/channels/draft-stream-controls.ts b/src/channels/draft-stream-controls.ts index 88acd0777c3..0741f096ea9 100644 --- a/src/channels/draft-stream-controls.ts +++ b/src/channels/draft-stream-controls.ts @@ -19,7 +19,10 @@ type ClearFinalizableDraftMessageParams = StopAndClearMessageIdParams & { warnPrefix: string; }; -type FinalizableDraftLifecycleParams = ClearFinalizableDraftMessageParams & { +type FinalizableDraftLifecycleParams = Omit< + ClearFinalizableDraftMessageParams, + "stopForClear" +> & { throttleMs: number; state: FinalizableDraftStreamState; sendOrEditStreamMessage: (text: string) => Promise; diff --git a/src/cli/channel-auth.ts b/src/cli/channel-auth.ts index 7c4d68d5c6b..8b47cf4364d 100644 --- a/src/cli/channel-auth.ts +++ b/src/cli/channel-auth.ts @@ -43,10 +43,14 @@ export async function runChannelLogin( runtime: RuntimeEnv = defaultRuntime, ) { const { channelInput, plugin } = resolveChannelPluginForMode(opts, "login"); + const login = plugin.auth?.login; + if (!login) { + throw new Error(`Channel ${channelInput} does not support login`); + } // Auth-only flow: do not mutate channel config here. setVerbose(Boolean(opts.verbose)); const { cfg, accountId } = resolveAccountContext(plugin, opts); - await plugin.auth!.login({ + await login({ cfg, accountId, runtime, @@ -59,11 +63,15 @@ export async function runChannelLogout( opts: ChannelAuthOptions, runtime: RuntimeEnv = defaultRuntime, ) { - const { plugin } = resolveChannelPluginForMode(opts, "logout"); + const { channelInput, plugin } = resolveChannelPluginForMode(opts, "logout"); + const logoutAccount = plugin.gateway?.logoutAccount; + if (!logoutAccount) { + throw new Error(`Channel ${channelInput} does not support logout`); + } // Auth-only flow: resolve account + clear session state only. const { cfg, accountId } = resolveAccountContext(plugin, opts); const account = plugin.config.resolveAccount(cfg, accountId); - await plugin.gateway!.logoutAccount({ + await logoutAccount({ cfg, accountId, account, diff --git a/src/discord/draft-stream.ts b/src/discord/draft-stream.ts index 108ca09ba20..cfc1871d45a 100644 --- a/src/discord/draft-stream.ts +++ b/src/discord/draft-stream.ts @@ -114,7 +114,9 @@ export function createDiscordDraftStream(params: { streamMessageId = undefined; }, isValidMessageId: (value): value is string => typeof value === "string", - deleteMessage: (messageId) => rest.delete(Routes.channelMessage(channelId, messageId)), + deleteMessage: async (messageId) => { + await rest.delete(Routes.channelMessage(channelId, messageId)); + }, warn: params.warn, warnPrefix: "discord stream preview cleanup failed", }); diff --git a/src/infra/npm-pack-install.test.ts b/src/infra/npm-pack-install.test.ts index a0e08663b48..0b8f43b7a98 100644 --- a/src/infra/npm-pack-install.test.ts +++ b/src/infra/npm-pack-install.test.ts @@ -1,5 +1,6 @@ import { beforeEach, describe, expect, it, vi } from "vitest"; import { packNpmSpecToArchive, withTempDir } from "./install-source-utils.js"; +import type { NpmIntegrityDriftPayload } from "./npm-integrity.js"; import { installFromNpmSpecArchive } from "./npm-pack-install.js"; vi.mock("./install-source-utils.js", async (importOriginal) => { @@ -37,12 +38,7 @@ describe("installFromNpmSpecArchive", () => { const runInstall = async (overrides: { expectedIntegrity?: string; - onIntegrityDrift?: (payload: { - spec: string; - expectedIntegrity: string; - actualIntegrity: string; - resolvedSpec: string; - }) => boolean | Promise; + onIntegrityDrift?: (payload: NpmIntegrityDriftPayload) => boolean | Promise; warn?: (message: string) => void; installFromArchive: (params: { archivePath: string; diff --git a/src/infra/outbound/outbound.test.ts b/src/infra/outbound/outbound.test.ts index 8ec62fc129e..897a4a3f054 100644 --- a/src/infra/outbound/outbound.test.ts +++ b/src/infra/outbound/outbound.test.ts @@ -4,8 +4,6 @@ import path from "node:path"; import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import type { ReplyPayload } from "../../auto-reply/types.js"; import type { OpenClawConfig } from "../../config/config.js"; -import { setActivePluginRegistry } from "../../plugins/runtime.js"; -import { createTestRegistry } from "../../test-utils/channel-plugins.js"; import { typedCases } from "../../test-utils/typed-cases.js"; import { ackDelivery, diff --git a/src/infra/outbound/targets.ts b/src/infra/outbound/targets.ts index 6ce063afe75..608e62c6005 100644 --- a/src/infra/outbound/targets.ts +++ b/src/infra/outbound/targets.ts @@ -160,7 +160,7 @@ export function resolveOutboundTarget(params: { }; } - const allowFrom = + const allowFromRaw = params.allowFrom ?? (params.cfg && plugin.config.resolveAllowFrom ? plugin.config.resolveAllowFrom({ @@ -168,6 +168,7 @@ export function resolveOutboundTarget(params: { accountId: params.accountId ?? undefined, }) : undefined); + const allowFrom = allowFromRaw?.map((entry) => String(entry)); // Fall back to per-channel defaultTo when no explicit target is provided. const effectiveTo = @@ -360,12 +361,13 @@ export function resolveHeartbeatSenderContext(params: { const accountId = params.delivery.accountId ?? (provider === params.delivery.lastChannel ? params.delivery.lastAccountId : undefined); - const allowFrom = provider + const allowFromRaw = provider ? (getChannelPlugin(provider)?.config.resolveAllowFrom?.({ cfg: params.cfg, accountId, }) ?? []) : []; + const allowFrom = allowFromRaw.map((entry) => String(entry)); const sender = resolveHeartbeatSenderId({ allowFrom, diff --git a/src/telegram/bot-message-context.test-harness.ts b/src/telegram/bot-message-context.test-harness.ts index 9a1fca9b2e3..afdbbffce68 100644 --- a/src/telegram/bot-message-context.test-harness.ts +++ b/src/telegram/bot-message-context.test-harness.ts @@ -1,5 +1,9 @@ import { vi } from "vitest"; -import { buildTelegramMessageContext } from "./bot-message-context.js"; +import { + buildTelegramMessageContext, + type BuildTelegramMessageContextParams, + type TelegramMediaRef, +} from "./bot-message-context.js"; export const baseTelegramMessageContextConfig = { agents: { defaults: { model: "anthropic/claude-opus-4-5", workspace: "/tmp/openclaw" } }, @@ -9,15 +13,12 @@ export const baseTelegramMessageContextConfig = { type BuildTelegramMessageContextForTestParams = { message: Record; - allMedia?: Array>; - options?: Record; + allMedia?: TelegramMediaRef[]; + options?: BuildTelegramMessageContextParams["options"]; cfg?: Record; - resolveGroupActivation?: () => boolean | undefined; - resolveGroupRequireMention?: () => boolean; - resolveTelegramGroupConfig?: () => { - groupConfig?: { requireMention?: boolean }; - topicConfig?: unknown; - }; + resolveGroupActivation?: BuildTelegramMessageContextParams["resolveGroupActivation"]; + resolveGroupRequireMention?: BuildTelegramMessageContextParams["resolveGroupRequireMention"]; + resolveTelegramGroupConfig?: BuildTelegramMessageContextParams["resolveTelegramGroupConfig"]; }; export async function buildTelegramMessageContextForTest( diff --git a/src/telegram/draft-stream.ts b/src/telegram/draft-stream.ts index 7f9d92dc7c1..87b45f2c8fb 100644 --- a/src/telegram/draft-stream.ts +++ b/src/telegram/draft-stream.ts @@ -153,7 +153,9 @@ export function createTelegramDraftStream(params: { }, isValidMessageId: (value): value is number => typeof value === "number" && Number.isFinite(value), - deleteMessage: (messageId) => params.api.deleteMessage(chatId, messageId), + deleteMessage: async (messageId) => { + await params.api.deleteMessage(chatId, messageId); + }, onDeleteSuccess: (messageId) => { params.log?.(`telegram stream preview deleted (chat=${chatId}, message=${messageId})`); }, diff --git a/src/tui/tui-command-handlers.test.ts b/src/tui/tui-command-handlers.test.ts index c4e3d1ae3f5..c71ae8907d8 100644 --- a/src/tui/tui-command-handlers.test.ts +++ b/src/tui/tui-command-handlers.test.ts @@ -1,19 +1,23 @@ import { describe, expect, it, vi } from "vitest"; import { createCommandHandlers } from "./tui-command-handlers.js"; +type LoadHistoryMock = ReturnType & (() => Promise); +type SetActivityStatusMock = ReturnType & ((text: string) => void); + function createHarness(params?: { sendChat?: ReturnType; resetSession?: ReturnType; - loadHistory?: ReturnType; - setActivityStatus?: ReturnType; + loadHistory?: LoadHistoryMock; + setActivityStatus?: SetActivityStatusMock; }) { const sendChat = params?.sendChat ?? vi.fn().mockResolvedValue({ runId: "r1" }); const resetSession = params?.resetSession ?? vi.fn().mockResolvedValue({ ok: true }); const addUser = vi.fn(); const addSystem = vi.fn(); const requestRender = vi.fn(); - const loadHistory = params?.loadHistory ?? vi.fn().mockResolvedValue(undefined); - const setActivityStatus = params?.setActivityStatus ?? vi.fn(); + const loadHistory = + params?.loadHistory ?? (vi.fn().mockResolvedValue(undefined) as LoadHistoryMock); + const setActivityStatus = params?.setActivityStatus ?? (vi.fn() as SetActivityStatusMock); const { handleCommand } = createCommandHandlers({ client: { sendChat, resetSession } as never,