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>
95 lines
3.0 KiB
Python
95 lines
3.0 KiB
Python
"""Unit: ProgrammaticBottleRunner + slugify."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import sys
|
|
import types
|
|
import unittest
|
|
from unittest.mock import MagicMock
|
|
|
|
from bot_bottle.orchestrator.runner import ProgrammaticBottleRunner, slugify
|
|
|
|
|
|
class SlugifyTest(unittest.TestCase):
|
|
def test_basic(self):
|
|
self.assertEqual("impl-didericis-bot-bottle-17",
|
|
slugify("impl-didericis-bot-bottle-17"))
|
|
|
|
def test_collapses_and_strips(self):
|
|
self.assertEqual("a-b-c", slugify(" A_B/C!! "))
|
|
|
|
|
|
def _make_api_stub(**overrides):
|
|
"""Return a mock bot_bottle.api module with sensible defaults."""
|
|
stub = types.ModuleType("bot_bottle.api")
|
|
stub.start_headless = MagicMock(return_value="impl-r-17")
|
|
stub.freeze = MagicMock()
|
|
stub.resume_headless = MagicMock()
|
|
stub.destroy = MagicMock()
|
|
for k, v in overrides.items():
|
|
setattr(stub, k, v)
|
|
return stub
|
|
|
|
|
|
class ProgrammaticRunnerTest(unittest.TestCase):
|
|
def setUp(self):
|
|
self._api = _make_api_stub()
|
|
sys.modules["bot_bottle.api"] = self._api
|
|
self.runner = ProgrammaticBottleRunner()
|
|
|
|
def tearDown(self):
|
|
sys.modules.pop("bot_bottle.api", None)
|
|
|
|
def test_start_returns_slug_from_api(self):
|
|
slug = self.runner.start(
|
|
agent="impl", bottles=["claude", "dev"], label="impl-r-17",
|
|
prompt="do it", forge_env={"FORGE_OWNER": "didericis"},
|
|
)
|
|
self.assertEqual("impl-r-17", slug)
|
|
|
|
def test_start_forwards_all_args(self):
|
|
self.runner.start(
|
|
agent="impl", bottles=["claude", "dev"], label="impl-r-17",
|
|
prompt="do it", forge_env={"FORGE_OWNER": "didericis"},
|
|
)
|
|
self._api.start_headless.assert_called_once_with(
|
|
"impl",
|
|
prompt="do it",
|
|
bottles=["claude", "dev"],
|
|
label="impl-r-17",
|
|
forge_env={"FORGE_OWNER": "didericis"},
|
|
)
|
|
|
|
def test_start_no_bottles_passes_none(self):
|
|
self.runner.start(agent="impl", bottles=[], label="l", prompt="p", forge_env={})
|
|
call_kwargs = self._api.start_headless.call_args[1]
|
|
self.assertIsNone(call_kwargs["bottles"])
|
|
|
|
def test_freeze_delegates_to_api(self):
|
|
self.runner.freeze("slug-1")
|
|
self._api.freeze.assert_called_once_with("slug-1")
|
|
|
|
def test_freeze_returns_none(self):
|
|
result = self.runner.freeze("slug-1")
|
|
self.assertIsNone(result)
|
|
|
|
def test_resume_delegates_to_api(self):
|
|
self.runner.resume("slug-1", "address review")
|
|
self._api.resume_headless.assert_called_once_with("slug-1", prompt="address review")
|
|
|
|
def test_resume_returns_none(self):
|
|
result = self.runner.resume("slug-1", "p")
|
|
self.assertIsNone(result)
|
|
|
|
def test_destroy_delegates_to_api(self):
|
|
self.runner.destroy("slug-7")
|
|
self._api.destroy.assert_called_once_with("slug-7")
|
|
|
|
def test_destroy_returns_none(self):
|
|
result = self.runner.destroy("slug-7")
|
|
self.assertIsNone(result)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|