fix(cron): align regression fixtures with Gateway scheduler

Complete the test dependency migration after #159099 merged alongside
both history fixtures and the startup recovery service/lazy proxy.

Drive startup recovery with the existing joined scheduler clock instead
of polling detached fake-timer callbacks. Inject an actual schedule-call
failure and prove that work due during suspension runs exactly once after
resume. Production startup recovery and Gateway scheduler behavior remain
unchanged.

Validation: all 25 pnpm tsgo:core:test shards; all cron tests plus
server-cron-lazy and both cron.history suites (263 files, 3400 tests);
changed-file checks, formatting, lint, and independent review. The focused
start-arm run passed all 3 cases in 18.29s wall time (1.6s test time).
Audited all CronService/lazy-cron callers under src, extensions, and test.
This commit is contained in:
Peter Steinberger
2026-09-26 12:55:51 -07:00
parent 0830a3fa45
commit c0e6951d6f
3 changed files with 17 additions and 13 deletions
@@ -8,7 +8,10 @@ import type { CliDeps } from "../../cli/deps.types.js";
import { createLazyGatewayCronState } from "../../gateway/server-cron-lazy.js";
import type { GatewayCronState } from "../../gateway/server-cron.js";
import { openOpenClawStateDatabase } from "../../state/openclaw-state-db.js";
import { createTestGatewayScheduler } from "../../test-utils/gateway-scheduler-clock.js";
import {
createGatewaySchedulerClock,
createTestGatewayScheduler,
} from "../../test-utils/gateway-scheduler-clock.js";
import { CronService } from "../service.js";
import { saveCronStore } from "../store.js";
@@ -50,12 +53,13 @@ it.each([
}
const enqueueSystemEvent = vi.fn();
const log = { ...noopLogger, debug: vi.fn(), warn: vi.fn() };
const clock = createGatewaySchedulerClock(now);
const scheduler = createTestGatewayScheduler(clock.clock);
if (failArm) {
log.debug.mockImplementationOnce(() => {
vi.spyOn(scheduler, "schedule").mockImplementationOnce(() => {
throw new Error("secondary arm failure");
});
}
const scheduler = createTestGatewayScheduler("fake-timers");
const service = new CronService({
scheduler,
storePath,
@@ -75,10 +79,10 @@ it.each([
reconcileSystemJobs: async () => "converged",
};
const { cron } = createLazyGatewayCronState({
scheduler,
cfg: {},
deps: {} as CliDeps,
broadcast: vi.fn(),
scheduler,
});
try {
if (failWrite) {
@@ -95,16 +99,12 @@ it.each([
database.exec("DROP TRIGGER IF EXISTS reject_startup_terminal_write");
expect(enqueueSystemEvent.mock.calls.map(([text]) => text)).toEqual(["overdue"]);
cron.pauseScheduling();
await vi.advanceTimersByTimeAsync(1_000);
await clock.advanceBy(15_000);
expect(enqueueSystemEvent.mock.calls.map(([text]) => text)).toEqual(["overdue"]);
cron.resumeScheduling();
await vi.advanceTimersByTimeAsync(15_000);
await vi.waitFor(() =>
expect(enqueueSystemEvent.mock.calls.map(([text]) => text)).toEqual([
"overdue",
"upcoming",
]),
);
await vi.advanceTimersByTimeAsync(60_000);
await clock.advanceBy(15_000);
expect(enqueueSystemEvent.mock.calls.map(([text]) => text)).toEqual(["overdue", "upcoming"]);
await clock.advanceBy(60_000);
expect(enqueueSystemEvent.mock.calls.map(([text]) => text)).toEqual(["overdue", "upcoming"]);
} finally {
cron.stop();
@@ -4,6 +4,7 @@ import { createOperationalRunInstanceRef } from "../../agents/admitted-run-conte
import { cronRunLogEntryToDetail } from "../../cron/run-history-detail.js";
import { CronService } from "../../cron/service.js";
import { createNoopLogger } from "../../cron/service.test-harness.js";
import { createTestGatewayScheduler } from "../../test-utils/gateway-scheduler-clock.js";
import { createDirectChatContext } from "../server-chat.agent-events.test-helpers.js";
import { cronHistoryHandler } from "./cron-history.js";
import { cronHandlers } from "./cron.js";
@@ -71,6 +72,7 @@ it.each([
"$method rechecks $change authority before final publication",
async ({ method, change }) => {
const cron = new CronService({
scheduler: createTestGatewayScheduler(),
storePath: "/synthetic/cron",
cronEnabled: false,
defaultAgentId: "main",
@@ -17,6 +17,7 @@ import { createNoopLogger } from "../../cron/service.test-harness.js";
import { cronStoreKey } from "../../cron/store/key.js";
import { ensureProfileForEmail } from "../../state/user-profiles.js";
import { createTaskFixture } from "../../tasks/task-registry.test-support.js";
import { createTestGatewayScheduler } from "../../test-utils/gateway-scheduler-clock.js";
import {
forbidMainThreadSql,
observeMainThreadSql,
@@ -85,6 +86,7 @@ async function setup() {
"jobs.json",
);
const cron = new CronService({
scheduler: createTestGatewayScheduler(),
storePath,
defaultAgentId: "main",
cronEnabled: false,