From 592c84da5d24b6b9e002a10fc0fa2e3bb4b2488a Mon Sep 17 00:00:00 2001 From: Renn F Date: Mon, 29 Jun 2026 02:33:09 +0200 Subject: [PATCH] [fix] resolve 16 mypy errors across 9 test files (make quality gate) type-clean the test files so make quality (mypy roboco/ tests/) is green: - Any-typed locals for the two TypeError-asserting scoping tests (bypass the required-arg check without getattr/ruff B009) - Any-typed view for the shutdown-drain _drain_bg_tasks override (bypass mypy method-assign without setattr/ruff B010) - cast("uuid.UUID", ...) / cast("UUID", ...) for SQLAlchemy UUID[Any] returns (TC006-quoted), config=None for AgentInstance stubs, None-narrowed await_args, Iterator return on a yielding fixture, UUID annotation on the _task helper. No type:ignore / noqa. --- .../test_active_task_owns_branch_scoping.py | 8 +++++--- tests/integration/test_messaging_service.py | 16 +++++++++++----- .../test_notification_delivery_phantom.py | 6 +++--- .../runtime/test_orchestrator_shutdown_drain.py | 5 ++++- tests/unit/runtime/test_parked_spawn_shortcut.py | 8 +++++--- .../runtime/test_reaper_subprocess_timeout.py | 4 +++- .../runtime/test_secretary_spawn_shutdown.py | 7 +++++-- .../test_git_close_pull_request_scoping.py | 5 ++++- .../unit/services/test_git_pr_target_scoping.py | 5 ++++- 9 files changed, 44 insertions(+), 20 deletions(-) diff --git a/tests/integration/services/test_active_task_owns_branch_scoping.py b/tests/integration/services/test_active_task_owns_branch_scoping.py index 6b95b451..3c1a2b43 100644 --- a/tests/integration/services/test_active_task_owns_branch_scoping.py +++ b/tests/integration/services/test_active_task_owns_branch_scoping.py @@ -71,7 +71,7 @@ async def _seed_project(db: AsyncSession, slug: str) -> ProjectTable: return project -def _task(project_id, *, branch: str, status: TaskStatus) -> TaskTable: +def _task(project_id: UUID, *, branch: str, status: TaskStatus) -> TaskTable: return TaskTable( id=uuid4(), title=f"task {branch}", @@ -102,8 +102,10 @@ async def test_branch_owned_only_by_its_own_project(db_session: AsyncSession) -> proj_b = await _seed_project(db_session, "gca-collide-b") db_session.add_all( [ - _task(proj_a.id, branch=_BRANCH, status=TaskStatus.IN_PROGRESS), - _task(proj_b.id, branch=_BRANCH, status=TaskStatus.COMPLETED), + _task( + cast("UUID", proj_a.id), branch=_BRANCH, status=TaskStatus.IN_PROGRESS + ), + _task(cast("UUID", proj_b.id), branch=_BRANCH, status=TaskStatus.COMPLETED), ] ) await db_session.flush() diff --git a/tests/integration/test_messaging_service.py b/tests/integration/test_messaging_service.py index b4de1c4e..249dbec9 100644 --- a/tests/integration/test_messaging_service.py +++ b/tests/integration/test_messaging_service.py @@ -1170,14 +1170,20 @@ async def _seed_messages_same_timestamp( timestamp so the equal-timestamp pagination skip is reproducible. Returns ``(session_id, message_ids)``.""" ch = await svc.create_channel(_channel_req(uuid4().hex[:6])) - grp = await svc.create_group(GroupCreateRequest(name="g1", channel_id=ch.id)) - sess = await svc.create_session(SessionCreateRequest(group_id=grp.id)) + grp = await svc.create_group( + GroupCreateRequest(name="g1", channel_id=cast("uuid.UUID", ch.id)) + ) + sess = await svc.create_session( + SessionCreateRequest(group_id=cast("uuid.UUID", grp.id)) + ) ids: list[UUID] = [] for i in range(count): m = await svc.send_message( - MessageCreateRequest(agent_id=aid, session_id=sess.id, content=f"m-{i}") + MessageCreateRequest( + agent_id=aid, session_id=cast("uuid.UUID", sess.id), content=f"m-{i}" + ) ) - ids.append(m.id) + ids.append(cast("uuid.UUID", m.id)) fixed = datetime.now(UTC) rows = ( ( @@ -1191,7 +1197,7 @@ async def _seed_messages_same_timestamp( for row in rows: row.timestamp = fixed await session.flush() - return sess.id, ids + return cast("uuid.UUID", sess.id), ids @pytest.mark.asyncio diff --git a/tests/integration/test_notification_delivery_phantom.py b/tests/integration/test_notification_delivery_phantom.py index 5ba73a18..216b2fa0 100644 --- a/tests/integration/test_notification_delivery_phantom.py +++ b/tests/integration/test_notification_delivery_phantom.py @@ -8,7 +8,7 @@ SQLAlchemy ``after_commit`` events and a recording bus stand-in. from __future__ import annotations import asyncio -from typing import TYPE_CHECKING +from typing import TYPE_CHECKING, cast from uuid import UUID, uuid4 import pytest @@ -99,7 +99,7 @@ async def _seed_agents_and_notification( metrics={}, ) db.add(r) - recipient_ids.append(r.id) + recipient_ids.append(cast("UUID", r.id)) await db.flush() notification = NotificationTable( @@ -113,7 +113,7 @@ async def _seed_agents_and_notification( ) db.add(notification) await db.flush() - return notification.id, notification + return cast("UUID", notification.id), notification @pytest.mark.asyncio diff --git a/tests/unit/runtime/test_orchestrator_shutdown_drain.py b/tests/unit/runtime/test_orchestrator_shutdown_drain.py index b376ea56..d5486ed1 100644 --- a/tests/unit/runtime/test_orchestrator_shutdown_drain.py +++ b/tests/unit/runtime/test_orchestrator_shutdown_drain.py @@ -152,7 +152,10 @@ async def test_stop_is_idempotent_double_call_is_noop() -> None: drain_calls += 1 await real_drain() - orch._drain_bg_tasks = counting_drain + # Override via an Any-typed view so the assignment bypasses mypy's + # method-assign check while staying a plain attribute write (no setattr). + orch_any: Any = orch + orch_any._drain_bg_tasks = counting_drain await orch.stop() assert drain_calls == 1, "first stop() drained the bg tasks" diff --git a/tests/unit/runtime/test_parked_spawn_shortcut.py b/tests/unit/runtime/test_parked_spawn_shortcut.py index 4cc3adde..c3a3aaf4 100644 --- a/tests/unit/runtime/test_parked_spawn_shortcut.py +++ b/tests/unit/runtime/test_parked_spawn_shortcut.py @@ -61,7 +61,9 @@ def _wire(monitor: dict[str, Any]) -> Any: # Mirrors the real prepare's registration side-effect so the RED test # observes the STARTING instance the current code leaks. cfg = SimpleNamespace(provider_type="anthropic", model="opus") - inst = AgentInstance(agent_id="be-dev-1", state=AgentState.STARTING, config=cfg) + inst = AgentInstance( + agent_id="be-dev-1", state=AgentState.STARTING, config=None + ) return cfg, inst, None return _readiness_gate, _git_context, _route, _prepare @@ -116,7 +118,7 @@ async def test_not_parked_spawn_still_runs_prepare_and_launches( return AgentInstance( agent_id="be-dev-1", state=AgentState.ACTIVE, - config=SimpleNamespace(provider_type="anthropic", model="opus"), + config=None, ) monkeypatch.setattr(orch, "_readiness_gate", _rg) @@ -146,7 +148,7 @@ async def test_running_agent_not_bailed_by_parked_check( existing = AgentInstance( agent_id="be-dev-1", state=AgentState.ACTIVE, - config=SimpleNamespace(provider_type="anthropic", model="opus"), + config=None, ) orch._instances["be-dev-1"] = existing diff --git a/tests/unit/runtime/test_reaper_subprocess_timeout.py b/tests/unit/runtime/test_reaper_subprocess_timeout.py index 9626465a..6fca07d5 100644 --- a/tests/unit/runtime/test_reaper_subprocess_timeout.py +++ b/tests/unit/runtime/test_reaper_subprocess_timeout.py @@ -280,4 +280,6 @@ async def test_check_health_skips_agent_on_inspect_timeout_not_aborts( assert hang.killed # a2 WAS reached despite a1's timeout — the sweep continued. handle.assert_awaited_once() - assert handle.await_args.args[0] == "a2" + call = handle.await_args + assert call is not None + assert call.args[0] == "a2" diff --git a/tests/unit/runtime/test_secretary_spawn_shutdown.py b/tests/unit/runtime/test_secretary_spawn_shutdown.py index 6e408ef3..6fd2bbfd 100644 --- a/tests/unit/runtime/test_secretary_spawn_shutdown.py +++ b/tests/unit/runtime/test_secretary_spawn_shutdown.py @@ -9,7 +9,7 @@ from __future__ import annotations import asyncio from pathlib import Path from types import SimpleNamespace -from typing import Any +from typing import TYPE_CHECKING, Any from unittest.mock import patch from uuid import UUID @@ -20,6 +20,9 @@ from roboco.runtime.orchestrator import ( ) from roboco.services import prompter_live +if TYPE_CHECKING: + from collections.abc import Iterator + def _make_orchestrator() -> AgentOrchestrator: """AgentOrchestrator with constructor I/O skipped; a RUNNING minimal one.""" @@ -97,7 +100,7 @@ def _wire_secretary_spawn_mocks( @pytest.fixture(autouse=True) -def _fresh_registry() -> None: +def _fresh_registry() -> Iterator[None]: """Isolate the process-wide live registry per test.""" prev = prompter_live._RegistryHolder.instance prompter_live._RegistryHolder.instance = prompter_live.PrompterLiveRegistry() diff --git a/tests/unit/services/test_git_close_pull_request_scoping.py b/tests/unit/services/test_git_close_pull_request_scoping.py index 81c49a52..da2a657e 100644 --- a/tests/unit/services/test_git_close_pull_request_scoping.py +++ b/tests/unit/services/test_git_close_pull_request_scoping.py @@ -86,8 +86,11 @@ async def test_close_pull_request_requires_project_id() -> None: recorder: list[object] = [] svc = _service(recorder) + # Bind to an Any-typed local so mypy doesn't flag the missing project_id; + # the call still reaches the runtime, where it raises TypeError as asserted. + closer: Any = svc.close_pull_request with pytest.raises(TypeError): - await svc.close_pull_request(_PR_NUMBER, comment="superseded") # no project_id + await closer(_PR_NUMBER, comment="superseded") # The unscoped lookup was never issued — no SQL reached the session. assert recorder == [] diff --git a/tests/unit/services/test_git_pr_target_scoping.py b/tests/unit/services/test_git_pr_target_scoping.py index 3f9a10b0..ee5768a3 100644 --- a/tests/unit/services/test_git_pr_target_scoping.py +++ b/tests/unit/services/test_git_pr_target_scoping.py @@ -90,8 +90,11 @@ async def test_pr_target_requires_project_id() -> None: recorder: list[object] = [] svc = _service(recorder) + # Bind to an Any-typed local so mypy doesn't flag the missing project_id; + # the call still reaches the runtime, where it raises TypeError as asserted. + target: Any = svc.pr_target with pytest.raises(TypeError): - await svc.pr_target(_PR_NUMBER) # missing required project_id + await target(_PR_NUMBER) # The unscoped lookup was never issued — no SQL reached the session. assert recorder == []