test: speed up history and cron suites

This commit is contained in:
Peter Steinberger
2026-01-23 07:34:39 +00:00
parent ff78e9a564
commit 86a341be62
4 changed files with 26 additions and 30 deletions

View File

@@ -1,7 +1,21 @@
export const MAX_PAYLOAD_BYTES = 512 * 1024; // cap incoming frame size export const MAX_PAYLOAD_BYTES = 512 * 1024; // cap incoming frame size
export const MAX_BUFFERED_BYTES = 1.5 * 1024 * 1024; // per-connection send buffer limit export const MAX_BUFFERED_BYTES = 1.5 * 1024 * 1024; // per-connection send buffer limit
export const MAX_CHAT_HISTORY_MESSAGES_BYTES = 6 * 1024 * 1024; // keep history responses comfortably under client WS limits const DEFAULT_MAX_CHAT_HISTORY_MESSAGES_BYTES = 6 * 1024 * 1024; // keep history responses comfortably under client WS limits
let maxChatHistoryMessagesBytes = DEFAULT_MAX_CHAT_HISTORY_MESSAGES_BYTES;
export const getMaxChatHistoryMessagesBytes = () => maxChatHistoryMessagesBytes;
export const __setMaxChatHistoryMessagesBytesForTest = (value?: number) => {
if (!process.env.VITEST && process.env.NODE_ENV !== "test") return;
if (value === undefined) {
maxChatHistoryMessagesBytes = DEFAULT_MAX_CHAT_HISTORY_MESSAGES_BYTES;
return;
}
if (Number.isFinite(value) && value > 0) {
maxChatHistoryMessagesBytes = value;
}
};
export const DEFAULT_HANDSHAKE_TIMEOUT_MS = 10_000; export const DEFAULT_HANDSHAKE_TIMEOUT_MS = 10_000;
export const getHandshakeTimeoutMs = () => { export const getHandshakeTimeoutMs = () => {
if (process.env.VITEST && process.env.CLAWDBOT_TEST_HANDSHAKE_TIMEOUT_MS) { if (process.env.VITEST && process.env.CLAWDBOT_TEST_HANDSHAKE_TIMEOUT_MS) {

View File

@@ -28,7 +28,7 @@ import {
validateChatInjectParams, validateChatInjectParams,
validateChatSendParams, validateChatSendParams,
} from "../protocol/index.js"; } from "../protocol/index.js";
import { MAX_CHAT_HISTORY_MESSAGES_BYTES } from "../server-constants.js"; import { getMaxChatHistoryMessagesBytes } from "../server-constants.js";
import { import {
capArrayByJsonBytes, capArrayByJsonBytes,
loadSessionEntry, loadSessionEntry,
@@ -66,7 +66,7 @@ export const chatHandlers: GatewayRequestHandlers = {
const max = Math.min(hardMax, requested); const max = Math.min(hardMax, requested);
const sliced = rawMessages.length > max ? rawMessages.slice(-max) : rawMessages; const sliced = rawMessages.length > max ? rawMessages.slice(-max) : rawMessages;
const sanitized = stripEnvelopeFromMessages(sliced); const sanitized = stripEnvelopeFromMessages(sliced);
const capped = capArrayByJsonBytes(sanitized, MAX_CHAT_HISTORY_MESSAGES_BYTES).items; const capped = capArrayByJsonBytes(sanitized, getMaxChatHistoryMessagesBytes()).items;
let thinkingLevel = entry?.thinkingLevel; let thinkingLevel = entry?.thinkingLevel;
if (!thinkingLevel) { if (!thinkingLevel) {
const configured = cfg.agents?.defaults?.thinkingDefault; const configured = cfg.agents?.defaults?.thinkingDefault;

View File

@@ -14,6 +14,7 @@ import {
testState, testState,
writeSessionStore, writeSessionStore,
} from "./test-helpers.js"; } from "./test-helpers.js";
import { __setMaxChatHistoryMessagesBytesForTest } from "./server-constants.js";
installGatewayTestHooks({ scope: "suite" }); installGatewayTestHooks({ scope: "suite" });
async function waitFor(condition: () => boolean, timeoutMs = 1500) { async function waitFor(condition: () => boolean, timeoutMs = 1500) {
const deadline = Date.now() + timeoutMs; const deadline = Date.now() + timeoutMs;
@@ -52,6 +53,8 @@ describe("gateway server chat", () => {
spy.mockResolvedValue(undefined); spy.mockResolvedValue(undefined);
}; };
try { try {
const historyMaxBytes = 192 * 1024;
__setMaxChatHistoryMessagesBytesForTest(historyMaxBytes);
await connectOk(ws); await connectOk(ws);
const sessionDir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-gw-")); const sessionDir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-gw-"));
tempDirs.push(sessionDir); tempDirs.push(sessionDir);
@@ -66,9 +69,9 @@ describe("gateway server chat", () => {
}; };
await writeStore({ main: { sessionId: "sess-main", updatedAt: Date.now() } }); await writeStore({ main: { sessionId: "sess-main", updatedAt: Date.now() } });
const bigText = "x".repeat(155_000); const bigText = "x".repeat(4_000);
const largeLines: string[] = []; const largeLines: string[] = [];
for (let i = 0; i < 40; i += 1) { for (let i = 0; i < 60; i += 1) {
largeLines.push( largeLines.push(
JSON.stringify({ JSON.stringify({
message: { message: {
@@ -91,7 +94,7 @@ describe("gateway server chat", () => {
expect(cappedRes.ok).toBe(true); expect(cappedRes.ok).toBe(true);
const cappedMsgs = cappedRes.payload?.messages ?? []; const cappedMsgs = cappedRes.payload?.messages ?? [];
const bytes = Buffer.byteLength(JSON.stringify(cappedMsgs), "utf8"); const bytes = Buffer.byteLength(JSON.stringify(cappedMsgs), "utf8");
expect(bytes).toBeLessThanOrEqual(6 * 1024 * 1024); expect(bytes).toBeLessThanOrEqual(historyMaxBytes);
expect(cappedMsgs.length).toBeLessThan(60); expect(cappedMsgs.length).toBeLessThan(60);
await writeStore({ await writeStore({
@@ -473,6 +476,7 @@ describe("gateway server chat", () => {
: undefined; : undefined;
expect(run2).toBe("idem-2"); expect(run2).toBe("idem-2");
} finally { } finally {
__setMaxChatHistoryMessagesBytesForTest();
testState.sessionStorePath = undefined; testState.sessionStorePath = undefined;
sessionStoreSaveDelayMs.value = 0; sessionStoreSaveDelayMs.value = 0;
ws.close(); ws.close();

View File

@@ -5,7 +5,6 @@ import { describe, expect, test } from "vitest";
import { import {
connectOk, connectOk,
installGatewayTestHooks, installGatewayTestHooks,
onceMessage,
rpcReq, rpcReq,
startServerWithClient, startServerWithClient,
testState, testState,
@@ -36,22 +35,6 @@ async function rmTempDir(dir: string) {
await fs.rm(dir, { recursive: true, force: true }); await fs.rm(dir, { recursive: true, force: true });
} }
async function waitForCronFinished(
ws: { send: (data: string) => void },
jobId: string,
timeoutMs = 20_000,
) {
await onceMessage(
ws as never,
(o) =>
o.type === "event" &&
o.event === "cron" &&
o.payload?.action === "finished" &&
o.payload?.jobId === jobId,
timeoutMs,
);
}
async function waitForNonEmptyFile(pathname: string, timeoutMs = 2000) { async function waitForNonEmptyFile(pathname: string, timeoutMs = 2000) {
const startedAt = process.hrtime.bigint(); const startedAt = process.hrtime.bigint();
for (;;) { for (;;) {
@@ -307,13 +290,10 @@ describe("gateway server cron", () => {
const jobId = typeof jobIdValue === "string" ? jobIdValue : ""; const jobId = typeof jobIdValue === "string" ? jobIdValue : "";
expect(jobId.length > 0).toBe(true); expect(jobId.length > 0).toBe(true);
const finishedP = waitForCronFinished(ws, jobId);
const runRes = await rpcReq(ws, "cron.run", { id: jobId, mode: "force" }, 20_000); const runRes = await rpcReq(ws, "cron.run", { id: jobId, mode: "force" }, 20_000);
expect(runRes.ok).toBe(true); expect(runRes.ok).toBe(true);
await finishedP;
const logPath = path.join(dir, "cron", "runs", `${jobId}.jsonl`); const logPath = path.join(dir, "cron", "runs", `${jobId}.jsonl`);
const raw = await waitForNonEmptyFile(logPath); const raw = await waitForNonEmptyFile(logPath, 5000);
const line = raw const line = raw
.split("\n") .split("\n")
.map((l) => l.trim()) .map((l) => l.trim())
@@ -359,9 +339,7 @@ describe("gateway server cron", () => {
const autoJobId = typeof autoJobIdValue === "string" ? autoJobIdValue : ""; const autoJobId = typeof autoJobIdValue === "string" ? autoJobIdValue : "";
expect(autoJobId.length > 0).toBe(true); expect(autoJobId.length > 0).toBe(true);
await waitForCronFinished(ws, autoJobId); await waitForNonEmptyFile(path.join(dir, "cron", "runs", `${autoJobId}.jsonl`), 5000);
await waitForNonEmptyFile(path.join(dir, "cron", "runs", `${autoJobId}.jsonl`));
const autoEntries = (await rpcReq(ws, "cron.runs", { id: autoJobId, limit: 10 })).payload as const autoEntries = (await rpcReq(ws, "cron.runs", { id: autoJobId, limit: 10 })).payload as
| { entries?: Array<{ jobId?: unknown }> } | { entries?: Array<{ jobId?: unknown }> }
| undefined; | undefined;