57290da1e8
Three new unit test modules: - tests/unit/test_contrib_gitea_client.py — GiteaClient (urllib mocked) and GiteaForge delegation - tests/unit/orchestrator/test_main.py — __main__ run/status commands - tests/unit/orchestrator/test_bootstrap.py — _token, BotBottleStateStore, _to_forge_state/_to_record, make_forge, make_sidecar, build Augments to existing suites: - test_events: non-"created" comment action ignored - test_lifecycle: _iso_now callable, untracked-issue comment ignored, untracked-PR closed ignored (covers _find_by_pr return-None path) - test_runner: destroy command, _default_run via subprocess mock - test_sidecar: _jsonable dataclass/list branches, OpLog.read on missing file, drain_done_events on corrupted file, socket _Handler invalid-JSON and empty-line paths, serve() with pre-existing socket path - test_watchdog: _loop body covered by patching _TICK_SECS to 0.01s - test_webhook: unknown GET path returns 404 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
82 lines
3.0 KiB
Python
82 lines
3.0 KiB
Python
"""Unit: SubprocessBottleRunner + slugify (injected run fn)."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import unittest
|
|
from collections.abc import Sequence
|
|
|
|
from bot_bottle.orchestrator.runner import SubprocessBottleRunner, 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!! "))
|
|
|
|
|
|
class SubprocessRunnerTest(unittest.TestCase):
|
|
def setUp(self):
|
|
self.argvs: list[list[str]] = []
|
|
self.envs: list[dict[str, str]] = []
|
|
|
|
def fake_run(argv: Sequence[str], env: dict[str, str]) -> int:
|
|
self.argvs.append(list(argv))
|
|
self.envs.append(dict(env))
|
|
return 0
|
|
|
|
self.runner = SubprocessBottleRunner(
|
|
cli="/x/cli.py", base_env={"PATH": "/bin"}, python="/py", run=fake_run
|
|
)
|
|
|
|
def test_start_argv_and_env(self):
|
|
result = 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", result.slug)
|
|
argv = self.argvs[0]
|
|
self.assertEqual(["/py", "/x/cli.py", "start", "impl", "--headless",
|
|
"--label", "impl-r-17", "--prompt", "do it",
|
|
"--bottle", "claude", "--bottle", "dev"], argv)
|
|
# forge_env merged over base_env for the child.
|
|
self.assertEqual("didericis", self.envs[0]["FORGE_OWNER"])
|
|
self.assertEqual("/bin", self.envs[0]["PATH"])
|
|
|
|
def test_start_no_bottles_omits_flag(self):
|
|
self.runner.start(agent="impl", bottles=[], label="l", prompt="p", forge_env={})
|
|
self.assertNotIn("--bottle", self.argvs[0])
|
|
|
|
def test_freeze_calls_commit(self):
|
|
self.runner.freeze("slug-1")
|
|
self.assertEqual(["/py", "/x/cli.py", "commit", "slug-1"], self.argvs[0])
|
|
|
|
def test_resume_headless(self):
|
|
r = self.runner.resume("slug-1", "address review")
|
|
self.assertEqual("slug-1", r.slug)
|
|
self.assertEqual(
|
|
["/py", "/x/cli.py", "resume", "slug-1", "--headless", "--prompt",
|
|
"address review"], self.argvs[0])
|
|
|
|
def test_destroy_calls_cleanup(self):
|
|
code = self.runner.destroy("slug-7")
|
|
self.assertEqual(0, code)
|
|
self.assertEqual(["/py", "/x/cli.py", "cleanup", "slug-7"], self.argvs[0])
|
|
|
|
|
|
class DefaultRunTest(unittest.TestCase):
|
|
def test_calls_subprocess_and_returns_code(self):
|
|
from unittest.mock import MagicMock, patch
|
|
from bot_bottle.orchestrator.runner import _default_run
|
|
with patch("subprocess.run") as mock_run:
|
|
mock_run.return_value = MagicMock(returncode=42)
|
|
code = _default_run(["echo", "hi"], {"PATH": "/bin"})
|
|
self.assertEqual(42, code)
|
|
mock_run.assert_called_once_with(["echo", "hi"], env={"PATH": "/bin"}, check=False)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|