feat: fold bot-bottle-orchestrator into bot_bottle/orchestrator subpackage
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
This commit is contained in:
@@ -0,0 +1,75 @@
|
||||
"""Unit: ForgeState + SqliteForgeStateStore."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import unittest
|
||||
|
||||
from bot_bottle.contrib.gitea.forge_state import ForgeState, SqliteForgeStateStore
|
||||
|
||||
|
||||
def _state(**kw: object) -> ForgeState:
|
||||
defaults: dict[str, object] = dict(
|
||||
owner="alice", repo="myrepo", issue_number=1,
|
||||
slug="impl-alice-myrepo-1", agent_name="impl",
|
||||
)
|
||||
defaults.update(kw)
|
||||
return ForgeState(**defaults) # type: ignore[arg-type]
|
||||
|
||||
|
||||
class ForgeStateStoreTest(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.store = SqliteForgeStateStore(None)
|
||||
|
||||
def test_upsert_and_get(self):
|
||||
s = _state()
|
||||
self.store.upsert(s)
|
||||
got = self.store.get("alice", "myrepo", 1)
|
||||
assert got is not None
|
||||
self.assertEqual("impl-alice-myrepo-1", got.slug)
|
||||
self.assertEqual("impl", got.agent_name)
|
||||
|
||||
def test_get_missing(self):
|
||||
self.assertIsNone(self.store.get("alice", "myrepo", 99))
|
||||
|
||||
def test_upsert_replaces(self):
|
||||
self.store.upsert(_state(status="running"))
|
||||
self.store.upsert(_state(status="frozen"))
|
||||
got = self.store.get("alice", "myrepo", 1)
|
||||
assert got is not None
|
||||
self.assertEqual("frozen", got.status)
|
||||
|
||||
def test_delete(self):
|
||||
self.store.upsert(_state())
|
||||
self.store.delete("alice", "myrepo", 1)
|
||||
self.assertIsNone(self.store.get("alice", "myrepo", 1))
|
||||
|
||||
def test_delete_missing_no_error(self):
|
||||
self.store.delete("alice", "myrepo", 99)
|
||||
|
||||
def test_all_sorted(self):
|
||||
self.store.upsert(_state(owner="z", issue_number=2))
|
||||
self.store.upsert(_state(owner="a", issue_number=1))
|
||||
rows = self.store.all()
|
||||
self.assertEqual(("a", "z"), (rows[0].owner, rows[1].owner))
|
||||
|
||||
def test_bottle_names_roundtrip(self):
|
||||
self.store.upsert(_state(bottle_names=["claude", "dev"]))
|
||||
got = self.store.get("alice", "myrepo", 1)
|
||||
assert got is not None
|
||||
self.assertEqual(["claude", "dev"], got.bottle_names)
|
||||
|
||||
def test_pr_number_none_roundtrip(self):
|
||||
self.store.upsert(_state(pr_number=None))
|
||||
got = self.store.get("alice", "myrepo", 1)
|
||||
assert got is not None
|
||||
self.assertIsNone(got.pr_number)
|
||||
|
||||
def test_pr_number_int_roundtrip(self):
|
||||
self.store.upsert(_state(pr_number=42))
|
||||
got = self.store.get("alice", "myrepo", 1)
|
||||
assert got is not None
|
||||
self.assertEqual(42, got.pr_number)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
Reference in New Issue
Block a user