314dc03b0d
Moves the orchestrator into bot_bottle/orchestrator/ so one install gets everything. Entry point is now `python -m bot_bottle.orchestrator run`. - Add bot_bottle/orchestrator/ with all 14 modules (verbatim move; internal imports were already relative, so no changes inside orchestrator modules) - Rewrite bootstrap.py: remove the lazy bot_bottle import guard, use direct relative imports from ..contrib.* - Add bot_bottle/contrib/forge/base.py: ScopedForge (read-anywhere / write-scoped) - Add bot_bottle/contrib/gitea/client.py: GiteaClient + GiteaForge (urllib.request only) - Add bot_bottle/contrib/gitea/forge_state.py: ForgeState + SqliteForgeStateStore - Add tests/unit/orchestrator/ (82 tests: 63 migrated + 19 new for contrib modules) Closes #321
70 lines
2.2 KiB
Python
70 lines
2.2 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 RunResult, 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],
|
|
) -> RunResult:
|
|
self.calls.append(("start", agent, tuple(bottles), label, prompt, dict(forge_env)))
|
|
return RunResult(slug=slugify(label), exit_code=0)
|
|
|
|
def freeze(self, slug: str) -> int:
|
|
self.calls.append(("freeze", slug))
|
|
return 0
|
|
|
|
def resume(self, slug: str, prompt: str) -> RunResult:
|
|
self.calls.append(("resume", slug, prompt))
|
|
return RunResult(slug=slug, exit_code=0)
|
|
|
|
def destroy(self, slug: str) -> int:
|
|
self.calls.append(("destroy", slug))
|
|
return 0
|