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
76 lines
2.4 KiB
Python
76 lines
2.4 KiB
Python
"""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()
|