mirror of
https://github.com/openclaw/openclaw.git
synced 2026-03-12 07:20:45 +00:00
Cron+Slack: fix cooldown omission and cache cap enforcement
This commit is contained in:
@@ -55,4 +55,13 @@ describe("slack sent-thread-cache", () => {
|
||||
vi.spyOn(Date, "now").mockReturnValue(Date.now() + 25 * 60 * 60 * 1000);
|
||||
expect(hasSlackThreadParticipation("A1", "C123", "1700000000.000001")).toBe(false);
|
||||
});
|
||||
|
||||
it("enforces maximum entries by evicting oldest fresh entries", () => {
|
||||
for (let i = 0; i < 5001; i += 1) {
|
||||
recordSlackThreadParticipation("A1", "C123", `1700000000.${String(i).padStart(6, "0")}`);
|
||||
}
|
||||
|
||||
expect(hasSlackThreadParticipation("A1", "C123", "1700000000.000000")).toBe(false);
|
||||
expect(hasSlackThreadParticipation("A1", "C123", "1700000000.005000")).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -22,6 +22,13 @@ function evictExpired(): void {
|
||||
}
|
||||
}
|
||||
|
||||
function evictOldest(): void {
|
||||
const oldest = threadParticipation.keys().next().value;
|
||||
if (oldest) {
|
||||
threadParticipation.delete(oldest);
|
||||
}
|
||||
}
|
||||
|
||||
export function recordSlackThreadParticipation(
|
||||
accountId: string,
|
||||
channelId: string,
|
||||
@@ -33,6 +40,9 @@ export function recordSlackThreadParticipation(
|
||||
if (threadParticipation.size >= MAX_ENTRIES) {
|
||||
evictExpired();
|
||||
}
|
||||
if (threadParticipation.size >= MAX_ENTRIES) {
|
||||
evictOldest();
|
||||
}
|
||||
threadParticipation.set(makeKey(accountId, channelId, threadTs), Date.now());
|
||||
}
|
||||
|
||||
|
||||
@@ -346,6 +346,55 @@ describe("cron controller", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("omits failureAlert.cooldownMs when custom cooldown is left blank", async () => {
|
||||
const request = vi.fn(async (method: string, _payload?: unknown) => {
|
||||
if (method === "cron.update") {
|
||||
return { id: "job-alert-no-cooldown" };
|
||||
}
|
||||
if (method === "cron.list") {
|
||||
return { jobs: [{ id: "job-alert-no-cooldown" }] };
|
||||
}
|
||||
if (method === "cron.status") {
|
||||
return { enabled: true, jobs: 1, nextWakeAtMs: null };
|
||||
}
|
||||
return {};
|
||||
});
|
||||
const state = createState({
|
||||
client: { request } as unknown as CronState["client"],
|
||||
cronEditingJobId: "job-alert-no-cooldown",
|
||||
cronForm: {
|
||||
...DEFAULT_CRON_FORM,
|
||||
name: "alert job no cooldown",
|
||||
payloadKind: "agentTurn",
|
||||
payloadText: "run it",
|
||||
failureAlertMode: "custom",
|
||||
failureAlertAfter: "3",
|
||||
failureAlertCooldownSeconds: "",
|
||||
failureAlertChannel: "telegram",
|
||||
failureAlertTo: "123456",
|
||||
},
|
||||
});
|
||||
|
||||
await addCronJob(state);
|
||||
|
||||
const updateCall = request.mock.calls.find(([method]) => method === "cron.update");
|
||||
expect(updateCall).toBeDefined();
|
||||
expect(updateCall?.[1]).toMatchObject({
|
||||
id: "job-alert-no-cooldown",
|
||||
patch: {
|
||||
failureAlert: {
|
||||
after: 3,
|
||||
channel: "telegram",
|
||||
to: "123456",
|
||||
},
|
||||
},
|
||||
});
|
||||
expect(
|
||||
(updateCall?.[1] as { patch?: { failureAlert?: { cooldownMs?: number } } })?.patch
|
||||
?.failureAlert,
|
||||
).not.toHaveProperty("cooldownMs");
|
||||
});
|
||||
|
||||
it("includes failureAlert=false when disabled per job", async () => {
|
||||
const request = vi.fn(async (method: string, _payload?: unknown) => {
|
||||
if (method === "cron.update") {
|
||||
|
||||
@@ -579,15 +579,17 @@ function buildFailureAlert(form: CronFormState) {
|
||||
return undefined;
|
||||
}
|
||||
const after = toNumber(form.failureAlertAfter.trim(), 0);
|
||||
const cooldownSeconds = toNumber(form.failureAlertCooldownSeconds.trim(), 0);
|
||||
const cooldownRaw = form.failureAlertCooldownSeconds.trim();
|
||||
const cooldownSeconds = cooldownRaw.length > 0 ? toNumber(cooldownRaw, 0) : undefined;
|
||||
const cooldownMs =
|
||||
cooldownSeconds !== undefined && Number.isFinite(cooldownSeconds) && cooldownSeconds >= 0
|
||||
? Math.floor(cooldownSeconds * 1000)
|
||||
: undefined;
|
||||
return {
|
||||
after: after > 0 ? Math.floor(after) : undefined,
|
||||
channel: form.failureAlertChannel.trim() || CRON_CHANNEL_LAST,
|
||||
to: form.failureAlertTo.trim() || undefined,
|
||||
cooldownMs:
|
||||
Number.isFinite(cooldownSeconds) && cooldownSeconds >= 0
|
||||
? Math.floor(cooldownSeconds * 1000)
|
||||
: undefined,
|
||||
...(cooldownMs !== undefined ? { cooldownMs } : {}),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user