From fc54e3eabd5c5d618916009d92f90b7bcc85cf29 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Sat, 21 Feb 2026 21:47:41 +0000 Subject: [PATCH] test(cli): dedupe cron shared test fixtures --- src/cli/cron-cli/shared.test.ts | 111 +++++++++++++------------------- 1 file changed, 45 insertions(+), 66 deletions(-) diff --git a/src/cli/cron-cli/shared.test.ts b/src/cli/cron-cli/shared.test.ts index fb453a930..0ecfb8635 100644 --- a/src/cli/cron-cli/shared.test.ts +++ b/src/cli/cron-cli/shared.test.ts @@ -3,32 +3,45 @@ import type { CronJob } from "../../cron/types.js"; import type { RuntimeEnv } from "../../runtime.js"; import { printCronList } from "./shared.js"; +function createRuntimeLogCapture(): { logs: string[]; runtime: RuntimeEnv } { + const logs: string[] = []; + const runtime = { + log: (msg: string) => logs.push(msg), + error: () => {}, + exit: () => {}, + } as RuntimeEnv; + return { logs, runtime }; +} + +function createBaseJob(overrides: Partial): CronJob { + const now = Date.now(); + return { + id: "job-id", + agentId: "main", + name: "Test Job", + enabled: true, + createdAtMs: now, + updatedAtMs: now, + schedule: { kind: "at", at: new Date(now + 3600000).toISOString() }, + wakeMode: "next-heartbeat", + payload: { kind: "systemEvent", text: "test" }, + state: { nextRunAtMs: now + 3600000 }, + ...overrides, + } as CronJob; +} + describe("printCronList", () => { it("handles job with undefined sessionTarget (#9649)", () => { - const logs: string[] = []; - const mockRuntime = { - log: (msg: string) => logs.push(msg), - error: () => {}, - exit: () => {}, - } as RuntimeEnv; + const { logs, runtime } = createRuntimeLogCapture(); // Simulate a job without sessionTarget (as reported in #9649) - const jobWithUndefinedTarget = { + const jobWithUndefinedTarget = createBaseJob({ id: "test-job-id", - agentId: "main", - name: "Test Job", - enabled: true, - createdAtMs: Date.now(), - updatedAtMs: Date.now(), - schedule: { kind: "at", at: new Date(Date.now() + 3600000).toISOString() }, // sessionTarget is intentionally omitted to simulate the bug - wakeMode: "next-heartbeat", - payload: { kind: "systemEvent", text: "test" }, - state: { nextRunAtMs: Date.now() + 3600000 }, - } as CronJob; + }); // This should not throw "Cannot read properties of undefined (reading 'trim')" - expect(() => printCronList([jobWithUndefinedTarget], mockRuntime)).not.toThrow(); + expect(() => printCronList([jobWithUndefinedTarget], runtime)).not.toThrow(); // Verify output contains the job expect(logs.length).toBeGreaterThan(1); @@ -36,78 +49,44 @@ describe("printCronList", () => { }); it("handles job with defined sessionTarget", () => { - const logs: string[] = []; - const mockRuntime = { - log: (msg: string) => logs.push(msg), - error: () => {}, - exit: () => {}, - } as RuntimeEnv; - - const jobWithTarget: CronJob = { + const { logs, runtime } = createRuntimeLogCapture(); + const jobWithTarget = createBaseJob({ id: "test-job-id-2", - agentId: "main", name: "Test Job 2", - enabled: true, - createdAtMs: Date.now(), - updatedAtMs: Date.now(), - schedule: { kind: "at", at: new Date(Date.now() + 3600000).toISOString() }, sessionTarget: "isolated", - wakeMode: "next-heartbeat", - payload: { kind: "systemEvent", text: "test" }, - state: { nextRunAtMs: Date.now() + 3600000 }, - }; + }); - expect(() => printCronList([jobWithTarget], mockRuntime)).not.toThrow(); + expect(() => printCronList([jobWithTarget], runtime)).not.toThrow(); expect(logs.some((line) => line.includes("isolated"))).toBe(true); }); it("shows stagger label for cron schedules", () => { - const logs: string[] = []; - const mockRuntime = { - log: (msg: string) => logs.push(msg), - error: () => {}, - exit: () => {}, - } as RuntimeEnv; - - const job: CronJob = { + const { logs, runtime } = createRuntimeLogCapture(); + const job = createBaseJob({ id: "staggered-job", name: "Staggered", - enabled: true, - createdAtMs: Date.now(), - updatedAtMs: Date.now(), schedule: { kind: "cron", expr: "0 * * * *", staggerMs: 5 * 60_000 }, sessionTarget: "main", - wakeMode: "next-heartbeat", - payload: { kind: "systemEvent", text: "tick" }, state: {}, - }; + payload: { kind: "systemEvent", text: "tick" }, + }); - printCronList([job], mockRuntime); + printCronList([job], runtime); expect(logs.some((line) => line.includes("(stagger 5m)"))).toBe(true); }); it("shows exact label for cron schedules with stagger disabled", () => { - const logs: string[] = []; - const mockRuntime = { - log: (msg: string) => logs.push(msg), - error: () => {}, - exit: () => {}, - } as RuntimeEnv; - - const job: CronJob = { + const { logs, runtime } = createRuntimeLogCapture(); + const job = createBaseJob({ id: "exact-job", name: "Exact", - enabled: true, - createdAtMs: Date.now(), - updatedAtMs: Date.now(), schedule: { kind: "cron", expr: "0 7 * * *", staggerMs: 0 }, sessionTarget: "main", - wakeMode: "next-heartbeat", - payload: { kind: "systemEvent", text: "tick" }, state: {}, - }; + payload: { kind: "systemEvent", text: "tick" }, + }); - printCronList([job], mockRuntime); + printCronList([job], runtime); expect(logs.some((line) => line.includes("(exact)"))).toBe(true); }); });