mirror of
https://github.com/openclaw/openclaw.git
synced 2026-04-19 07:47:28 +00:00
test(perf): reuse cron heartbeat delivery temp homes per suite
This commit is contained in:
@@ -1,17 +1,67 @@
|
|||||||
import "./isolated-agent.mocks.js";
|
import "./isolated-agent.mocks.js";
|
||||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
import fs from "node:fs/promises";
|
||||||
|
import os from "node:os";
|
||||||
|
import path from "node:path";
|
||||||
|
import { afterAll, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
|
||||||
import { runEmbeddedPiAgent } from "../agents/pi-embedded.js";
|
import { runEmbeddedPiAgent } from "../agents/pi-embedded.js";
|
||||||
import { runSubagentAnnounceFlow } from "../agents/subagent-announce.js";
|
import { runSubagentAnnounceFlow } from "../agents/subagent-announce.js";
|
||||||
import type { CliDeps } from "../cli/deps.js";
|
import type { CliDeps } from "../cli/deps.js";
|
||||||
import { runCronIsolatedAgentTurn } from "./isolated-agent.js";
|
import { runCronIsolatedAgentTurn } from "./isolated-agent.js";
|
||||||
import {
|
import { makeCfg, makeJob, writeSessionStore } from "./isolated-agent.test-harness.js";
|
||||||
makeCfg,
|
|
||||||
makeJob,
|
|
||||||
withTempCronHome,
|
|
||||||
writeSessionStore,
|
|
||||||
} from "./isolated-agent.test-harness.js";
|
|
||||||
import { setupIsolatedAgentTurnMocks } from "./isolated-agent.test-setup.js";
|
import { setupIsolatedAgentTurnMocks } from "./isolated-agent.test-setup.js";
|
||||||
|
|
||||||
|
let tempRoot = "";
|
||||||
|
let tempHomeId = 0;
|
||||||
|
|
||||||
|
async function withTempHome<T>(fn: (home: string) => Promise<T>): Promise<T> {
|
||||||
|
if (!tempRoot) {
|
||||||
|
throw new Error("temp root not initialized");
|
||||||
|
}
|
||||||
|
const home = path.join(tempRoot, `case-${tempHomeId++}`);
|
||||||
|
await fs.mkdir(path.join(home, ".openclaw", "agents", "main", "sessions"), {
|
||||||
|
recursive: true,
|
||||||
|
});
|
||||||
|
const snapshot = {
|
||||||
|
HOME: process.env.HOME,
|
||||||
|
USERPROFILE: process.env.USERPROFILE,
|
||||||
|
HOMEDRIVE: process.env.HOMEDRIVE,
|
||||||
|
HOMEPATH: process.env.HOMEPATH,
|
||||||
|
OPENCLAW_HOME: process.env.OPENCLAW_HOME,
|
||||||
|
OPENCLAW_STATE_DIR: process.env.OPENCLAW_STATE_DIR,
|
||||||
|
};
|
||||||
|
process.env.HOME = home;
|
||||||
|
process.env.USERPROFILE = home;
|
||||||
|
delete process.env.OPENCLAW_HOME;
|
||||||
|
process.env.OPENCLAW_STATE_DIR = path.join(home, ".openclaw");
|
||||||
|
|
||||||
|
if (process.platform === "win32") {
|
||||||
|
const driveMatch = home.match(/^([A-Za-z]:)(.*)$/);
|
||||||
|
if (driveMatch) {
|
||||||
|
process.env.HOMEDRIVE = driveMatch[1];
|
||||||
|
process.env.HOMEPATH = driveMatch[2] || "\\";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
return await fn(home);
|
||||||
|
} finally {
|
||||||
|
const restoreKey = (key: keyof typeof snapshot) => {
|
||||||
|
const value = snapshot[key];
|
||||||
|
if (value === undefined) {
|
||||||
|
delete process.env[key];
|
||||||
|
} else {
|
||||||
|
process.env[key] = value;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
restoreKey("HOME");
|
||||||
|
restoreKey("USERPROFILE");
|
||||||
|
restoreKey("HOMEDRIVE");
|
||||||
|
restoreKey("HOMEPATH");
|
||||||
|
restoreKey("OPENCLAW_HOME");
|
||||||
|
restoreKey("OPENCLAW_STATE_DIR");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async function createTelegramDeliveryFixture(home: string): Promise<{
|
async function createTelegramDeliveryFixture(home: string): Promise<{
|
||||||
storePath: string;
|
storePath: string;
|
||||||
deps: CliDeps;
|
deps: CliDeps;
|
||||||
@@ -70,12 +120,23 @@ async function runTelegramAnnounceTurn(params: {
|
|||||||
}
|
}
|
||||||
|
|
||||||
describe("runCronIsolatedAgentTurn", () => {
|
describe("runCronIsolatedAgentTurn", () => {
|
||||||
|
beforeAll(async () => {
|
||||||
|
tempRoot = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-cron-heartbeat-suite-"));
|
||||||
|
});
|
||||||
|
|
||||||
|
afterAll(async () => {
|
||||||
|
if (!tempRoot) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
await fs.rm(tempRoot, { recursive: true, force: true });
|
||||||
|
});
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
setupIsolatedAgentTurnMocks({ fast: true });
|
setupIsolatedAgentTurnMocks({ fast: true });
|
||||||
});
|
});
|
||||||
|
|
||||||
it("does not fan out telegram cron delivery across allowFrom entries", async () => {
|
it("does not fan out telegram cron delivery across allowFrom entries", async () => {
|
||||||
await withTempCronHome(async (home) => {
|
await withTempHome(async (home) => {
|
||||||
const { storePath, deps } = await createTelegramDeliveryFixture(home);
|
const { storePath, deps } = await createTelegramDeliveryFixture(home);
|
||||||
mockEmbeddedAgentPayloads([
|
mockEmbeddedAgentPayloads([
|
||||||
{ text: "HEARTBEAT_OK", mediaUrl: "https://example.com/img.png" },
|
{ text: "HEARTBEAT_OK", mediaUrl: "https://example.com/img.png" },
|
||||||
@@ -117,7 +178,7 @@ describe("runCronIsolatedAgentTurn", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("handles media heartbeat delivery and announce cleanup modes", async () => {
|
it("handles media heartbeat delivery and announce cleanup modes", async () => {
|
||||||
await withTempCronHome(async (home) => {
|
await withTempHome(async (home) => {
|
||||||
const { storePath, deps } = await createTelegramDeliveryFixture(home);
|
const { storePath, deps } = await createTelegramDeliveryFixture(home);
|
||||||
|
|
||||||
// Media should still be delivered even if text is just HEARTBEAT_OK.
|
// Media should still be delivered even if text is just HEARTBEAT_OK.
|
||||||
@@ -200,7 +261,7 @@ describe("runCronIsolatedAgentTurn", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("skips structured outbound delivery when timeout abort is already set", async () => {
|
it("skips structured outbound delivery when timeout abort is already set", async () => {
|
||||||
await withTempCronHome(async (home) => {
|
await withTempHome(async (home) => {
|
||||||
const { storePath, deps } = await createTelegramDeliveryFixture(home);
|
const { storePath, deps } = await createTelegramDeliveryFixture(home);
|
||||||
const controller = new AbortController();
|
const controller = new AbortController();
|
||||||
controller.abort("cron: job execution timed out");
|
controller.abort("cron: job execution timed out");
|
||||||
@@ -224,7 +285,7 @@ describe("runCronIsolatedAgentTurn", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("uses a unique announce childRunId for each cron run", async () => {
|
it("uses a unique announce childRunId for each cron run", async () => {
|
||||||
await withTempCronHome(async (home) => {
|
await withTempHome(async (home) => {
|
||||||
const storePath = await writeSessionStore(home, {
|
const storePath = await writeSessionStore(home, {
|
||||||
lastProvider: "telegram",
|
lastProvider: "telegram",
|
||||||
lastChannel: "telegram",
|
lastChannel: "telegram",
|
||||||
@@ -251,23 +312,30 @@ describe("runCronIsolatedAgentTurn", () => {
|
|||||||
const job = makeJob({ kind: "agentTurn", message: "do it" });
|
const job = makeJob({ kind: "agentTurn", message: "do it" });
|
||||||
job.delivery = { mode: "announce", channel: "last" };
|
job.delivery = { mode: "announce", channel: "last" };
|
||||||
|
|
||||||
await runCronIsolatedAgentTurn({
|
const nowSpy = vi.spyOn(Date, "now");
|
||||||
cfg,
|
let now = Date.now();
|
||||||
deps,
|
nowSpy.mockImplementation(() => now);
|
||||||
job,
|
try {
|
||||||
message: "do it",
|
await runCronIsolatedAgentTurn({
|
||||||
sessionKey: "cron:job-1",
|
cfg,
|
||||||
lane: "cron",
|
deps,
|
||||||
});
|
job,
|
||||||
await new Promise((resolve) => setTimeout(resolve, 5));
|
message: "do it",
|
||||||
await runCronIsolatedAgentTurn({
|
sessionKey: "cron:job-1",
|
||||||
cfg,
|
lane: "cron",
|
||||||
deps,
|
});
|
||||||
job,
|
now += 5;
|
||||||
message: "do it",
|
await runCronIsolatedAgentTurn({
|
||||||
sessionKey: "cron:job-1",
|
cfg,
|
||||||
lane: "cron",
|
deps,
|
||||||
});
|
job,
|
||||||
|
message: "do it",
|
||||||
|
sessionKey: "cron:job-1",
|
||||||
|
lane: "cron",
|
||||||
|
});
|
||||||
|
} finally {
|
||||||
|
nowSpy.mockRestore();
|
||||||
|
}
|
||||||
|
|
||||||
expect(runSubagentAnnounceFlow).toHaveBeenCalledTimes(2);
|
expect(runSubagentAnnounceFlow).toHaveBeenCalledTimes(2);
|
||||||
const firstArgs = vi.mocked(runSubagentAnnounceFlow).mock.calls[0]?.[0] as
|
const firstArgs = vi.mocked(runSubagentAnnounceFlow).mock.calls[0]?.[0] as
|
||||||
|
|||||||
Reference in New Issue
Block a user