fix: send Scheduler API Bearer auth from the task registrar

The Scheduler's task-management endpoints (GET/POST /tasks, PUT
/tasks/{name}) are guarded by verify_api_key, but execute() built a bare
httpx.Client with no Authorization header: the existence probe 401'd
(misread as 'task absent') and every POST/PUT registration failed, so
--execute was never runnable end-to-end against the real Scheduler.

--execute now requires SCHEDULER_API_KEY from the environment (never
stored) and sends Authorization: Bearer on all registrar HTTP calls.
Deploy notes updated alongside the LIBRARY_API_KEY requirement.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QbFZyDvYksazX6nYQYZ67L
This commit is contained in:
2026-07-14 15:13:16 +02:00
co-authored by Claude Fable 5
parent 9ceec1464a
commit b4a5a92fee
4 changed files with 85 additions and 3 deletions
+51
View File
@@ -128,3 +128,54 @@ class TestSchedulerTaskDefinitions:
assert "DRY RUN" in out
assert "library_integrity_check" in out
assert mod.API_KEY_PLACEHOLDER in out # placeholder, never a real key
def test_execute_requires_scheduler_api_key(self, capsys, monkeypatch):
"""--execute must refuse to run without SCHEDULER_API_KEY (the
Scheduler's task endpoints are Bearer-guarded; without the key
every probe 401s and registration silently fails)."""
mod = self._load_module()
monkeypatch.setattr(
"sys.argv", ["register_scheduler_tasks.py", "--execute"]
)
monkeypatch.setenv("SCHEDULER_URL", "http://scheduler.test:8090")
monkeypatch.delenv("SCHEDULER_API_KEY", raising=False)
def _boom(*args, **kwargs):
raise AssertionError("must not contact the Scheduler without a key")
monkeypatch.setattr(mod.httpx, "Client", _boom)
assert mod.main() == 1
assert "SCHEDULER_API_KEY" in capsys.readouterr().out
def test_execute_sends_scheduler_bearer_auth(self, monkeypatch):
"""The registrar's own HTTP client must carry
Authorization: Bearer $SCHEDULER_API_KEY on every call."""
import httpx as real_httpx
mod = self._load_module()
seen = {"auth_headers": [], "paths": []}
def handler(request: real_httpx.Request) -> real_httpx.Response:
seen["auth_headers"].append(request.headers.get("Authorization"))
path = request.url.path
seen["paths"].append(f"{request.method} {path}")
if path == "/health":
return real_httpx.Response(200, json={"status": "healthy"})
if request.method == "GET" and path.startswith("/tasks/"):
return real_httpx.Response(404) # not registered yet
return real_httpx.Response(200, json={"ok": True})
real_client = real_httpx.Client
def client_factory(**kwargs):
kwargs["transport"] = real_httpx.MockTransport(handler)
return real_client(**kwargs)
monkeypatch.setattr(mod.httpx, "Client", client_factory)
assert mod.execute("http://scheduler.test:8090", "sched-key-123") == 0
assert seen["auth_headers"], "no HTTP calls were made"
assert all(h == "Bearer sched-key-123" for h in seen["auth_headers"])
# All three tasks created (404 probe -> POST /tasks)
assert seen["paths"].count("POST /tasks") == len(mod.TASKS)