fix: reject stale one-shot cron jobs

This commit is contained in:
Flownium 2026-07-06 15:45:50 +10:00 committed by kshitij
parent 845a2d8152
commit 848089ac93
3 changed files with 48 additions and 1 deletions

View file

@ -978,6 +978,19 @@ def create_job(
no_agent=normalized_no_agent,
)
next_run_at = compute_next_run(parsed_schedule)
if parsed_schedule.get("kind") == "once" and next_run_at is None:
run_at = parsed_schedule.get("run_at") or schedule
logger.warning(
"Rejecting one-shot cron job '%s': run_at %s is outside the %ss grace window",
name or label_source[:50].strip(),
run_at,
ONESHOT_GRACE_SECONDS,
)
raise ValueError(
f"Requested one-shot time {run_at} is in the past and cannot be scheduled."
)
job = {
"id": job_id,
"name": name or label_source[:50].strip(),
@ -1006,7 +1019,7 @@ def create_job(
"paused_at": None,
"paused_reason": None,
"created_at": now,
"next_run_at": compute_next_run(parsed_schedule),
"next_run_at": next_run_at,
"last_run_at": None,
"last_status": None,
"last_error": None,

View file

@ -11,6 +11,7 @@ import json
import os
import sys
import textwrap
from datetime import datetime, timedelta, timezone
from pathlib import Path
import pytest
@ -86,6 +87,19 @@ class TestJobScriptField:
assert updated.get("script") is None
def test_cronjob_tool_rejects_stale_past_one_shot(cron_env, monkeypatch):
from tools.cronjob_tools import cronjob
now = datetime(2026, 3, 18, 4, 30, 0, tzinfo=timezone.utc)
monkeypatch.setattr("cron.jobs._hermes_now", lambda: now)
stale = (now - timedelta(minutes=5)).isoformat()
result = json.loads(cronjob(action="create", prompt="Too late", schedule=stale))
assert result["success"] is False
assert "past and cannot be scheduled" in result["error"]
class TestRunJobScript:
"""Test the _run_job_script() function."""

View file

@ -305,6 +305,26 @@ class TestJobCRUD:
job = create_job(prompt="One-shot", schedule="1h")
assert job["repeat"]["times"] == 1
def test_rejects_stale_past_one_shot_at_creation(self, tmp_cron_dir, monkeypatch):
now = datetime(2026, 3, 18, 4, 30, 0, tzinfo=timezone.utc)
monkeypatch.setattr("cron.jobs._hermes_now", lambda: now)
stale = (now - timedelta(minutes=5)).isoformat()
with pytest.raises(ValueError, match="past and cannot be scheduled"):
create_job(prompt="Too late", schedule=stale)
assert load_jobs() == []
def test_recent_past_one_shot_within_grace_still_creates(self, tmp_cron_dir, monkeypatch):
now = datetime(2026, 3, 18, 4, 30, 30, tzinfo=timezone.utc)
monkeypatch.setattr("cron.jobs._hermes_now", lambda: now)
recent = (now - timedelta(seconds=30)).isoformat()
job = create_job(prompt="Still valid", schedule=recent)
assert job["next_run_at"] == recent
assert load_jobs()[0]["id"] == job["id"]
def test_interval_no_auto_repeat(self, tmp_cron_dir):
job = create_job(prompt="Recurring", schedule="every 1h")
assert job["repeat"]["times"] is None