mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-11 04:04:32 +00:00
refactor(agent): dedupe harness and command workflows
This commit is contained in:
@@ -1,39 +1,40 @@
|
||||
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 { describe, expect, it, vi } from "vitest";
|
||||
import { CronService } from "./service.js";
|
||||
import {
|
||||
createCronStoreHarness,
|
||||
createNoopLogger,
|
||||
installCronTestHooks,
|
||||
} from "./service.test-harness.js";
|
||||
|
||||
const noopLogger = {
|
||||
debug: vi.fn(),
|
||||
info: vi.fn(),
|
||||
warn: vi.fn(),
|
||||
error: vi.fn(),
|
||||
};
|
||||
|
||||
async function makeStorePath() {
|
||||
const dir = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-cron-"));
|
||||
return {
|
||||
storePath: path.join(dir, "cron", "jobs.json"),
|
||||
cleanup: async () => {
|
||||
await fs.rm(dir, { recursive: true, force: true });
|
||||
},
|
||||
};
|
||||
}
|
||||
const noopLogger = createNoopLogger();
|
||||
const { makeStorePath } = createCronStoreHarness({ prefix: "openclaw-cron-" });
|
||||
installCronTestHooks({
|
||||
logger: noopLogger,
|
||||
baseTimeIso: "2025-12-13T17:00:00.000Z",
|
||||
});
|
||||
|
||||
describe("CronService restart catch-up", () => {
|
||||
beforeEach(() => {
|
||||
vi.useFakeTimers();
|
||||
vi.setSystemTime(new Date("2025-12-13T17:00:00.000Z"));
|
||||
noopLogger.debug.mockClear();
|
||||
noopLogger.info.mockClear();
|
||||
noopLogger.warn.mockClear();
|
||||
noopLogger.error.mockClear();
|
||||
});
|
||||
async function writeStoreJobs(storePath: string, jobs: unknown[]) {
|
||||
await fs.mkdir(path.dirname(storePath), { recursive: true });
|
||||
await fs.writeFile(storePath, JSON.stringify({ version: 1, jobs }, null, 2), "utf-8");
|
||||
}
|
||||
|
||||
afterEach(() => {
|
||||
vi.useRealTimers();
|
||||
});
|
||||
function createRestartCronService(params: {
|
||||
storePath: string;
|
||||
enqueueSystemEvent: ReturnType<typeof vi.fn>;
|
||||
requestHeartbeatNow: ReturnType<typeof vi.fn>;
|
||||
}) {
|
||||
return new CronService({
|
||||
storePath: params.storePath,
|
||||
cronEnabled: true,
|
||||
log: noopLogger,
|
||||
enqueueSystemEvent: params.enqueueSystemEvent,
|
||||
requestHeartbeatNow: params.requestHeartbeatNow,
|
||||
runIsolatedAgentJob: vi.fn(async () => ({ status: "ok" })),
|
||||
});
|
||||
}
|
||||
|
||||
it("executes an overdue recurring job immediately on start", async () => {
|
||||
const store = await makeStorePath();
|
||||
@@ -43,44 +44,29 @@ describe("CronService restart catch-up", () => {
|
||||
const dueAt = Date.parse("2025-12-13T15:00:00.000Z");
|
||||
const lastRunAt = Date.parse("2025-12-12T15:00:00.000Z");
|
||||
|
||||
await fs.mkdir(path.dirname(store.storePath), { recursive: true });
|
||||
await fs.writeFile(
|
||||
store.storePath,
|
||||
JSON.stringify(
|
||||
{
|
||||
version: 1,
|
||||
jobs: [
|
||||
{
|
||||
id: "restart-overdue-job",
|
||||
name: "daily digest",
|
||||
enabled: true,
|
||||
createdAtMs: Date.parse("2025-12-10T12:00:00.000Z"),
|
||||
updatedAtMs: Date.parse("2025-12-12T15:00:00.000Z"),
|
||||
schedule: { kind: "cron", expr: "0 15 * * *", tz: "UTC" },
|
||||
sessionTarget: "main",
|
||||
wakeMode: "next-heartbeat",
|
||||
payload: { kind: "systemEvent", text: "digest now" },
|
||||
state: {
|
||||
nextRunAtMs: dueAt,
|
||||
lastRunAtMs: lastRunAt,
|
||||
lastStatus: "ok",
|
||||
},
|
||||
},
|
||||
],
|
||||
await writeStoreJobs(store.storePath, [
|
||||
{
|
||||
id: "restart-overdue-job",
|
||||
name: "daily digest",
|
||||
enabled: true,
|
||||
createdAtMs: Date.parse("2025-12-10T12:00:00.000Z"),
|
||||
updatedAtMs: Date.parse("2025-12-12T15:00:00.000Z"),
|
||||
schedule: { kind: "cron", expr: "0 15 * * *", tz: "UTC" },
|
||||
sessionTarget: "main",
|
||||
wakeMode: "next-heartbeat",
|
||||
payload: { kind: "systemEvent", text: "digest now" },
|
||||
state: {
|
||||
nextRunAtMs: dueAt,
|
||||
lastRunAtMs: lastRunAt,
|
||||
lastStatus: "ok",
|
||||
},
|
||||
null,
|
||||
2,
|
||||
),
|
||||
"utf-8",
|
||||
);
|
||||
},
|
||||
]);
|
||||
|
||||
const cron = new CronService({
|
||||
const cron = createRestartCronService({
|
||||
storePath: store.storePath,
|
||||
cronEnabled: true,
|
||||
log: noopLogger,
|
||||
enqueueSystemEvent,
|
||||
requestHeartbeatNow,
|
||||
runIsolatedAgentJob: vi.fn(async () => ({ status: "ok" })),
|
||||
});
|
||||
|
||||
await cron.start();
|
||||
@@ -109,43 +95,28 @@ describe("CronService restart catch-up", () => {
|
||||
const dueAt = Date.parse("2025-12-13T16:00:00.000Z");
|
||||
const staleRunningAt = Date.parse("2025-12-13T16:30:00.000Z");
|
||||
|
||||
await fs.mkdir(path.dirname(store.storePath), { recursive: true });
|
||||
await fs.writeFile(
|
||||
store.storePath,
|
||||
JSON.stringify(
|
||||
{
|
||||
version: 1,
|
||||
jobs: [
|
||||
{
|
||||
id: "restart-stale-running",
|
||||
name: "daily stale marker",
|
||||
enabled: true,
|
||||
createdAtMs: Date.parse("2025-12-10T12:00:00.000Z"),
|
||||
updatedAtMs: Date.parse("2025-12-13T16:30:00.000Z"),
|
||||
schedule: { kind: "cron", expr: "0 16 * * *", tz: "UTC" },
|
||||
sessionTarget: "main",
|
||||
wakeMode: "next-heartbeat",
|
||||
payload: { kind: "systemEvent", text: "resume stale marker" },
|
||||
state: {
|
||||
nextRunAtMs: dueAt,
|
||||
runningAtMs: staleRunningAt,
|
||||
},
|
||||
},
|
||||
],
|
||||
await writeStoreJobs(store.storePath, [
|
||||
{
|
||||
id: "restart-stale-running",
|
||||
name: "daily stale marker",
|
||||
enabled: true,
|
||||
createdAtMs: Date.parse("2025-12-10T12:00:00.000Z"),
|
||||
updatedAtMs: Date.parse("2025-12-13T16:30:00.000Z"),
|
||||
schedule: { kind: "cron", expr: "0 16 * * *", tz: "UTC" },
|
||||
sessionTarget: "main",
|
||||
wakeMode: "next-heartbeat",
|
||||
payload: { kind: "systemEvent", text: "resume stale marker" },
|
||||
state: {
|
||||
nextRunAtMs: dueAt,
|
||||
runningAtMs: staleRunningAt,
|
||||
},
|
||||
null,
|
||||
2,
|
||||
),
|
||||
"utf-8",
|
||||
);
|
||||
},
|
||||
]);
|
||||
|
||||
const cron = new CronService({
|
||||
const cron = createRestartCronService({
|
||||
storePath: store.storePath,
|
||||
cronEnabled: true,
|
||||
log: noopLogger,
|
||||
enqueueSystemEvent,
|
||||
requestHeartbeatNow,
|
||||
runIsolatedAgentJob: vi.fn(async () => ({ status: "ok" })),
|
||||
});
|
||||
|
||||
await cron.start();
|
||||
|
||||
Reference in New Issue
Block a user