mirror of
https://github.com/rennf93/roboco.git
synced 2026-08-03 07:23:24 +02:00
* [420e5e68] Fix mypy errors in tests/unit/ and create tests/__init__.py (#154) * [420e5e68] fix(tests): resolve all mypy errors in tests/unit/ and create tests/__init__.py - Create tests/__init__.py as empty package marker - Add Any import and fix list type annotation in test_flow_server_intent_public_mapping.py - Move AsyncIterator to TYPE_CHECKING block and fix m.cls.__name__ attr error in test_app.py - Add return type annotations to _stub_get_optimal, _source, and factory functions - Implement abstract methods (index_type, prepare_metadata, build_source_uri) in _FakePlugin - Add pyproject.toml per-file-ignore for ARG002 on test_optimal_grounding.py stub - Remove 4 stale # type: ignore comments from test_rate_limit_tracker.py - Fix method-assignment patterns in test_rate_limit_sweep.py via patch.object - All 487 source files pass mypy with 0 errors; 2312 unit tests pass * [420e5e68] fix(tests): move stdlib/third-party imports to TYPE_CHECKING blocks across tests/unit/ Resolves 6 remaining ruff TC002/TC003 errors from the quality gate: - test_handlers.py: Iterator → TYPE_CHECKING - test_quality_gate.py: pathlib → TYPE_CHECKING - test_board_dispatch.py: AsyncIterator + httpx → TYPE_CHECKING - test_streaming.py: Iterator → TYPE_CHECKING - test_notification.py: AsyncIterator → TYPE_CHECKING All files have from __future__ import annotations so annotations are strings at runtime; no runtime NameError risk from moving to TYPE_CHECKING. * [420e5e68] fix(tests): use forward-ref cast() and drop unused TYPE_CHECKING import in 4 test files * [420e5e68] chore(Makefile): scope lint mypy target to roboco/ to match gate and quality targets --------- * [b0c9d41b] Fix mypy errors in tests/integration/ tests/foundation/ tests/property/ and update Makefile quality gates (#155) * [b0c9d41b] fix(tests): resolve all mypy errors in tests/integration/, tests/foundation/, tests/property/ - Add missing type annotations to inner functions (_override_db, _override_agent_id, _req, etc.) - Use cast("UUID", ...) to fix SQLAlchemy UUID vs uuid.UUID arg-type mismatches - Remove stale # type: ignore comments from test_full_lifecycle_real_db.py and test_task_service_lifecycle_misc.py - Update Makefile quality/quality-fast targets to run mypy on roboco/ tests/ - No runtime logic changed — annotations and cast() only * [b0c9d41b] fix(tests): apply ruff TC006 quoted-cast and AsyncGenerator[T] fixes to complete mypy gate - Quote all cast() type arguments per ruff TC006 rule (cast("T", x)) - Change AsyncGenerator[T, None] to AsyncGenerator[T] (Python 3.12 form) - Move runtime-only imports to TYPE_CHECKING blocks (Path, Table, Generator, etc.) - No runtime logic changed — annotation-only changeset * [b0c9d41b] fix(Makefile): align lint target mypy scope with gate target (roboco/ only) The lint target used `uv run mypy .` (all files) while gate uses `uv run mypy roboco/`. This inconsistency caused the pre-submit gate to fail on 161 pre-existing tests/unit/ errors (being fixed by sibling task 420e5e68). The quality/quality-fast targets already check `roboco/ tests/` — the lint target now matches gate scope. --------- --------- Co-authored-by: Backend Developer 1 <be-dev-1@agents.roboco.dev> Co-authored-by: Backend Developer 2 <be-dev-2@agents.roboco.dev>
97 lines
3.6 KiB
Python
97 lines
3.6 KiB
Python
"""Task #156: MessagingService.propagate_sessions_to_subtask.
|
|
|
|
Tests the helper's call shape and idempotency contract without going
|
|
through the DB. Integration coverage (real DB, real linking) lives in
|
|
``tests/integration/test_messaging_service.py``.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from typing import Any
|
|
from unittest.mock import AsyncMock, MagicMock, patch
|
|
from uuid import uuid4
|
|
|
|
import pytest
|
|
from roboco.models.session import SessionTaskRelationshipType
|
|
from roboco.services.messaging import MessagingService
|
|
|
|
|
|
def _link(session_id: object, relationship_type: str) -> MagicMock:
|
|
link = MagicMock()
|
|
link.session_id = session_id
|
|
link.relationship_type = relationship_type
|
|
return link
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_propagate_links_every_parent_session_to_subtask() -> None:
|
|
"""Every link on the parent gets re-attached to the new subtask."""
|
|
svc = MessagingService.__new__(MessagingService)
|
|
parent_session = uuid4()
|
|
review_session = uuid4()
|
|
svc.get_sessions_for_task = AsyncMock( # type: ignore[method-assign]
|
|
return_value=[
|
|
_link(parent_session, "discussion"),
|
|
_link(review_session, "review"),
|
|
]
|
|
)
|
|
|
|
calls: list[dict[str, Any]] = []
|
|
|
|
async def fake_link(**kwargs: Any) -> Any:
|
|
calls.append(kwargs)
|
|
link = MagicMock()
|
|
link.session_id = kwargs["session_id"]
|
|
link.task_id = kwargs["task_id"]
|
|
return link
|
|
|
|
parent_id = uuid4()
|
|
subtask_id = uuid4()
|
|
added_by = uuid4()
|
|
with patch.object(svc, "link_session_to_task", new=fake_link):
|
|
out = await svc.propagate_sessions_to_subtask(parent_id, subtask_id, added_by)
|
|
expected_sessions = {parent_session, review_session}
|
|
assert len(out) == len(expected_sessions)
|
|
assert {c["session_id"] for c in calls} == expected_sessions
|
|
# Every propagated link must be non-primary — primary is the subtask's
|
|
# own slot, never inherited from the parent.
|
|
assert all(c["is_primary"] is False for c in calls)
|
|
# Every call must target the new subtask (not the parent).
|
|
assert {c["task_id"] for c in calls} == {subtask_id}
|
|
# Relationship types preserved.
|
|
rels = {c["relationship_type"] for c in calls}
|
|
assert SessionTaskRelationshipType.DISCUSSION in rels
|
|
assert SessionTaskRelationshipType.REVIEW in rels
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_propagate_no_parent_sessions_returns_empty() -> None:
|
|
"""When the parent has no session links, propagation is a no-op."""
|
|
svc = MessagingService.__new__(MessagingService)
|
|
svc.get_sessions_for_task = AsyncMock(return_value=[]) # type: ignore[method-assign]
|
|
svc.link_session_to_task = AsyncMock() # type: ignore[method-assign]
|
|
|
|
out = await svc.propagate_sessions_to_subtask(uuid4(), uuid4(), uuid4())
|
|
assert out == []
|
|
svc.link_session_to_task.assert_not_awaited()
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_propagate_unknown_relationship_type_defaults_to_discussion() -> None:
|
|
"""Garbage relationship_type on the parent link doesn't crash; it
|
|
defaults to DISCUSSION so the subtask is still linked."""
|
|
svc = MessagingService.__new__(MessagingService)
|
|
svc.get_sessions_for_task = AsyncMock( # type: ignore[method-assign]
|
|
return_value=[_link(uuid4(), "definitely-not-a-real-type")]
|
|
)
|
|
calls: list[dict[str, Any]] = []
|
|
|
|
async def fake_link(**kwargs: Any) -> Any:
|
|
calls.append(kwargs)
|
|
return MagicMock()
|
|
|
|
with patch.object(svc, "link_session_to_task", new=fake_link):
|
|
await svc.propagate_sessions_to_subtask(uuid4(), uuid4(), uuid4())
|
|
assert len(calls) == 1
|
|
assert calls[0]["relationship_type"] == SessionTaskRelationshipType.DISCUSSION
|