fix: honor live scheduling revisions at dispatch

This commit is contained in:
lda
2026-09-09 19:53:12 +07:00 Verified
parent b7c5f4e0eb
commit ecb5a76b12
11 changed files with 134 additions and 27 deletions
+30 -2
View File
@@ -13,8 +13,8 @@ from tests.scheduling.controlled import (
)
from wf_artifacts.runs.models import StoredRunStatus
from wf_artifacts.runs.store import FileRunStore
from wf_scheduling.calendar import OneShotSource
from wf_scheduling.models import Schedule
from wf_scheduling.calendar import CronSource, OneShotSource
from wf_scheduling.models import CronTrigger, Schedule
from wf_scheduling.ownership import SchedulerOwnership
from wf_scheduling.poll import SCAN_CAP, Scheduler
from wf_scheduling.prepare import SchedulePreparer
@@ -249,6 +249,34 @@ def test_schedule_edit_between_poll_snapshot_and_admission_cannot_overwrite_term
sched.ownership.release()
def test_trigger_edit_refreshes_managed_calendar_before_polling(tmp_path: Path) -> None:
"""A trigger edit cannot be paired with the previous tick's source."""
sched, store, runs, sources = _harness(tmp_path, script={"*": "complete"})
start = ts(2026, 9, 8, 11, 0)
now = ts(2026, 9, 8, 12, 0)
_add(
sched,
store,
sources,
"a",
CronSource("0 * * * *", "UTC"),
start,
)
old_trigger = store.get_schedule("a").trigger
sched.set_source_definitions({"a": old_trigger})
edited = store.get_schedule("a")
edited.revision = 2
edited.trigger = CronTrigger(
expression="0 13 * * *",
timezone="UTC",
)
store.update_schedule(edited, expected_revision=1)
assert sched.poll(now) == {"a": "idle"}
assert runs.list_admissions() == []
sched.ownership.release()
def test_parallel_limits_and_interrupted_slots() -> None:
import tempfile
+24 -1
View File
@@ -56,9 +56,15 @@ def _scheduler(
) -> tuple[Scheduler, FileScheduleStore, FileRunStore]:
sched_store = FileScheduleStore(tmp_path / "sched")
run_store = FileRunStore(tmp_path / "runs")
def revisioned_fixture_environment(sched: Any) -> Any:
environment = fixture_environment(sched)
deployment = environment.deployment.model_copy(update={"revision": 3})
return environment.model_copy(update={"deployment": deployment})
preparer = SchedulePreparer(
DictDeployments({"dep-1": {"rev": 3, "required": []}}),
fixture_environment,
revisioned_fixture_environment,
)
sched = Scheduler(
schedule_store=sched_store,
@@ -159,6 +165,23 @@ def test_unknown_deployment_rejects_without_a_run(tmp_path: Path) -> None:
sched.ownership.release()
def test_deployment_revision_mismatch_rejects_pinned_environment() -> None:
"""Preparation fails when directory metadata no longer matches the pin."""
preparer = SchedulePreparer(
DictDeployments({"dep-1": {"rev": 2, "required": []}}),
fixture_environment,
)
result = preparer.prepare(
sched=_sched_model("s"),
intended=ts(2026, 9, 8, 12, 0),
now=ts(2026, 9, 8, 12, 0),
)
assert isinstance(result, PreparationRejected)
assert result.reason == "deployment-changed"
def test_missing_required_input_rejects_without_a_run(tmp_path: Path) -> None:
sched_store = FileScheduleStore(tmp_path / "sched")
run_store = FileRunStore(tmp_path / "runs")
+1 -1
View File
@@ -248,7 +248,7 @@ def test_changed_contract_rejects_before_admission(tmp_path: Path) -> None:
)
sched.preparer = SchedulePreparer(
DictDeployments({"dep-1": {"rev": 2, "required": []}}), changed_env
DictDeployments({"dep-1": {"rev": 1, "required": []}}), changed_env
)
assert sched.poll(ts(2026, 9, 8, 12, 0)) == {"s": "admit:None"}
_rejected_entry(store)