mirror of
https://github.com/rennf93/roboco.git
synced 2026-08-03 07:23:24 +02:00
[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.
This commit is contained in:
@@ -71,7 +71,7 @@ async def _seed_project(db: AsyncSession, slug: str) -> ProjectTable:
|
|||||||
return project
|
return project
|
||||||
|
|
||||||
|
|
||||||
def _task(project_id, *, branch: str, status: TaskStatus) -> TaskTable:
|
def _task(project_id: UUID, *, branch: str, status: TaskStatus) -> TaskTable:
|
||||||
return TaskTable(
|
return TaskTable(
|
||||||
id=uuid4(),
|
id=uuid4(),
|
||||||
title=f"task {branch}",
|
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")
|
proj_b = await _seed_project(db_session, "gca-collide-b")
|
||||||
db_session.add_all(
|
db_session.add_all(
|
||||||
[
|
[
|
||||||
_task(proj_a.id, branch=_BRANCH, status=TaskStatus.IN_PROGRESS),
|
_task(
|
||||||
_task(proj_b.id, branch=_BRANCH, status=TaskStatus.COMPLETED),
|
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()
|
await db_session.flush()
|
||||||
|
|||||||
@@ -1170,14 +1170,20 @@ async def _seed_messages_same_timestamp(
|
|||||||
timestamp so the equal-timestamp pagination skip is reproducible. Returns
|
timestamp so the equal-timestamp pagination skip is reproducible. Returns
|
||||||
``(session_id, message_ids)``."""
|
``(session_id, message_ids)``."""
|
||||||
ch = await svc.create_channel(_channel_req(uuid4().hex[:6]))
|
ch = await svc.create_channel(_channel_req(uuid4().hex[:6]))
|
||||||
grp = await svc.create_group(GroupCreateRequest(name="g1", channel_id=ch.id))
|
grp = await svc.create_group(
|
||||||
sess = await svc.create_session(SessionCreateRequest(group_id=grp.id))
|
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] = []
|
ids: list[UUID] = []
|
||||||
for i in range(count):
|
for i in range(count):
|
||||||
m = await svc.send_message(
|
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)
|
fixed = datetime.now(UTC)
|
||||||
rows = (
|
rows = (
|
||||||
(
|
(
|
||||||
@@ -1191,7 +1197,7 @@ async def _seed_messages_same_timestamp(
|
|||||||
for row in rows:
|
for row in rows:
|
||||||
row.timestamp = fixed
|
row.timestamp = fixed
|
||||||
await session.flush()
|
await session.flush()
|
||||||
return sess.id, ids
|
return cast("uuid.UUID", sess.id), ids
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ SQLAlchemy ``after_commit`` events and a recording bus stand-in.
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
from typing import TYPE_CHECKING
|
from typing import TYPE_CHECKING, cast
|
||||||
from uuid import UUID, uuid4
|
from uuid import UUID, uuid4
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
@@ -99,7 +99,7 @@ async def _seed_agents_and_notification(
|
|||||||
metrics={},
|
metrics={},
|
||||||
)
|
)
|
||||||
db.add(r)
|
db.add(r)
|
||||||
recipient_ids.append(r.id)
|
recipient_ids.append(cast("UUID", r.id))
|
||||||
await db.flush()
|
await db.flush()
|
||||||
|
|
||||||
notification = NotificationTable(
|
notification = NotificationTable(
|
||||||
@@ -113,7 +113,7 @@ async def _seed_agents_and_notification(
|
|||||||
)
|
)
|
||||||
db.add(notification)
|
db.add(notification)
|
||||||
await db.flush()
|
await db.flush()
|
||||||
return notification.id, notification
|
return cast("UUID", notification.id), notification
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
|
|||||||
@@ -152,7 +152,10 @@ async def test_stop_is_idempotent_double_call_is_noop() -> None:
|
|||||||
drain_calls += 1
|
drain_calls += 1
|
||||||
await real_drain()
|
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()
|
await orch.stop()
|
||||||
assert drain_calls == 1, "first stop() drained the bg tasks"
|
assert drain_calls == 1, "first stop() drained the bg tasks"
|
||||||
|
|||||||
@@ -61,7 +61,9 @@ def _wire(monitor: dict[str, Any]) -> Any:
|
|||||||
# Mirrors the real prepare's registration side-effect so the RED test
|
# Mirrors the real prepare's registration side-effect so the RED test
|
||||||
# observes the STARTING instance the current code leaks.
|
# observes the STARTING instance the current code leaks.
|
||||||
cfg = SimpleNamespace(provider_type="anthropic", model="opus")
|
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 cfg, inst, None
|
||||||
|
|
||||||
return _readiness_gate, _git_context, _route, _prepare
|
return _readiness_gate, _git_context, _route, _prepare
|
||||||
@@ -116,7 +118,7 @@ async def test_not_parked_spawn_still_runs_prepare_and_launches(
|
|||||||
return AgentInstance(
|
return AgentInstance(
|
||||||
agent_id="be-dev-1",
|
agent_id="be-dev-1",
|
||||||
state=AgentState.ACTIVE,
|
state=AgentState.ACTIVE,
|
||||||
config=SimpleNamespace(provider_type="anthropic", model="opus"),
|
config=None,
|
||||||
)
|
)
|
||||||
|
|
||||||
monkeypatch.setattr(orch, "_readiness_gate", _rg)
|
monkeypatch.setattr(orch, "_readiness_gate", _rg)
|
||||||
@@ -146,7 +148,7 @@ async def test_running_agent_not_bailed_by_parked_check(
|
|||||||
existing = AgentInstance(
|
existing = AgentInstance(
|
||||||
agent_id="be-dev-1",
|
agent_id="be-dev-1",
|
||||||
state=AgentState.ACTIVE,
|
state=AgentState.ACTIVE,
|
||||||
config=SimpleNamespace(provider_type="anthropic", model="opus"),
|
config=None,
|
||||||
)
|
)
|
||||||
orch._instances["be-dev-1"] = existing
|
orch._instances["be-dev-1"] = existing
|
||||||
|
|
||||||
|
|||||||
@@ -280,4 +280,6 @@ async def test_check_health_skips_agent_on_inspect_timeout_not_aborts(
|
|||||||
assert hang.killed
|
assert hang.killed
|
||||||
# a2 WAS reached despite a1's timeout — the sweep continued.
|
# a2 WAS reached despite a1's timeout — the sweep continued.
|
||||||
handle.assert_awaited_once()
|
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"
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ from __future__ import annotations
|
|||||||
import asyncio
|
import asyncio
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from types import SimpleNamespace
|
from types import SimpleNamespace
|
||||||
from typing import Any
|
from typing import TYPE_CHECKING, Any
|
||||||
from unittest.mock import patch
|
from unittest.mock import patch
|
||||||
from uuid import UUID
|
from uuid import UUID
|
||||||
|
|
||||||
@@ -20,6 +20,9 @@ from roboco.runtime.orchestrator import (
|
|||||||
)
|
)
|
||||||
from roboco.services import prompter_live
|
from roboco.services import prompter_live
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from collections.abc import Iterator
|
||||||
|
|
||||||
|
|
||||||
def _make_orchestrator() -> AgentOrchestrator:
|
def _make_orchestrator() -> AgentOrchestrator:
|
||||||
"""AgentOrchestrator with constructor I/O skipped; a RUNNING minimal one."""
|
"""AgentOrchestrator with constructor I/O skipped; a RUNNING minimal one."""
|
||||||
@@ -97,7 +100,7 @@ def _wire_secretary_spawn_mocks(
|
|||||||
|
|
||||||
|
|
||||||
@pytest.fixture(autouse=True)
|
@pytest.fixture(autouse=True)
|
||||||
def _fresh_registry() -> None:
|
def _fresh_registry() -> Iterator[None]:
|
||||||
"""Isolate the process-wide live registry per test."""
|
"""Isolate the process-wide live registry per test."""
|
||||||
prev = prompter_live._RegistryHolder.instance
|
prev = prompter_live._RegistryHolder.instance
|
||||||
prompter_live._RegistryHolder.instance = prompter_live.PrompterLiveRegistry()
|
prompter_live._RegistryHolder.instance = prompter_live.PrompterLiveRegistry()
|
||||||
|
|||||||
@@ -86,8 +86,11 @@ async def test_close_pull_request_requires_project_id() -> None:
|
|||||||
recorder: list[object] = []
|
recorder: list[object] = []
|
||||||
svc = _service(recorder)
|
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):
|
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.
|
# The unscoped lookup was never issued — no SQL reached the session.
|
||||||
assert recorder == []
|
assert recorder == []
|
||||||
|
|||||||
@@ -90,8 +90,11 @@ async def test_pr_target_requires_project_id() -> None:
|
|||||||
recorder: list[object] = []
|
recorder: list[object] = []
|
||||||
svc = _service(recorder)
|
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):
|
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.
|
# The unscoped lookup was never issued — no SQL reached the session.
|
||||||
assert recorder == []
|
assert recorder == []
|
||||||
|
|||||||
Reference in New Issue
Block a user