mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-24 11:25:50 -06:00
fix(cron): align list ordering and visible-name search (#116834)
Co-authored-by: Peter Steinberger <steipete@macos.shared>
This commit is contained in:
committed by
GitHub
parent
67943535b1
commit
dbd8a0981a
@@ -4,7 +4,7 @@ import os from "node:os";
|
||||
import path from "node:path";
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { createMockCronStateForJobs } from "./service.test-harness.js";
|
||||
import { listPage } from "./service/ops-read.js";
|
||||
import { list, listPage } from "./service/ops-read.js";
|
||||
import type { CronJob } from "./types.js";
|
||||
|
||||
function createBaseJob(overrides?: Partial<CronJob>): CronJob {
|
||||
@@ -86,6 +86,62 @@ describe("cron listPage sort guards", () => {
|
||||
},
|
||||
);
|
||||
|
||||
it("keeps unscheduled jobs after scheduled jobs in the unpaginated list", async () => {
|
||||
const jobs = [
|
||||
createBaseJob({ id: "paused-z", enabled: false, state: {} }),
|
||||
createBaseJob({ id: "later", state: { nextRunAtMs: 200 } }),
|
||||
createBaseJob({ id: "paused-a", enabled: false, state: {} }),
|
||||
createBaseJob({ id: "earlier", state: { nextRunAtMs: 100 } }),
|
||||
];
|
||||
const state = createMockCronStateForJobs({ jobs });
|
||||
|
||||
const unpaginated = await list(state, { includeDisabled: true });
|
||||
const page = await listPage(state, { enabled: "all", sortBy: "nextRunAtMs" });
|
||||
|
||||
expect(unpaginated.map((job) => job.id)).toEqual(["earlier", "later", "paused-a", "paused-z"]);
|
||||
expect(unpaginated.map((job) => job.id)).toEqual(page.jobs.map((job) => job.id));
|
||||
});
|
||||
|
||||
it("applies the same stable id tiebreaker to unpaginated cron jobs", async () => {
|
||||
const nextRunAtMs = Date.parse("2026-02-27T15:30:00.000Z");
|
||||
const jobs = [
|
||||
createBaseJob({ id: "scheduled-z", state: { nextRunAtMs } }),
|
||||
createBaseJob({ id: "scheduled-a", state: { nextRunAtMs } }),
|
||||
];
|
||||
const state = createMockCronStateForJobs({ jobs });
|
||||
|
||||
const unpaginated = await list(state);
|
||||
|
||||
expect(unpaginated.map((job) => job.id)).toEqual(["scheduled-a", "scheduled-z"]);
|
||||
});
|
||||
|
||||
it("matches the operator-visible display name when filtering cron jobs", async () => {
|
||||
const job = createBaseJob({
|
||||
id: "report-job",
|
||||
name: "internal-report-name",
|
||||
displayName: "Daily summary",
|
||||
});
|
||||
const state = createMockCronStateForJobs({ jobs: [job] });
|
||||
|
||||
const page = await listPage(state, { query: "Daily summary" });
|
||||
|
||||
expect(page.jobs.map((entry) => entry.id)).toEqual(["report-job"]);
|
||||
});
|
||||
|
||||
it("preserves phrase searches across existing cron job fields", async () => {
|
||||
const job = createBaseJob({
|
||||
id: "report-job",
|
||||
name: "Daily report",
|
||||
description: "Quarterly summary",
|
||||
displayName: "Executive overview",
|
||||
});
|
||||
const state = createMockCronStateForJobs({ jobs: [job] });
|
||||
|
||||
const page = await listPage(state, { query: "report Quarterly" });
|
||||
|
||||
expect(page.jobs.map((entry) => entry.id)).toEqual(["report-job"]);
|
||||
});
|
||||
|
||||
it("normalizes requested agent ids before filtering", async () => {
|
||||
const jobs = [
|
||||
createBaseJob({ id: "job-main", agentId: "main", name: "main" }),
|
||||
|
||||
@@ -50,7 +50,7 @@ export async function list(state: CronServiceState, opts?: { includeDisabled?: b
|
||||
await ensureLoadedForRead(state);
|
||||
const includeDisabled = opts?.includeDisabled === true;
|
||||
const jobs = (state.store?.jobs ?? []).filter((j) => includeDisabled || isJobEnabled(j));
|
||||
return jobs.toSorted((a, b) => (a.state.nextRunAtMs ?? 0) - (b.state.nextRunAtMs ?? 0));
|
||||
return sortCronJobs(jobs, "nextRunAtMs", "asc");
|
||||
});
|
||||
}
|
||||
|
||||
@@ -289,7 +289,13 @@ export async function listPage(state: CronServiceState, opts?: CronListPageOptio
|
||||
return true;
|
||||
}
|
||||
const haystack = normalizeLowercaseStringOrEmpty(
|
||||
[job.id, job.name, job.description ?? "", job.agentId ?? ""].join(" "),
|
||||
[
|
||||
job.id,
|
||||
job.name,
|
||||
job.description ?? "",
|
||||
job.agentId ?? "",
|
||||
...(job.displayName ? [job.displayName] : []),
|
||||
].join(" "),
|
||||
);
|
||||
return haystack.includes(query);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user