refactor: migrate legacy cron payloads to bound sessions

This commit is contained in:
chengyongru
2026-06-12 16:51:20 +08:00
parent af8192dc38
commit 8335554894
6 changed files with 210 additions and 431 deletions
+111 -20
View File
@@ -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