refactor: migrate legacy cron payloads to bound sessions
This commit is contained in:
+111
-20
@@ -43,7 +43,7 @@ def test_add_job_accepts_valid_timezone(tmp_path) -> None:
|
||||
assert job.state.next_run_at_ms is not None
|
||||
|
||||
|
||||
def test_add_job_preserves_channel_meta_and_session_key(tmp_path) -> None:
|
||||
def test_add_job_migrates_legacy_delivery_context(tmp_path) -> None:
|
||||
service = CronService(tmp_path / "cron" / "jobs.json")
|
||||
meta = {"slack": {"thread_ts": "1234567890.123456", "channel_type": "channel"}}
|
||||
job = service.add_job(
|
||||
@@ -56,16 +56,108 @@ def test_add_job_preserves_channel_meta_and_session_key(tmp_path) -> None:
|
||||
channel_meta=meta,
|
||||
session_key="slack:C123:1234567890.123456",
|
||||
)
|
||||
assert job.payload.channel_meta == meta
|
||||
assert job.payload.deliver is False
|
||||
assert job.payload.channel is None
|
||||
assert job.payload.to is None
|
||||
assert job.payload.channel_meta == {}
|
||||
assert job.payload.session_key == "slack:C123:1234567890.123456"
|
||||
assert job.payload.origin_channel == "slack"
|
||||
assert job.payload.origin_chat_id == "C123"
|
||||
assert job.payload.origin_metadata == meta
|
||||
|
||||
reloaded = service.get_job(job.id)
|
||||
assert reloaded is not None
|
||||
assert reloaded.payload.channel_meta == meta
|
||||
assert reloaded.payload.channel_meta == {}
|
||||
assert reloaded.payload.session_key == "slack:C123:1234567890.123456"
|
||||
assert reloaded.payload.origin_channel == "slack"
|
||||
assert reloaded.payload.origin_chat_id == "C123"
|
||||
assert reloaded.payload.origin_metadata == meta
|
||||
|
||||
|
||||
def test_list_bound_agent_jobs_excludes_legacy_delivery_payloads(tmp_path) -> None:
|
||||
def test_load_store_migrates_legacy_delivery_context(tmp_path) -> None:
|
||||
store_path = tmp_path / "cron" / "jobs.json"
|
||||
store_path.parent.mkdir(parents=True)
|
||||
store_path.write_text(
|
||||
json.dumps(
|
||||
{
|
||||
"version": 1,
|
||||
"jobs": [
|
||||
{
|
||||
"id": "legacy-1",
|
||||
"name": "Legacy reminder",
|
||||
"enabled": True,
|
||||
"schedule": {"kind": "every", "everyMs": 60_000},
|
||||
"payload": {
|
||||
"kind": "agent_turn",
|
||||
"message": "check status",
|
||||
"deliver": True,
|
||||
"channel": "telegram",
|
||||
"to": "user-1",
|
||||
"channelMeta": {"message_thread_id": 42},
|
||||
"sessionKey": "telegram:user-1:topic:42",
|
||||
},
|
||||
"state": {},
|
||||
"createdAtMs": 1,
|
||||
"updatedAtMs": 1,
|
||||
}
|
||||
],
|
||||
}
|
||||
),
|
||||
encoding="utf-8",
|
||||
)
|
||||
|
||||
job = CronService(store_path).get_job("legacy-1")
|
||||
|
||||
assert job is not None
|
||||
assert job.payload.session_key == "telegram:user-1:topic:42"
|
||||
assert job.payload.origin_channel == "telegram"
|
||||
assert job.payload.origin_chat_id == "user-1"
|
||||
assert job.payload.origin_metadata == {"message_thread_id": 42}
|
||||
assert job.payload.deliver is False
|
||||
assert job.payload.channel is None
|
||||
assert job.payload.to is None
|
||||
assert job.payload.channel_meta == {}
|
||||
|
||||
|
||||
def test_load_store_disables_malformed_legacy_payload(tmp_path) -> None:
|
||||
store_path = tmp_path / "cron" / "jobs.json"
|
||||
store_path.parent.mkdir(parents=True)
|
||||
store_path.write_text(
|
||||
json.dumps(
|
||||
{
|
||||
"version": 1,
|
||||
"jobs": [
|
||||
{
|
||||
"id": "legacy-bad",
|
||||
"name": "Broken legacy",
|
||||
"enabled": True,
|
||||
"schedule": {"kind": "every", "everyMs": 60_000},
|
||||
"payload": {
|
||||
"kind": "agent_turn",
|
||||
"message": "check status",
|
||||
"deliver": True,
|
||||
},
|
||||
"state": {"nextRunAtMs": 123},
|
||||
"createdAtMs": 1,
|
||||
"updatedAtMs": 1,
|
||||
}
|
||||
],
|
||||
}
|
||||
),
|
||||
encoding="utf-8",
|
||||
)
|
||||
|
||||
job = CronService(store_path).get_job("legacy-bad")
|
||||
|
||||
assert job is not None
|
||||
assert job.enabled is False
|
||||
assert job.state.next_run_at_ms is None
|
||||
assert job.state.last_status == "error"
|
||||
assert "missing channel/to" in (job.state.last_error or "")
|
||||
assert job.payload.deliver is False
|
||||
|
||||
|
||||
def test_list_bound_agent_jobs_includes_migrated_legacy_delivery_payloads(tmp_path) -> None:
|
||||
service = CronService(tmp_path / "cron" / "jobs.json")
|
||||
schedule = CronSchedule(kind="every", every_ms=60_000)
|
||||
bound = service.add_job(
|
||||
@@ -73,8 +165,10 @@ def test_list_bound_agent_jobs_excludes_legacy_delivery_payloads(tmp_path) -> No
|
||||
schedule=schedule,
|
||||
message="new bound job",
|
||||
session_key="websocket:chat-1",
|
||||
origin_channel="websocket",
|
||||
origin_chat_id="chat-1",
|
||||
)
|
||||
service.add_job(
|
||||
migrated = service.add_job(
|
||||
name="Legacy same session",
|
||||
schedule=schedule,
|
||||
message="legacy job",
|
||||
@@ -84,7 +178,7 @@ def test_list_bound_agent_jobs_excludes_legacy_delivery_payloads(tmp_path) -> No
|
||||
session_key="websocket:chat-1",
|
||||
)
|
||||
|
||||
assert service.list_bound_cron_jobs_for_session("websocket:chat-1") == [bound]
|
||||
assert service.list_bound_cron_jobs_for_session("websocket:chat-1") == [bound, migrated]
|
||||
|
||||
|
||||
def test_add_job_preserves_origin_delivery_context(tmp_path) -> None:
|
||||
@@ -143,7 +237,10 @@ async def test_channel_meta_and_session_key_survive_store_reload(tmp_path) -> No
|
||||
|
||||
raw = json.loads(store_path.read_text(encoding="utf-8"))
|
||||
payload = raw["jobs"][0]["payload"]
|
||||
assert payload["channelMeta"] == meta
|
||||
assert payload["deliver"] is False
|
||||
assert payload["channel"] is None
|
||||
assert payload["to"] is None
|
||||
assert payload["channelMeta"] == {}
|
||||
assert payload["sessionKey"] == "slack:C123:1234567890.123456"
|
||||
assert payload["originChannel"] == "slack"
|
||||
assert payload["originChatId"] == "C123"
|
||||
@@ -151,7 +248,7 @@ async def test_channel_meta_and_session_key_survive_store_reload(tmp_path) -> No
|
||||
|
||||
reloaded = CronService(store_path).get_job(job.id)
|
||||
assert reloaded is not None
|
||||
assert reloaded.payload.channel_meta == meta
|
||||
assert reloaded.payload.channel_meta == {}
|
||||
assert reloaded.payload.session_key == "slack:C123:1234567890.123456"
|
||||
assert reloaded.payload.origin_channel == "slack"
|
||||
assert reloaded.payload.origin_chat_id == "C123"
|
||||
@@ -648,28 +745,22 @@ def test_update_job_offline_writes_action(tmp_path) -> None:
|
||||
assert last["params"]["name"] == "updated-offline"
|
||||
|
||||
|
||||
def test_update_job_sentinel_channel_and_to(tmp_path) -> None:
|
||||
"""Passing None clears channel/to; omitting leaves them unchanged."""
|
||||
def test_update_job_migrates_legacy_delivery_target(tmp_path) -> None:
|
||||
service = CronService(tmp_path / "cron" / "jobs.json")
|
||||
job = service.add_job(
|
||||
name="sentinel",
|
||||
schedule=CronSchedule(kind="every", every_ms=60_000),
|
||||
message="hello",
|
||||
channel="telegram",
|
||||
to="user123",
|
||||
)
|
||||
assert job.payload.channel == "telegram"
|
||||
assert job.payload.to == "user123"
|
||||
|
||||
result = service.update_job(job.id, name="renamed")
|
||||
assert isinstance(result, CronJob)
|
||||
assert result.payload.channel == "telegram"
|
||||
assert result.payload.to == "user123"
|
||||
|
||||
result = service.update_job(job.id, channel=None, to=None)
|
||||
result = service.update_job(job.id, channel="telegram", to="user123")
|
||||
assert isinstance(result, CronJob)
|
||||
assert result.payload.session_key == "telegram:user123"
|
||||
assert result.payload.origin_channel == "telegram"
|
||||
assert result.payload.origin_chat_id == "user123"
|
||||
assert result.payload.channel is None
|
||||
assert result.payload.to is None
|
||||
assert result.payload.channel_meta == {}
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
|
||||
Reference in New Issue
Block a user