test: speed up history and cron suites
This commit is contained in:
@@ -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) {
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
Reference in New Issue
Block a user