d5fb159857
BottleRunner Protocol tightened: start() → str, freeze/resume/destroy → None. RunResult removed. lifecycle.py unpacks the slug directly. FakeRunner and test_runner updated to match. Config.bot_bottle_cli dropped (nothing uses it). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
67 lines
2.1 KiB
Python
67 lines
2.1 KiB
Python
"""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))
|