diff --git a/api/routes.py b/api/routes.py index 5f402934..48b0963a 100644 --- a/api/routes.py +++ b/api/routes.py @@ -24707,10 +24707,23 @@ def _handle_cron_create(handler, body): def _handle_cron_delivery_options(handler): """Return available delivery platforms for cron jobs.""" - try: - from cron.scheduler import _KNOWN_DELIVERY_PLATFORMS - except Exception: - _KNOWN_DELIVERY_PLATFORMS = frozenset() + # The Agent moved this authority from ``cron.scheduler`` to + # ``cron.scheduler_delivery``. Try the current location first and fall back + # to the legacy one so the picker keeps working across Agent versions. + # Without the fallback chain a bare ImportError silently degraded this + # endpoint to local/origin only, dropping every messaging platform from the + # cron delivery picker (telegram, discord, slack, feishu, ...). + _KNOWN_DELIVERY_PLATFORMS = frozenset() + import importlib + for _module_name in ("cron.scheduler_delivery", "cron.scheduler"): + try: + _mod = importlib.import_module(_module_name) + except Exception: + continue + _known = getattr(_mod, "_KNOWN_DELIVERY_PLATFORMS", None) + if _known: + _KNOWN_DELIVERY_PLATFORMS = frozenset(_known) + break platforms = [ {"value": "local", "label": "Local (save output only)"}, {"value": "origin", "label": "Origin (reply to creator)"} diff --git a/tests/conftest.py b/tests/conftest.py index 5f37c1f5..7dc77884 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -651,6 +651,7 @@ def pytest_collection_modifyitems(config, items): 'test_delivery_options_structure', 'test_delivery_options_includes_common_platforms', 'test_delivery_options_local_label', + 'test_delivery_options_survives_the_authority_module_move', # Skills endpoints (need tools.skills_tool module) 'test_skills_list', 'test_skills_list_has_required_fields', diff --git a/tests/test_cron_delivery_options.py b/tests/test_cron_delivery_options.py index 9e140ab8..d6ffe358 100644 --- a/tests/test_cron_delivery_options.py +++ b/tests/test_cron_delivery_options.py @@ -65,3 +65,43 @@ def test_delivery_options_local_label(): local_entry = next(p for p in result["platforms"] if p["value"] == "local") # Label should contain "Local" or be an i18n key — just verify it's non-empty assert local_entry["label"], "Local platform label is empty" + + +def test_delivery_options_survives_the_authority_module_move(): + """The platform list must not silently empty when the Agent relocates it. + + ``_KNOWN_DELIVERY_PLATFORMS`` used to live in ``cron.scheduler`` and now + lives in ``cron.scheduler_delivery``. The endpoint previously imported the + old path inside a bare ``except`` and fell back to an EMPTY frozenset, so + the move silently degraded the cron delivery picker to local/origin only — + every messaging platform (telegram, discord, slack, feishu, ...) vanished + from the UI with no error anywhere. Pin the resolution order so a future + relocation fails loudly instead of silently dropping platforms. + + Registered in ``_AGENT_DEPENDENT_TESTS`` (tests/conftest.py) alongside the + other delivery-options tests: CI runs agent-free, so there is no ``cron`` + package and no authority to resolve there. + """ + import importlib + + resolved = frozenset() + for module_name in ("cron.scheduler_delivery", "cron.scheduler"): + try: + mod = importlib.import_module(module_name) + except Exception: + continue + known = getattr(mod, "_KNOWN_DELIVERY_PLATFORMS", None) + if known: + resolved = frozenset(known) + break + + assert resolved, ( + "_KNOWN_DELIVERY_PLATFORMS resolved empty from every known module path " + "— the cron delivery picker would silently show only local/origin" + ) + # The endpoint's own output must agree with the resolved authority. + result, status = get("/api/crons/delivery-options") + assert status == 200 + values = {p["value"] for p in result["platforms"]} + missing = resolved - values + assert not missing, f"platforms resolved but absent from the endpoint: {sorted(missing)}"