mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-19 12:04:45 +00:00
159 lines
5.0 KiB
TypeScript
159 lines
5.0 KiB
TypeScript
import { beforeEach, describe, expect, it, vi } from "vitest";
|
|
import type { CliDeps } from "../cli/deps.types.js";
|
|
import type { OpenClawConfig } from "../config/types.openclaw.js";
|
|
import type { CronServiceContract } from "../cron/service-contract.js";
|
|
import type { GatewayCronState } from "./server-cron.js";
|
|
|
|
const hoisted = vi.hoisted(() => {
|
|
let state: unknown;
|
|
return {
|
|
buildGatewayCronService: vi.fn(() => state),
|
|
setState(nextState: unknown) {
|
|
state = nextState;
|
|
},
|
|
};
|
|
});
|
|
|
|
vi.mock("./server-cron.js", () => ({
|
|
buildGatewayCronService: hoisted.buildGatewayCronService,
|
|
}));
|
|
|
|
const { createLazyGatewayCronState } = await import("./server-cron-lazy.js");
|
|
|
|
describe("createLazyGatewayCronState", () => {
|
|
beforeEach(() => {
|
|
vi.unstubAllEnvs();
|
|
hoisted.buildGatewayCronService.mockClear();
|
|
});
|
|
|
|
it("does not build the heavy cron service until an async cron operation needs it", async () => {
|
|
const cron = createCronService();
|
|
const state = createCronState(cron);
|
|
hoisted.setState(state);
|
|
|
|
const lazy = createLazyGatewayCronState(createParams());
|
|
|
|
expect(hoisted.buildGatewayCronService).not.toHaveBeenCalled();
|
|
expect(lazy.cron.getJob("demo")).toBeUndefined();
|
|
expect(lazy.cron.getDefaultAgentId()).toBeUndefined();
|
|
|
|
await lazy.cron.status();
|
|
|
|
expect(hoisted.buildGatewayCronService).toHaveBeenCalledTimes(1);
|
|
expect(cron.status).toHaveBeenCalledTimes(1);
|
|
});
|
|
|
|
it("loads the cron service for direct job reads", async () => {
|
|
const cron = createCronService();
|
|
hoisted.setState(createCronState(cron));
|
|
|
|
const lazy = createLazyGatewayCronState(createParams());
|
|
await lazy.cron.readJob("demo");
|
|
|
|
expect(hoisted.buildGatewayCronService).toHaveBeenCalledTimes(1);
|
|
expect(cron.readJob).toHaveBeenCalledWith("demo");
|
|
});
|
|
|
|
it("starts the loaded cron service once", async () => {
|
|
const cron = createCronService();
|
|
hoisted.setState(createCronState(cron));
|
|
|
|
const lazy = createLazyGatewayCronState(createParams());
|
|
|
|
await lazy.cron.start();
|
|
await lazy.cron.start();
|
|
|
|
expect(hoisted.buildGatewayCronService).toHaveBeenCalledTimes(1);
|
|
expect(cron.start).toHaveBeenCalledTimes(1);
|
|
});
|
|
|
|
it("does not start cron after stop wins the lazy startup race", async () => {
|
|
const cron = createCronService();
|
|
hoisted.setState(createCronState(cron));
|
|
|
|
const lazy = createLazyGatewayCronState(createParams());
|
|
const startPromise = lazy.cron.start();
|
|
|
|
lazy.cron.stop();
|
|
await startPromise;
|
|
|
|
expect(cron.start).not.toHaveBeenCalled();
|
|
expect(cron.stop).toHaveBeenCalledTimes(1);
|
|
});
|
|
|
|
it("allows a stopped loaded cron service to start again", async () => {
|
|
const cron = createCronService();
|
|
hoisted.setState(createCronState(cron));
|
|
|
|
const lazy = createLazyGatewayCronState(createParams());
|
|
|
|
await lazy.cron.start();
|
|
lazy.cron.stop();
|
|
await lazy.cron.start();
|
|
|
|
expect(hoisted.buildGatewayCronService).toHaveBeenCalledTimes(1);
|
|
expect(cron.stop).toHaveBeenCalledTimes(1);
|
|
expect(cron.start).toHaveBeenCalledTimes(2);
|
|
});
|
|
|
|
it("keeps synchronous wake non-blocking before the cron service is loaded", async () => {
|
|
const cron = createCronService();
|
|
hoisted.setState(createCronState(cron));
|
|
|
|
const lazy = createLazyGatewayCronState(createParams());
|
|
|
|
expect(lazy.cron.wake({ mode: "now", text: "ping" })).toEqual({ ok: false });
|
|
|
|
await vi.waitFor(() => {
|
|
expect(hoisted.buildGatewayCronService).toHaveBeenCalledTimes(1);
|
|
});
|
|
expect(cron.wake).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it("preserves the startup cron enabled flag without loading cron runtime", () => {
|
|
vi.stubEnv("OPENCLAW_SKIP_CRON", "1");
|
|
|
|
const lazy = createLazyGatewayCronState(createParams());
|
|
|
|
expect(lazy.cronEnabled).toBe(false);
|
|
expect(hoisted.buildGatewayCronService).not.toHaveBeenCalled();
|
|
});
|
|
});
|
|
|
|
function createParams(overrides: Partial<OpenClawConfig> = {}) {
|
|
return {
|
|
cfg: {
|
|
...overrides,
|
|
} as OpenClawConfig,
|
|
deps: {} as CliDeps,
|
|
broadcast: vi.fn(),
|
|
};
|
|
}
|
|
|
|
function createCronState(cron: CronServiceContract): GatewayCronState {
|
|
return {
|
|
cron,
|
|
storePath: "/tmp/openclaw-cron.json",
|
|
cronEnabled: true,
|
|
} as GatewayCronState;
|
|
}
|
|
|
|
function createCronService(): CronServiceContract {
|
|
return {
|
|
start: vi.fn(async () => undefined),
|
|
stop: vi.fn(),
|
|
status: vi.fn(async () => ({ enabled: true }) as never),
|
|
list: vi.fn(async () => [] as never),
|
|
listPage: vi.fn(async () => ({ items: [], total: 0 }) as never),
|
|
add: vi.fn(async () => ({ ok: true }) as never),
|
|
update: vi.fn(async () => ({ ok: true }) as never),
|
|
remove: vi.fn(async () => ({ ok: true }) as never),
|
|
run: vi.fn(async () => ({ ok: true, ran: false, reason: "invalid-spec" }) as never),
|
|
enqueueRun: vi.fn(async () => ({ ok: true, ran: false, reason: "invalid-spec" }) as never),
|
|
getJob: vi.fn(() => undefined),
|
|
readJob: vi.fn(async () => undefined),
|
|
getDefaultAgentId: vi.fn(() => "default"),
|
|
wake: vi.fn(() => ({ ok: true })),
|
|
};
|
|
}
|