refactor(schedule): move orchestrator to application/ + review fixes (stage 8)
Follow-up to the layered refactor (review-driven):
- Move scheduling/orchestrator.py -> application/orchestrator.py
(orchestrator is application-level coordination, not a cron-trigger
primitive; matches the intended target tree)
- Migrate orchestrator re-exports from scheduling/__init__.py to
application/__init__.py; scheduling/ now exposes only CronScheduler
- Rewrite imports + 5 mock.patch string targets in test_janitor.py and
the orchestrator import in test_layering.py
- Update docstring refs in application/service.py + execution/worker.py
- Add 4 runner smoke tests (test_layering.py): _limited_log under-limit /
empty-sentinel / above-MAX_LOG_BYTES truncation; execute_artifact
rejects unsupported script_type with ValueError
- infrastructure/__init__.py re-exports SchedulerStorageClient so
``from schedule.infrastructure import SchedulerStorageClient`` is a
stable top-level surface
- CLAUDE.md engineering note: extend the commit trail to 7476c27 and
note the stage-8 orchestrator placement
Zero behavior change; schedule/pyproject.toml untouched. 29 tests green.
Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -36,7 +36,7 @@ from schedule.domain.context import (
|
||||
from schedule.domain.execution import ExecutionResult
|
||||
from schedule.execution import NodeExecutor
|
||||
from schedule.infrastructure.storage import SchedulerStorageClient
|
||||
from schedule.scheduling.orchestrator import DispatchOrchestrator
|
||||
from schedule.application.orchestrator import DispatchOrchestrator
|
||||
from schedule.scheduling.scheduler import CronScheduler
|
||||
|
||||
|
||||
@@ -175,3 +175,48 @@ def test_notebook_runner_shim_reexports_real_main() -> None:
|
||||
from schedule.notebook_runner import main as shim_main
|
||||
|
||||
assert shim_main is real.main
|
||||
|
||||
|
||||
# ── execution/runners: stage-4 merge boundary smoke (F4) ──────────────────
|
||||
|
||||
|
||||
def test_limited_log_under_limit_returns_encoded_unchanged() -> None:
|
||||
from schedule.execution.runners import notebook
|
||||
|
||||
assert notebook._limited_log("hello") == b"hello"
|
||||
|
||||
|
||||
def test_limited_log_empty_returns_sentinel() -> None:
|
||||
from schedule.execution.runners import notebook
|
||||
|
||||
assert notebook._limited_log("") == b"execution produced no console output\n"
|
||||
|
||||
|
||||
def test_limited_log_above_max_bytes_truncates_with_suffix() -> None:
|
||||
from schedule.execution.runners import notebook
|
||||
|
||||
sentinel = b"\n[log truncated by scheduler worker]\n"
|
||||
overflowing = "x" * (notebook.MAX_LOG_BYTES + 1)
|
||||
truncated = notebook._limited_log(overflowing)
|
||||
assert len(truncated) == notebook.MAX_LOG_BYTES
|
||||
assert truncated.endswith(sentinel)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_execute_artifact_unsupported_script_type_raises() -> None:
|
||||
"""``script_type`` outside ``{"notebook", "python"}`` must raise —
|
||||
otherwise the worker would silently drop a malformed run into the
|
||||
queue and the node would hang.
|
||||
"""
|
||||
from schedule.execution.runners import notebook
|
||||
|
||||
with pytest.raises(ValueError, match="unsupported script_type"):
|
||||
await notebook.execute_artifact(
|
||||
source=b"print('hi')\n",
|
||||
run_id="01RUN0000000000000000000A",
|
||||
node_run_id="01NODE000000000000000000A",
|
||||
script_type="bogus",
|
||||
artifact_path="x.py",
|
||||
arguments=[],
|
||||
timeout_seconds=30,
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user