mirror of
https://github.com/rennf93/roboco.git
synced 2026-08-03 07:23:24 +02:00
fix(gateway): open_session passes model schema, not API schema
Smoke run 4 crashed with AttributeError: 'SessionForTasksCreateRequest' object has no attribute 'config' when main-pm called open_session. The service _build_session_request reads req.config.max_message_count (model has nested config). The gateway was passing the API schema SessionForTasksCreateRequest (flat fields, no config attribute at all) — so `req.config` blew up with AttributeError, not the safer None. Fix: gateway now constructs SessionForTasksCreate (the model) with the enum-typed relationship_type, matching what the route at roboco/api/routes/sessions.py:230 does. Unknown relationship strings fall back to DISCUSSION. Two regression tests pin the contract: service receives the model; invalid relationship_type defaults to DISCUSSION.
This commit is contained in:
@@ -0,0 +1,99 @@
|
||||
"""Smoke-4: open_session must pass the model schema (not the API schema) to
|
||||
the messaging service.
|
||||
|
||||
Smoke run 4 surfaced an AttributeError because content_actions.open_session
|
||||
constructed a `SessionForTasksCreateRequest` (API schema, flat fields) but
|
||||
`MessagingService._build_session_request` reads `req.config.max_message_count`
|
||||
(the model has nested `config`). Two schemas, one expected, one passed.
|
||||
|
||||
This test pins the contract: the gateway constructs the model and the
|
||||
service receives the model.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from unittest.mock import AsyncMock, MagicMock
|
||||
from uuid import uuid4
|
||||
|
||||
import pytest
|
||||
from roboco.models.session import SessionForTasksCreate, SessionTaskRelationshipType
|
||||
from roboco.services.gateway.content_actions import ContentActions, ContentActionsDeps
|
||||
|
||||
|
||||
def _make_deps(**overrides: object) -> ContentActionsDeps:
|
||||
base: dict[str, object] = {
|
||||
"task": AsyncMock(),
|
||||
"git": AsyncMock(),
|
||||
"messaging": AsyncMock(),
|
||||
"a2a": AsyncMock(),
|
||||
"journal": AsyncMock(),
|
||||
"workspace": AsyncMock(),
|
||||
"notifications": AsyncMock(),
|
||||
}
|
||||
base.update(overrides)
|
||||
return ContentActionsDeps(**base)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_open_session_passes_model_schema_to_service() -> None:
|
||||
"""The service must receive a SessionForTasksCreate (model), not the API schema."""
|
||||
agent_id = uuid4()
|
||||
task_id = uuid4()
|
||||
pm_agent = MagicMock(role="cell_pm")
|
||||
|
||||
task_svc = AsyncMock()
|
||||
task_svc.agent_for.return_value = pm_agent
|
||||
|
||||
session_row = MagicMock(id=uuid4())
|
||||
messaging_svc = AsyncMock()
|
||||
messaging_svc.create_session_for_tasks.return_value = (session_row, [])
|
||||
|
||||
deps = _make_deps(task=task_svc, messaging=messaging_svc)
|
||||
actions = ContentActions(deps)
|
||||
|
||||
env = await actions.open_session(
|
||||
agent_id=agent_id,
|
||||
task_id=task_id,
|
||||
channel="backend-cell",
|
||||
topic="discuss the plan",
|
||||
relationship_type="discussion",
|
||||
group_id=None,
|
||||
)
|
||||
|
||||
assert env.error is None, (
|
||||
f"open_session returned error: {env.error} / {env.message}"
|
||||
)
|
||||
messaging_svc.create_session_for_tasks.assert_awaited_once()
|
||||
call = messaging_svc.create_session_for_tasks.await_args
|
||||
passed_req = call.kwargs["req"]
|
||||
assert isinstance(passed_req, SessionForTasksCreate), (
|
||||
f"expected SessionForTasksCreate (model), got {type(passed_req).__name__}. "
|
||||
f"The service reads req.config.* which only exists on the model."
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_open_session_unknown_relationship_falls_back_to_discussion() -> None:
|
||||
"""Invalid relationship_type string defaults to DISCUSSION enum."""
|
||||
pm_agent = MagicMock(role="main_pm")
|
||||
task_svc = AsyncMock()
|
||||
task_svc.agent_for.return_value = pm_agent
|
||||
|
||||
session_row = MagicMock(id=uuid4())
|
||||
messaging_svc = AsyncMock()
|
||||
messaging_svc.create_session_for_tasks.return_value = (session_row, [])
|
||||
|
||||
deps = _make_deps(task=task_svc, messaging=messaging_svc)
|
||||
actions = ContentActions(deps)
|
||||
|
||||
await actions.open_session(
|
||||
agent_id=uuid4(),
|
||||
task_id=uuid4(),
|
||||
channel="backend-cell",
|
||||
topic="discuss",
|
||||
relationship_type="nonsense-type-not-in-enum",
|
||||
group_id=None,
|
||||
)
|
||||
|
||||
passed_req = messaging_svc.create_session_for_tasks.await_args.kwargs["req"]
|
||||
assert passed_req.relationship_type == SessionTaskRelationshipType.DISCUSSION
|
||||
Reference in New Issue
Block a user