Skip to content

Commit 1511a31

Browse files
committed
refactor(tests): extract _patched_main context manager
Five TestMain tests duplicated the same ~10-line patch stack. Collapse into a single `_patched_main` helper that uses ExitStack to apply all common patches and yields the mocks dict for per-test assertions.
1 parent 071b40a commit 1511a31

1 file changed

Lines changed: 34 additions & 39 deletions

File tree

tests/test_orchestrator.py

Lines changed: 34 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
"""Tests for clayde.orchestrator — event-driven loop."""
22

3+
from contextlib import ExitStack, contextmanager
34
from datetime import datetime, timezone
45
from unittest.mock import MagicMock, patch
56

@@ -24,66 +25,60 @@ def _mock_settings(enabled=False, github_token="tok", github_username="ClaydeCod
2425
return s
2526

2627

28+
@contextmanager
29+
def _patched_main(enabled=True, claude_available=True, assigned=(), state=None):
30+
"""Patch every external dependency `main()` touches.
31+
32+
Yields a dict of name → mock so individual tests can assert on call behavior.
33+
"""
34+
targets = {
35+
"get_settings": {"return_value": _mock_settings(enabled=enabled)},
36+
"setup_logging": {},
37+
"init_tracer": {},
38+
"_configure_global_git_identity": {},
39+
"is_claude_available": {"return_value": claude_available},
40+
"get_github_client": {},
41+
"get_assigned_issues": {"return_value": list(assigned)},
42+
"load_state": {"return_value": state if state is not None else {"issues": {}}},
43+
"_prune_closed_issues": {},
44+
"_handle_issue": {},
45+
}
46+
with ExitStack() as stack:
47+
yield {
48+
name: stack.enter_context(patch(f"clayde.orchestrator.{name}", **kwargs))
49+
for name, kwargs in targets.items()
50+
}
51+
52+
2753
class TestMain:
2854
def test_exits_when_disabled(self):
29-
with patch("clayde.orchestrator.setup_logging"), \
30-
patch("clayde.orchestrator.get_settings", return_value=_mock_settings(enabled=False)):
55+
with _patched_main(enabled=False):
3156
with pytest.raises(SystemExit) as exc_info:
3257
main()
3358
assert exc_info.value.code == 0
3459

3560
def test_returns_when_claude_unavailable(self):
36-
with patch("clayde.orchestrator.get_settings", return_value=_mock_settings(enabled=True)), \
37-
patch("clayde.orchestrator.setup_logging"), \
38-
patch("clayde.orchestrator.init_tracer"), \
39-
patch("clayde.orchestrator._configure_global_git_identity"), \
40-
patch("clayde.orchestrator.is_claude_available", return_value=False), \
41-
patch("clayde.orchestrator.get_github_client") as mock_gc:
61+
with _patched_main(claude_available=False) as mocks:
4262
main()
43-
mock_gc.assert_not_called()
63+
mocks["get_github_client"].assert_not_called()
4464

4565
def test_returns_when_no_assigned_issues(self):
46-
with patch("clayde.orchestrator.get_settings", return_value=_mock_settings(enabled=True)), \
47-
patch("clayde.orchestrator.setup_logging"), \
48-
patch("clayde.orchestrator.init_tracer"), \
49-
patch("clayde.orchestrator._configure_global_git_identity"), \
50-
patch("clayde.orchestrator.is_claude_available", return_value=True), \
51-
patch("clayde.orchestrator.get_github_client"), \
52-
patch("clayde.orchestrator.get_assigned_issues", return_value=[]), \
53-
patch("clayde.orchestrator.load_state", return_value={"issues": {}}):
66+
with _patched_main(assigned=[]):
5467
main()
5568

5669
def test_calls_handle_issue_for_each_assigned(self):
5770
issue = MagicMock()
5871
issue.html_url = "https://github.com/o/r/issues/1"
59-
with patch("clayde.orchestrator.get_settings", return_value=_mock_settings(enabled=True)), \
60-
patch("clayde.orchestrator.setup_logging"), \
61-
patch("clayde.orchestrator.init_tracer"), \
62-
patch("clayde.orchestrator._configure_global_git_identity"), \
63-
patch("clayde.orchestrator.is_claude_available", return_value=True), \
64-
patch("clayde.orchestrator.get_github_client"), \
65-
patch("clayde.orchestrator.get_assigned_issues", return_value=[issue]), \
66-
patch("clayde.orchestrator.load_state", return_value={"issues": {}}), \
67-
patch("clayde.orchestrator._prune_closed_issues"), \
68-
patch("clayde.orchestrator._handle_issue") as mock_handle:
72+
with _patched_main(assigned=[issue]) as mocks:
6973
main()
70-
mock_handle.assert_called_once()
74+
mocks["_handle_issue"].assert_called_once()
7175

7276
def test_main_calls_prune(self):
7377
issue = MagicMock()
7478
issue.html_url = "https://github.com/o/r/issues/1"
75-
with patch("clayde.orchestrator.get_settings", return_value=_mock_settings(enabled=True)), \
76-
patch("clayde.orchestrator.setup_logging"), \
77-
patch("clayde.orchestrator.init_tracer"), \
78-
patch("clayde.orchestrator._configure_global_git_identity"), \
79-
patch("clayde.orchestrator.is_claude_available", return_value=True), \
80-
patch("clayde.orchestrator.get_github_client"), \
81-
patch("clayde.orchestrator.get_assigned_issues", return_value=[issue]), \
82-
patch("clayde.orchestrator.load_state", return_value={"issues": {}}), \
83-
patch("clayde.orchestrator._prune_closed_issues") as mock_prune, \
84-
patch("clayde.orchestrator._handle_issue"):
79+
with _patched_main(assigned=[issue]) as mocks:
8580
main()
86-
mock_prune.assert_called_once()
81+
mocks["_prune_closed_issues"].assert_called_once()
8782

8883

8984
class TestHandleIssue:

0 commit comments

Comments
 (0)