test: stabilize infra tests

This commit is contained in:
Sebastian
2026-02-16 22:37:34 -05:00
parent 759c7fc18e
commit f7d2e15a2e
5 changed files with 37 additions and 18 deletions

View File

@@ -72,7 +72,7 @@ Docs: https://docs.openclaw.ai
- Sessions/Maintenance: archive transcripts when pruning stale sessions, clean expired media in subdirectories, and purge `.deleted` transcript archives after the prune window to prevent disk leaks. (#18538)
- Infra/Fetch: ensure foreign abort-signal listener cleanup never masks original fetch successes/failures, while still preventing detached-finally unhandled rejection noise in `wrapFetchWithAbortSignal`. Thanks @Jackten.
- Heartbeat: allow suppressing tool error warning payloads during heartbeat runs via a new heartbeat config flag. (#18497) Thanks @thewilloftheshadow.
- Heartbeat: include sender metadata (From/To/Provider) in heartbeat prompts so model context matches the delivery target. (#18532)
- Heartbeat: include sender metadata (From/To/Provider) in heartbeat prompts so model context matches the delivery target. (#18532) Thanks @dinakars777.
- Heartbeat/Telegram: strip configured `responsePrefix` before heartbeat ack detection (with boundary-safe matching) so prefixed `HEARTBEAT_OK` replies are correctly suppressed instead of leaking into DMs. (#18602)
## 2026.2.15

View File

@@ -2,12 +2,12 @@ import fs from "node:fs/promises";
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 { telegramPlugin } from "../../extensions/telegram/src/channel.js";
import { setTelegramRuntime } from "../../extensions/telegram/src/runtime.js";
import { whatsappPlugin } from "../../extensions/whatsapp/src/channel.js";
import { setWhatsAppRuntime } from "../../extensions/whatsapp/src/runtime.js";
import * as replyModule from "../auto-reply/reply.js";
import type { OpenClawConfig } from "../config/config.js";
import { resolveAgentMainSessionKey, resolveMainSessionKey } from "../config/sessions.js";
import { setActivePluginRegistry } from "../plugins/runtime.js";
import { createPluginRuntime } from "../plugins/runtime/index.js";
@@ -75,7 +75,10 @@ afterEach(() => {
});
describe("runHeartbeatOnce heartbeat model override", () => {
async function runDefaultsHeartbeat(params: { model?: string }) {
async function runDefaultsHeartbeat(params: {
model?: string;
suppressToolErrorWarnings?: boolean;
}) {
return withHeartbeatFixture(async ({ tmpDir, storePath, seedSession }) => {
const cfg: OpenClawConfig = {
agents: {
@@ -85,6 +88,7 @@ describe("runHeartbeatOnce heartbeat model override", () => {
every: "5m",
target: "whatsapp",
model: params.model,
suppressToolErrorWarnings: params.suppressToolErrorWarnings,
},
},
},
@@ -121,6 +125,16 @@ describe("runHeartbeatOnce heartbeat model override", () => {
);
});
it("passes suppressToolErrorWarnings when configured", async () => {
const replyOpts = await runDefaultsHeartbeat({ suppressToolErrorWarnings: true });
expect(replyOpts).toEqual(
expect.objectContaining({
isHeartbeat: true,
suppressToolErrorWarnings: true,
}),
);
});
it("passes per-agent heartbeat model override (merged with defaults)", async () => {
await withHeartbeatFixture(async ({ storePath, seedSession }) => {
const cfg: OpenClawConfig = {

View File

@@ -70,7 +70,8 @@ vi.mock("./manager.js", () => ({
import { QmdMemoryManager } from "./qmd-manager.js";
import { getMemorySearchManager } from "./search-manager.js";
const createQmdManagerMock = vi.mocked(QmdMemoryManager.create.bind(QmdMemoryManager));
// eslint-disable-next-line @typescript-eslint/unbound-method -- mocked static function
const createQmdManagerMock = vi.mocked(QmdMemoryManager.create);
type SearchManagerResult = Awaited<ReturnType<typeof getMemorySearchManager>>;
type SearchManager = NonNullable<SearchManagerResult["manager"]>;

View File

@@ -63,7 +63,8 @@ describe("runCommandWithTimeout", () => {
},
);
expect(result.code).toBe(0);
expect(result.signal).toBeNull();
expect(result.code ?? 0).toBe(0);
expect(result.termination).toBe("exit");
expect(result.noOutputTimedOut).toBe(false);
expect(result.stdout.length).toBeGreaterThanOrEqual(2);

View File

@@ -1,20 +1,23 @@
import { describe, expect, it, vi } from "vitest";
import { startTelegramWebhook } from "./webhook.js";
const handlerSpy = vi.fn(
(_req: unknown, res: { writeHead: (status: number) => void; end: (body?: string) => void }) => {
res.writeHead(200);
res.end("ok");
},
const handlerSpy = vi.hoisted(() =>
vi.fn(
(_req: unknown, res: { writeHead: (status: number) => void; end: (body?: string) => void }) => {
res.writeHead(200);
res.end("ok");
},
),
);
const setWebhookSpy = vi.hoisted(() => vi.fn());
const stopSpy = vi.hoisted(() => vi.fn());
const webhookCallbackSpy = vi.hoisted(() => vi.fn(() => handlerSpy));
const createTelegramBotSpy = vi.hoisted(() =>
vi.fn(() => ({
api: { setWebhook: setWebhookSpy },
stop: stopSpy,
})),
);
const setWebhookSpy = vi.fn();
const stopSpy = vi.fn();
const webhookCallbackSpy = vi.fn(() => handlerSpy);
const createTelegramBotSpy = vi.fn(() => ({
api: { setWebhook: setWebhookSpy },
stop: stopSpy,
}));
vi.mock("grammy", async (importOriginal) => {
const actual = await importOriginal<typeof import("grammy")>();