refactor(bottle): finish bottle package move
lint / lint (push) Successful in 1m59s
test / unit (pull_request) Successful in 46s
test / integration (pull_request) Successful in 16s
test / coverage (pull_request) Successful in 57s

This commit is contained in:
2026-07-06 16:13:30 -04:00
parent d4a4c47bea
commit ec14ce0048
13 changed files with 688 additions and 394 deletions
+1 -1
View File
@@ -30,7 +30,7 @@ import unittest
from pathlib import Path
from bot_bottle.backend import BottleSpec, get_bottle_backend
from bot_bottle.bottle_state import cleanup_state
from bot_bottle.bottle.state import cleanup_state
from bot_bottle.manifest import ManifestIndex
from tests._docker import skip_unless_docker
+34 -25
View File
@@ -50,15 +50,24 @@ def _metadata(
# ---------------------------------------------------------------------------
class TestCompatibilityImports(unittest.TestCase):
def test_legacy_api_module_reexports_public_runner_api(self):
from bot_bottle import api
from bot_bottle.bottle import runner
self.assertIs(api.BottleError, runner.BottleError)
self.assertIs(api.start_headless, runner.start_headless)
class TestStartHeadless(unittest.TestCase):
def setUp(self) -> None:
self._manifest = _make_manifest()
patch("bot_bottle.api.ManifestIndex.resolve", return_value=self._manifest).start()
patch("bot_bottle.bottle.runner.ManifestIndex.resolve", return_value=self._manifest).start()
self._launch = patch(
"bot_bottle.api._launch_bottle", return_value=("implementer-abc12", 0)
"bot_bottle.bottle.runner._launch_bottle", return_value=("implementer-abc12", 0)
).start()
patch(
"bot_bottle.api._uniquify_label_headless", side_effect=str
"bot_bottle.bottle.runner._uniquify_label_headless", side_effect=str
).start()
self.addCleanup(patch.stopall)
@@ -101,7 +110,7 @@ class TestStartHeadless(unittest.TestCase):
def test_no_default_bottle_raises_bottle_error(self):
manifest = _make_manifest(bottle_name="")
with patch("bot_bottle.api.ManifestIndex.resolve", return_value=manifest):
with patch("bot_bottle.bottle.runner.ManifestIndex.resolve", return_value=manifest):
with self.assertRaises(BottleError):
start_headless("implementer", prompt="Do it")
self._launch.assert_not_called()
@@ -109,7 +118,7 @@ class TestStartHeadless(unittest.TestCase):
def test_manifest_error_in_resolve_raises_bottle_error(self):
from bot_bottle.manifest import ManifestError
patch(
"bot_bottle.api.ManifestIndex.resolve", side_effect=ManifestError("bad")
"bot_bottle.bottle.runner.ManifestIndex.resolve", side_effect=ManifestError("bad")
).start()
with self.assertRaises(BottleError):
start_headless("implementer", prompt="Do it")
@@ -142,11 +151,11 @@ class TestStartHeadless(unittest.TestCase):
class TestResumeHeadless(unittest.TestCase):
def setUp(self) -> None:
self._md = _metadata()
patch("bot_bottle.api.read_metadata", return_value=self._md).start()
patch("bot_bottle.bottle.runner.read_metadata", return_value=self._md).start()
manifest = _make_manifest()
patch("bot_bottle.api.ManifestIndex.resolve", return_value=manifest).start()
patch("bot_bottle.bottle.runner.ManifestIndex.resolve", return_value=manifest).start()
self._launch = patch(
"bot_bottle.api._launch_bottle", return_value=("implementer-abc12", 0)
"bot_bottle.bottle.runner._launch_bottle", return_value=("implementer-abc12", 0)
).start()
self.addCleanup(patch.stopall)
@@ -170,7 +179,7 @@ class TestResumeHeadless(unittest.TestCase):
self.assertEqual(env, self._spec().forge_env)
def test_missing_state_raises_bottle_error(self):
with patch("bot_bottle.api.read_metadata", return_value=None):
with patch("bot_bottle.bottle.runner.read_metadata", return_value=None):
with self.assertRaises(BottleError):
resume_headless("no-such-abc12", prompt="Prompt")
self._launch.assert_not_called()
@@ -184,7 +193,7 @@ class TestResumeHeadless(unittest.TestCase):
def test_manifest_error_in_resolve_raises_bottle_error(self):
from bot_bottle.manifest import ManifestError
patch(
"bot_bottle.api.ManifestIndex.resolve", side_effect=ManifestError("bad")
"bot_bottle.bottle.runner.ManifestIndex.resolve", side_effect=ManifestError("bad")
).start()
with self.assertRaises(BottleError):
resume_headless("implementer-abc12", prompt="Prompt")
@@ -214,10 +223,10 @@ class TestResumeHeadless(unittest.TestCase):
class TestFreeze(unittest.TestCase):
def setUp(self) -> None:
patch("bot_bottle.api.read_metadata", return_value=_metadata()).start()
patch("bot_bottle.bottle.runner.read_metadata", return_value=_metadata()).start()
self._freezer = MagicMock()
self._get_freezer = patch(
"bot_bottle.api.get_freezer", return_value=self._freezer
"bot_bottle.bottle.runner.get_freezer", return_value=self._freezer
).start()
self.addCleanup(patch.stopall)
@@ -253,10 +262,10 @@ class TestFreeze(unittest.TestCase):
class TestDestroy(unittest.TestCase):
def setUp(self) -> None:
patch("bot_bottle.api.read_metadata", return_value=_metadata()).start()
self._dd = patch("bot_bottle.api._destroy_docker").start()
patch("bot_bottle.api.clear_preserve_marker").start()
self._cleanup = patch("bot_bottle.api.cleanup_state").start()
patch("bot_bottle.bottle.runner.read_metadata", return_value=_metadata()).start()
self._dd = patch("bot_bottle.bottle.runner._destroy_docker").start()
patch("bot_bottle.bottle.runner.clear_preserve_marker").start()
self._cleanup = patch("bot_bottle.bottle.runner.cleanup_state").start()
self.addCleanup(patch.stopall)
def test_docker_backend_calls_destroy_docker(self):
@@ -269,32 +278,32 @@ class TestDestroy(unittest.TestCase):
def test_smolmachines_backend_calls_destroy_smolmachines(self):
patch(
"bot_bottle.api.read_metadata",
"bot_bottle.bottle.runner.read_metadata",
return_value=_metadata(backend="smolmachines"),
).start()
ds = patch("bot_bottle.api._destroy_smolmachines").start()
ds = patch("bot_bottle.bottle.runner._destroy_smolmachines").start()
destroy("implementer-abc12")
ds.assert_called_once_with("implementer-abc12")
self._dd.assert_not_called()
def test_other_backend_skips_docker_and_smolmachines(self):
patch(
"bot_bottle.api.read_metadata",
"bot_bottle.bottle.runner.read_metadata",
return_value=_metadata(backend="macos-container"),
).start()
ds = patch("bot_bottle.api._destroy_smolmachines").start()
ds = patch("bot_bottle.bottle.runner._destroy_smolmachines").start()
destroy("implementer-abc12")
self._dd.assert_not_called()
ds.assert_not_called()
self._cleanup.assert_called_once_with("implementer-abc12")
def test_missing_metadata_defaults_to_docker(self):
patch("bot_bottle.api.read_metadata", return_value=None).start()
patch("bot_bottle.bottle.runner.read_metadata", return_value=None).start()
destroy("no-state-abc12")
self._dd.assert_called_once_with("no-state-abc12")
def test_explicit_backend_overrides_metadata(self):
ds = patch("bot_bottle.api._destroy_smolmachines").start()
ds = patch("bot_bottle.bottle.runner._destroy_smolmachines").start()
destroy("implementer-abc12", backend_name="smolmachines")
ds.assert_called_once_with("implementer-abc12")
self._dd.assert_not_called()
@@ -320,7 +329,7 @@ class TestDestroyDocker(unittest.TestCase):
return_value="bb-proj",
).start()
self._state_dir = patch(
"bot_bottle.bottle_state.bottle_state_dir",
"bot_bottle.bottle.state.bottle_state_dir",
return_value=Path("/fake/state"),
).start()
self.addCleanup(patch.stopall)
@@ -332,7 +341,7 @@ class TestDestroyDocker(unittest.TestCase):
"bot_bottle.backend.docker.compose.compose_file_path",
return_value=fake_file,
):
from bot_bottle.api import _destroy_docker
from bot_bottle.bottle.runner import _destroy_docker
_destroy_docker("slug-1")
def test_calls_compose_down_when_file_exists(self) -> None:
@@ -356,7 +365,7 @@ class TestDestroySmolmachines(unittest.TestCase):
self.addCleanup(patch.stopall)
def _call(self) -> None:
from bot_bottle.api import _destroy_smolmachines
from bot_bottle.bottle.runner import _destroy_smolmachines
_destroy_smolmachines("slug-7")
def test_issues_stop_then_delete(self) -> None:
+10 -1
View File
@@ -8,7 +8,7 @@ from pathlib import Path
from bot_bottle import supervise
from bot_bottle import bottle_state
from bot_bottle.bottle_state import (
from bot_bottle.bottle.state import (
BottleMetadata,
read_metadata,
write_metadata,
@@ -31,6 +31,15 @@ class _FakeHomeMixin:
self._tmp.cleanup()
class TestCompatibilityImports(unittest.TestCase):
def test_legacy_bottle_state_module_reexports_state_api(self):
from bot_bottle import bottle_state as legacy
from bot_bottle.bottle import state
self.assertIs(legacy.BottleMetadata, state.BottleMetadata)
self.assertIs(legacy.read_metadata, state.read_metadata)
class TestPerBottleDockerfile(_FakeHomeMixin, unittest.TestCase):
def setUp(self):
self._setup_fake_home()