"""Shared test doubles: a duck-typed forge and bottle runner.""" # Test doubles mirror an API shape; some params are intentionally unused. # pylint: disable=unused-argument from __future__ import annotations from collections.abc import Sequence from bot_bottle.orchestrator.runner import slugify class FakeForge: def __init__(self, members: tuple[str, ...] = ()) -> None: self.members = set(members) self.comments: list[tuple[int, str]] = [] self.descriptions: list[tuple[int, str]] = [] self.scope_denied: set[int] = set() def is_org_member(self, org: str, username: str) -> bool: return username in self.members def read_issue(self, number: int) -> dict[str, object]: return {"number": number, "kind": "issue"} def read_pr(self, number: int) -> dict[str, object]: return {"number": number, "merged": False} def read_comments(self, number: int) -> list[dict[str, object]]: return [{"id": 1, "user": "alice", "body": "hi"}] def post_comment(self, number: int, body: str) -> None: if number in self.scope_denied: raise PermissionError(f"write to #{number} denied") self.comments.append((number, body)) def update_description(self, number: int, body: str) -> None: if number in self.scope_denied: raise PermissionError(f"write to #{number} denied") self.descriptions.append((number, body)) class FakeRunner: def __init__(self) -> None: self.calls: list[tuple[object, ...]] = [] def start( self, *, agent: str, bottles: Sequence[str], label: str, prompt: str, forge_env: dict[str, str], ) -> str: self.calls.append(("start", agent, tuple(bottles), label, prompt, dict(forge_env))) return slugify(label) def freeze(self, slug: str) -> None: self.calls.append(("freeze", slug)) def resume(self, slug: str, prompt: str) -> None: self.calls.append(("resume", slug, prompt)) def destroy(self, slug: str) -> None: self.calls.append(("destroy", slug))