import { describe, expect, it } from "vitest"; import { filterCronJobsForAgent, formatCronJobDisplay, formatCronPayload, formatCronSchedule, resolveLatestCronJobForAgent, } from "@/lib/cron/types"; import type { CronJobSummary } from "@/lib/cron/types"; const buildJob = (input: { id: string; agentId?: string; updatedAtMs: number; }): CronJobSummary => ({ id: input.id, name: input.id, enabled: true, updatedAtMs: input.updatedAtMs, schedule: { kind: "every", everyMs: 60_000 }, sessionTarget: "isolated", wakeMode: "next-heartbeat", payload: { kind: "agentTurn", message: "hello" }, state: {}, ...(input.agentId ? { agentId: input.agentId } : {}), }); describe("cron selectors", () => { it("filters_jobs_to_selected_agent", () => { const jobs = [ buildJob({ id: "one", agentId: "agent-1", updatedAtMs: 10 }), buildJob({ id: "two", agentId: "agent-2", updatedAtMs: 20 }), buildJob({ id: "three", updatedAtMs: 30 }), ]; expect(filterCronJobsForAgent(jobs, "agent-1").map((job) => job.id)).toEqual(["one"]); expect(filterCronJobsForAgent(jobs, "agent-2").map((job) => job.id)).toEqual(["two"]); expect(filterCronJobsForAgent(jobs, "missing")).toEqual([]); }); it("resolves_latest_agent_job_by_updated_at", () => { const jobs = [ buildJob({ id: "older", agentId: "agent-1", updatedAtMs: 10 }), buildJob({ id: "newer", agentId: "agent-1", updatedAtMs: 30 }), buildJob({ id: "other", agentId: "agent-2", updatedAtMs: 40 }), ]; expect(resolveLatestCronJobForAgent(jobs, "agent-1")?.id).toBe("newer"); expect(resolveLatestCronJobForAgent(jobs, "agent-2")?.id).toBe("other"); expect(resolveLatestCronJobForAgent(jobs, "missing")).toBeNull(); }); it("matches_agent_ids_after_trimming_whitespace", () => { const jobs = [ buildJob({ id: "trimmed", agentId: "agent-1", updatedAtMs: 20 }), buildJob({ id: "other", agentId: "agent-2", updatedAtMs: 30 }), ]; expect(filterCronJobsForAgent(jobs, " agent-1 ").map((job) => job.id)).toEqual(["trimmed"]); expect(resolveLatestCronJobForAgent(jobs, " agent-1 ")?.id).toBe("trimmed"); }); }); describe("cron formatting", () => { it("formats_every_schedule_with_h_m_s_ms_suffixes", () => { expect(formatCronSchedule({ kind: "every", everyMs: 3_600_000 })).toBe("Every 1h"); expect(formatCronSchedule({ kind: "every", everyMs: 60_000 })).toBe("Every 1m"); expect(formatCronSchedule({ kind: "every", everyMs: 1_000 })).toBe("Every 1s"); expect(formatCronSchedule({ kind: "every", everyMs: 1_500 })).toBe("Every 1500ms"); }); it("formats_cron_schedule_with_optional_tz", () => { expect(formatCronSchedule({ kind: "cron", expr: "0 0 * * *" })).toBe("Cron: 0 0 * * *"); expect(formatCronSchedule({ kind: "cron", expr: "0 0 * * *", tz: "UTC" })).toBe( "Cron: 0 0 * * * (UTC)" ); }); it("formats_at_schedule_as_raw_when_not_parseable", () => { expect(formatCronSchedule({ kind: "at", at: "not-a-date" })).toBe("At: not-a-date"); }); it("formats_cron_payload_text", () => { expect(formatCronPayload({ kind: "systemEvent", text: "hello" })).toBe("hello"); expect(formatCronPayload({ kind: "agentTurn", message: "hi" })).toBe("hi"); }); it("formats_cron_job_display_as_three_lines", () => { const job: CronJobSummary = { id: "job-1", name: "Job name", enabled: true, updatedAtMs: 10, schedule: { kind: "every", everyMs: 60_000 }, sessionTarget: "isolated", wakeMode: "next-heartbeat", payload: { kind: "agentTurn", message: "hi" }, state: {}, }; expect(formatCronJobDisplay(job)).toBe("Job name\nEvery 1m\nhi"); }); });