refactor: store cron origin delivery context
This commit is contained in:
@@ -1611,6 +1611,8 @@ def test_gateway_bound_cron_runs_as_session_turn(
|
||||
payload=CronPayload(
|
||||
message="Check repository health.",
|
||||
session_key="websocket:chat-1",
|
||||
origin_channel="websocket",
|
||||
origin_chat_id="chat-1",
|
||||
),
|
||||
)
|
||||
|
||||
@@ -1646,6 +1648,13 @@ def test_gateway_bound_cron_runs_as_session_turn(
|
||||
payload=CronPayload(
|
||||
message="Check the Discord thread.",
|
||||
session_key="discord:456:thread:777",
|
||||
origin_channel="discord",
|
||||
origin_chat_id="777",
|
||||
origin_metadata={
|
||||
"context_chat_id": "456",
|
||||
"parent_channel_id": "456",
|
||||
"thread_id": "777",
|
||||
},
|
||||
),
|
||||
)
|
||||
|
||||
@@ -1667,6 +1676,9 @@ def test_gateway_bound_cron_runs_as_session_turn(
|
||||
payload=CronPayload(
|
||||
message="Check the Telegram topic.",
|
||||
session_key="telegram:-100123:topic:42",
|
||||
origin_channel="telegram",
|
||||
origin_chat_id="-100123",
|
||||
origin_metadata={"message_thread_id": 42},
|
||||
),
|
||||
)
|
||||
|
||||
@@ -1686,6 +1698,13 @@ def test_gateway_bound_cron_runs_as_session_turn(
|
||||
payload=CronPayload(
|
||||
message="Check the Feishu topic.",
|
||||
session_key="feishu:oc_abc:om_root123",
|
||||
origin_channel="feishu",
|
||||
origin_chat_id="oc_abc",
|
||||
origin_metadata={
|
||||
"chat_type": "group",
|
||||
"message_id": "om_root123",
|
||||
"thread_id": "om_root123",
|
||||
},
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
@@ -87,6 +87,37 @@ def test_list_bound_agent_jobs_excludes_legacy_delivery_payloads(tmp_path) -> No
|
||||
assert service.list_bound_cron_jobs_for_session("websocket:chat-1") == [bound]
|
||||
|
||||
|
||||
def test_add_job_preserves_origin_delivery_context(tmp_path) -> None:
|
||||
service = CronService(tmp_path / "cron" / "jobs.json")
|
||||
metadata = {"slack": {"thread_ts": "1234567890.123456", "channel_type": "channel"}}
|
||||
|
||||
job = service.add_job(
|
||||
name="bound thread",
|
||||
schedule=CronSchedule(kind="every", every_ms=60_000),
|
||||
message="hello",
|
||||
session_key="slack:C123:1234567890.123456",
|
||||
origin_channel="slack",
|
||||
origin_chat_id="C123",
|
||||
origin_metadata=metadata,
|
||||
)
|
||||
|
||||
assert job.payload.origin_channel == "slack"
|
||||
assert job.payload.origin_chat_id == "C123"
|
||||
assert job.payload.origin_metadata == metadata
|
||||
|
||||
raw = json.loads((tmp_path / "cron" / "action.jsonl").read_text(encoding="utf-8"))
|
||||
payload = raw["params"]["payload"]
|
||||
assert payload["origin_channel"] == "slack"
|
||||
assert payload["origin_chat_id"] == "C123"
|
||||
assert payload["origin_metadata"] == metadata
|
||||
|
||||
reloaded = service.get_job(job.id)
|
||||
assert reloaded is not None
|
||||
assert reloaded.payload.origin_channel == "slack"
|
||||
assert reloaded.payload.origin_chat_id == "C123"
|
||||
assert reloaded.payload.origin_metadata == metadata
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_channel_meta_and_session_key_survive_store_reload(tmp_path) -> None:
|
||||
store_path = tmp_path / "cron" / "jobs.json"
|
||||
@@ -103,6 +134,9 @@ async def test_channel_meta_and_session_key_survive_store_reload(tmp_path) -> No
|
||||
to="C123",
|
||||
channel_meta=meta,
|
||||
session_key="slack:C123:1234567890.123456",
|
||||
origin_channel="slack",
|
||||
origin_chat_id="C123",
|
||||
origin_metadata=meta,
|
||||
)
|
||||
finally:
|
||||
service.stop()
|
||||
@@ -111,11 +145,17 @@ async def test_channel_meta_and_session_key_survive_store_reload(tmp_path) -> No
|
||||
payload = raw["jobs"][0]["payload"]
|
||||
assert payload["channelMeta"] == meta
|
||||
assert payload["sessionKey"] == "slack:C123:1234567890.123456"
|
||||
assert payload["originChannel"] == "slack"
|
||||
assert payload["originChatId"] == "C123"
|
||||
assert payload["originMetadata"] == meta
|
||||
|
||||
reloaded = CronService(store_path).get_job(job.id)
|
||||
assert reloaded is not None
|
||||
assert reloaded.payload.channel_meta == 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
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
|
||||
@@ -339,6 +339,9 @@ def test_add_job_binds_current_session_key(tmp_path) -> None:
|
||||
assert result.startswith("Created job")
|
||||
job = tool._cron.list_jobs()[0]
|
||||
assert job.payload.session_key == "telegram:chat-1"
|
||||
assert job.payload.origin_channel == "telegram"
|
||||
assert job.payload.origin_chat_id == "chat-1"
|
||||
assert job.payload.origin_metadata == {}
|
||||
assert job.payload.channel is None
|
||||
assert job.payload.to is None
|
||||
|
||||
@@ -392,8 +395,8 @@ def test_add_job_empty_message_returns_actionable_error(tmp_path) -> None:
|
||||
assert "Retry including message=" in result
|
||||
|
||||
|
||||
def test_add_job_captures_only_session_key(tmp_path) -> None:
|
||||
"""CronTool stores the canonical session key without legacy delivery fields."""
|
||||
def test_add_job_captures_owner_and_origin_without_legacy_delivery_fields(tmp_path) -> None:
|
||||
"""CronTool stores owner/session identity separately from origin delivery context."""
|
||||
tool = _make_tool(tmp_path)
|
||||
meta = {"slack": {"thread_ts": "111.222", "channel_type": "channel"}}
|
||||
tool.set_context(RequestContext(
|
||||
@@ -406,6 +409,9 @@ def test_add_job_captures_only_session_key(tmp_path) -> None:
|
||||
jobs = tool._cron.list_jobs()
|
||||
assert len(jobs) == 1
|
||||
assert jobs[0].payload.session_key == "slack:C99:111.222"
|
||||
assert jobs[0].payload.origin_channel == "slack"
|
||||
assert jobs[0].payload.origin_chat_id == "C99"
|
||||
assert jobs[0].payload.origin_metadata == meta
|
||||
assert jobs[0].payload.channel is None
|
||||
assert jobs[0].payload.to is None
|
||||
assert jobs[0].payload.channel_meta == {}
|
||||
|
||||
@@ -1,45 +1,44 @@
|
||||
import pytest
|
||||
|
||||
from nanobot.cron.session_delivery import bound_session_inbound_context
|
||||
from nanobot.cron.session_delivery import origin_delivery_context
|
||||
from nanobot.cron.types import CronJob, CronPayload
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("session_key", "expected"),
|
||||
[
|
||||
("websocket:chat-1", ("websocket", "chat-1", {})),
|
||||
(
|
||||
"discord:456:thread:777",
|
||||
(
|
||||
"discord",
|
||||
"777",
|
||||
{
|
||||
"context_chat_id": "456",
|
||||
"parent_channel_id": "456",
|
||||
"thread_id": "777",
|
||||
},
|
||||
),
|
||||
def test_origin_delivery_context_uses_explicit_origin_fields() -> None:
|
||||
metadata = {
|
||||
"context_chat_id": "456",
|
||||
"parent_channel_id": "456",
|
||||
"thread_id": "777",
|
||||
}
|
||||
job = CronJob(
|
||||
id="thread-check",
|
||||
name="Thread check",
|
||||
payload=CronPayload(
|
||||
message="check",
|
||||
session_key="discord:456:thread:777",
|
||||
origin_channel="discord",
|
||||
origin_chat_id="777",
|
||||
origin_metadata=metadata,
|
||||
),
|
||||
(
|
||||
"feishu:oc_abc:om_root123",
|
||||
(
|
||||
"feishu",
|
||||
"oc_abc",
|
||||
{
|
||||
"chat_type": "group",
|
||||
"message_id": "om_root123",
|
||||
"thread_id": "om_root123",
|
||||
},
|
||||
),
|
||||
)
|
||||
|
||||
channel, chat_id, returned_metadata = origin_delivery_context(job)
|
||||
|
||||
assert channel == "discord"
|
||||
assert chat_id == "777"
|
||||
assert returned_metadata == metadata
|
||||
assert returned_metadata is not metadata
|
||||
|
||||
|
||||
def test_origin_delivery_context_rejects_missing_origin_fields() -> None:
|
||||
job = CronJob(
|
||||
id="old-bound",
|
||||
name="Old bound job",
|
||||
payload=CronPayload(
|
||||
message="check",
|
||||
session_key="websocket:chat-1",
|
||||
),
|
||||
("slack:C123:1700.42", ("slack", "C123", {"slack": {"thread_ts": "1700.42"}})),
|
||||
("telegram:-100123:topic:42", ("telegram", "-100123", {"message_thread_id": 42})),
|
||||
("dingtalk:group:conv-1:user-1", ("dingtalk", "group:conv-1", {})),
|
||||
],
|
||||
)
|
||||
def test_bound_session_inbound_context(session_key, expected) -> None:
|
||||
assert bound_session_inbound_context(session_key) == expected
|
||||
)
|
||||
|
||||
|
||||
def test_bound_session_inbound_context_rejects_invalid_key() -> None:
|
||||
with pytest.raises(ValueError):
|
||||
bound_session_inbound_context("unified")
|
||||
with pytest.raises(ValueError, match="missing origin delivery context"):
|
||||
origin_delivery_context(job)
|
||||
|
||||
@@ -123,6 +123,10 @@ async def test_cron_tool_keeps_task_local_context(tmp_path) -> None:
|
||||
|
||||
jobs = tool._cron.list_jobs()
|
||||
assert {job.payload.session_key for job in jobs} == {"feishu:chat-a", "email:chat-b"}
|
||||
assert {(job.payload.origin_channel, job.payload.origin_chat_id) for job in jobs} == {
|
||||
("feishu", "chat-a"),
|
||||
("email", "chat-b"),
|
||||
}
|
||||
|
||||
|
||||
# --- Basic single-task regression tests ---
|
||||
@@ -243,6 +247,8 @@ async def test_cron_tool_basic_set_context_and_execute(tmp_path) -> None:
|
||||
jobs = tool._cron.list_jobs()
|
||||
assert len(jobs) == 1
|
||||
assert jobs[0].payload.session_key == "wechat:user-789"
|
||||
assert jobs[0].payload.origin_channel == "wechat"
|
||||
assert jobs[0].payload.origin_chat_id == "user-789"
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@@ -272,6 +278,9 @@ async def test_webui_cron_tool_uses_origin_session_when_unified_enabled(tmp_path
|
||||
jobs = tool._cron.list_jobs()
|
||||
assert len(jobs) == 1
|
||||
assert jobs[0].payload.session_key == "websocket:chat-123"
|
||||
assert jobs[0].payload.origin_channel == "websocket"
|
||||
assert jobs[0].payload.origin_chat_id == "chat-123"
|
||||
assert jobs[0].payload.origin_metadata == {"webui": True}
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@@ -293,6 +302,9 @@ async def test_cron_tool_preserves_thread_scoped_session_key(tmp_path) -> None:
|
||||
jobs = tool._cron.list_jobs()
|
||||
assert len(jobs) == 1
|
||||
assert jobs[0].payload.session_key == "slack:C123:1700.42"
|
||||
assert jobs[0].payload.origin_channel == "slack"
|
||||
assert jobs[0].payload.origin_chat_id == "C123"
|
||||
assert jobs[0].payload.origin_metadata == {"slack": {"thread_ts": "1700.42"}}
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
|
||||
Reference in New Issue
Block a user