mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-10 18:24:57 +00:00
* fix(cron): handle undefined sessionTarget in list output (#9649) When sessionTarget is undefined, pad() would crash with 'Cannot read properties of undefined (reading trim)'. Use '-' as fallback value. * test(cron): add regression test for undefined sessionTarget (#9649) Verifies that printCronList handles jobs with undefined sessionTarget without crashing. Test fails on main branch, passes with the fix. * fix: use correct CronSchedule format in tests (#9752) (thanks @lailoo) Tests were using { kind: 'at', atMs: number } but the CronSchedule type requires { kind: 'at', at: string } where 'at' is an ISO date string. --------- Co-authored-by: damaozi <1811866786@qq.com> Co-authored-by: Tyler Yust <TYTYYUST@YAHOO.COM>
This commit is contained in:
63
src/cli/cron-cli/shared.test.ts
Normal file
63
src/cli/cron-cli/shared.test.ts
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
import { describe, expect, it } from "vitest";
|
||||||
|
import type { CronJob } from "../../cron/types.js";
|
||||||
|
import type { RuntimeEnv } from "../../runtime.js";
|
||||||
|
import { printCronList } from "./shared.js";
|
||||||
|
|
||||||
|
describe("printCronList", () => {
|
||||||
|
it("handles job with undefined sessionTarget (#9649)", () => {
|
||||||
|
const logs: string[] = [];
|
||||||
|
const mockRuntime = {
|
||||||
|
log: (msg: string) => logs.push(msg),
|
||||||
|
error: () => {},
|
||||||
|
exit: () => {},
|
||||||
|
} as RuntimeEnv;
|
||||||
|
|
||||||
|
// Simulate a job without sessionTarget (as reported in #9649)
|
||||||
|
const jobWithUndefinedTarget = {
|
||||||
|
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();
|
||||||
|
|
||||||
|
// Verify output contains the job
|
||||||
|
expect(logs.length).toBeGreaterThan(1);
|
||||||
|
expect(logs.some((line) => line.includes("test-job-id"))).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("handles job with defined sessionTarget", () => {
|
||||||
|
const logs: string[] = [];
|
||||||
|
const mockRuntime = {
|
||||||
|
log: (msg: string) => logs.push(msg),
|
||||||
|
error: () => {},
|
||||||
|
exit: () => {},
|
||||||
|
} as RuntimeEnv;
|
||||||
|
|
||||||
|
const jobWithTarget: CronJob = {
|
||||||
|
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(logs.some((line) => line.includes("isolated"))).toBe(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -197,7 +197,7 @@ export function printCronList(jobs: CronJob[], runtime = defaultRuntime) {
|
|||||||
const lastLabel = pad(formatRelative(job.state.lastRunAtMs, now), CRON_LAST_PAD);
|
const lastLabel = pad(formatRelative(job.state.lastRunAtMs, now), CRON_LAST_PAD);
|
||||||
const statusRaw = formatStatus(job);
|
const statusRaw = formatStatus(job);
|
||||||
const statusLabel = pad(statusRaw, CRON_STATUS_PAD);
|
const statusLabel = pad(statusRaw, CRON_STATUS_PAD);
|
||||||
const targetLabel = pad(job.sessionTarget, CRON_TARGET_PAD);
|
const targetLabel = pad(job.sessionTarget ?? "-", CRON_TARGET_PAD);
|
||||||
const agentLabel = pad(truncate(job.agentId ?? "default", CRON_AGENT_PAD), CRON_AGENT_PAD);
|
const agentLabel = pad(truncate(job.agentId ?? "default", CRON_AGENT_PAD), CRON_AGENT_PAD);
|
||||||
|
|
||||||
const coloredStatus = (() => {
|
const coloredStatus = (() => {
|
||||||
|
|||||||
Reference in New Issue
Block a user