openskynet / src /cron /service.issue-22895-every-next-run.test.ts
Darochin's picture
Mirror OpenSkyNet workspace snapshot from Git HEAD
fc93158 verified
import { describe, expect, it } from "vitest";
import { computeJobNextRunAtMs } from "./service/jobs.js";
import type { CronJob } from "./types.js";
const EVERY_30_MIN_MS = 30 * 60_000;
const ANCHOR_MS = Date.parse("2026-02-22T09:14:00.000Z");
function createEveryJob(state: CronJob["state"]): CronJob {
return {
id: "issue-22895",
name: "every-30-min",
enabled: true,
createdAtMs: ANCHOR_MS,
updatedAtMs: ANCHOR_MS,
schedule: { kind: "every", everyMs: EVERY_30_MIN_MS, anchorMs: ANCHOR_MS },
sessionTarget: "isolated",
wakeMode: "next-heartbeat",
payload: { kind: "agentTurn", message: "check cadence" },
delivery: { mode: "none" },
state,
};
}
describe("Cron issue #22895 interval scheduling", () => {
it("uses lastRunAtMs cadence when the next interval is still in the future", () => {
const nowMs = Date.parse("2026-02-22T10:10:00.000Z");
const job = createEveryJob({
lastRunAtMs: Date.parse("2026-02-22T10:04:00.000Z"),
});
const nextFromLast = computeJobNextRunAtMs(job, nowMs);
const nextFromAnchor = computeJobNextRunAtMs(
{ ...job, state: { ...job.state, lastRunAtMs: undefined } },
nowMs,
);
expect(nextFromLast).toBe(job.state.lastRunAtMs! + EVERY_30_MIN_MS);
expect(nextFromAnchor).toBe(Date.parse("2026-02-22T10:14:00.000Z"));
expect(nextFromLast).toBeGreaterThan(nextFromAnchor!);
});
it("falls back to anchor scheduling when lastRunAtMs cadence is already in the past", () => {
const nowMs = Date.parse("2026-02-22T10:40:00.000Z");
const job = createEveryJob({
lastRunAtMs: Date.parse("2026-02-22T10:04:00.000Z"),
});
const next = computeJobNextRunAtMs(job, nowMs);
expect(next).toBe(Date.parse("2026-02-22T10:44:00.000Z"));
});
});