Replace single-page GET labels?limit=100 with api_get_all-backed _repo_label_id_map so later-page labels (e.g. type:feature, workflow-hardening) are not falsely rejected during full-set replacement. Also add post-mutation verification, fix related MCP/CLI label inventory call sites, and add multi-page regression tests for the #601 reconciler failure mode. Closes #627
110 lines
4.2 KiB
Python
110 lines
4.2 KiB
Python
"""Tests for the python CLI scripts close_issue.py and mark_issue.py.
|
|
|
|
All tests mock credentials and API requests so no real network calls are made.
|
|
"""
|
|
import sys
|
|
import unittest
|
|
import io
|
|
import contextlib
|
|
from unittest.mock import patch, MagicMock
|
|
|
|
# The modules under test live in the repo root
|
|
sys.path.insert(0, str(__import__("pathlib").Path(__file__).resolve().parent.parent))
|
|
|
|
import close_issue
|
|
import mark_issue
|
|
|
|
FAKE_AUTH = "Basic dGVzdDp0ZXN0"
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# close_issue.py
|
|
# ---------------------------------------------------------------------------
|
|
class TestCloseIssueCLI(unittest.TestCase):
|
|
|
|
def test_missing_argument_exits(self):
|
|
with self.assertRaises(SystemExit):
|
|
close_issue.main([])
|
|
|
|
def test_invalid_argument_type_exits(self):
|
|
with self.assertRaises(SystemExit):
|
|
close_issue.main(["not_a_number"])
|
|
|
|
@patch("close_issue.api_request")
|
|
@patch("close_issue.get_auth_header", return_value=FAKE_AUTH)
|
|
def test_successful_close(self, _auth, mock_api):
|
|
with contextlib.redirect_stdout(io.StringIO()), contextlib.redirect_stderr(io.StringIO()):
|
|
rc = close_issue.main(["42"])
|
|
self.assertEqual(rc, 0)
|
|
mock_api.assert_called_once()
|
|
url = mock_api.call_args[0][1]
|
|
self.assertIn("/issues/42", url)
|
|
self.assertEqual(mock_api.call_args[0][3], {"state": "closed"})
|
|
|
|
@patch("close_issue.api_request")
|
|
@patch("close_issue.get_auth_header", return_value=FAKE_AUTH)
|
|
def test_remote_override(self, _auth, mock_api):
|
|
rc = close_issue.main(["--remote", "prgs", "12"])
|
|
self.assertEqual(rc, 0)
|
|
url = mock_api.call_args[0][1]
|
|
self.assertIn("gitea.prgs.cc", url)
|
|
self.assertIn("/issues/12", url)
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# mark_issue.py
|
|
# ---------------------------------------------------------------------------
|
|
class TestMarkIssueCLI(unittest.TestCase):
|
|
|
|
def test_missing_argument_exits(self):
|
|
with self.assertRaises(SystemExit):
|
|
mark_issue.main([])
|
|
|
|
def test_invalid_action_exits(self):
|
|
with self.assertRaises(SystemExit):
|
|
mark_issue.main(["10", "bogus_action"])
|
|
|
|
@patch("mark_issue.api_get_all", return_value=[{"id": 101, "name": "status:in-progress"}])
|
|
@patch("mark_issue.api_request")
|
|
@patch("mark_issue.get_auth_header", return_value=FAKE_AUTH)
|
|
def test_successful_start(self, _auth, mock_api, mock_get_all):
|
|
mock_api.return_value = [{"name": "status:in-progress"}]
|
|
with contextlib.redirect_stdout(io.StringIO()), contextlib.redirect_stderr(io.StringIO()):
|
|
rc = mark_issue.main(["15", "start"])
|
|
self.assertEqual(rc, 0)
|
|
mock_get_all.assert_called()
|
|
self.assertIn("/labels", mock_get_all.call_args[0][0])
|
|
|
|
# Verify POST labels call
|
|
post_call = mock_api.call_args_list[0]
|
|
self.assertEqual(post_call[0][0], "POST")
|
|
self.assertIn("/issues/15/labels", post_call[0][1])
|
|
self.assertEqual(post_call[0][3], {"labels": [101]})
|
|
|
|
@patch("mark_issue.api_get_all", return_value=[{"id": 101, "name": "status:in-progress"}])
|
|
@patch("mark_issue.api_request")
|
|
@patch("mark_issue.get_auth_header", return_value=FAKE_AUTH)
|
|
def test_successful_done(self, _auth, mock_api, _get_all):
|
|
mock_api.return_value = None
|
|
rc = mark_issue.main(["15", "done"])
|
|
self.assertEqual(rc, 0)
|
|
self.assertEqual(mock_api.call_count, 1)
|
|
|
|
# Verify DELETE labels call
|
|
delete_call = mock_api.call_args_list[0]
|
|
self.assertEqual(delete_call[0][0], "DELETE")
|
|
self.assertIn("/issues/15/labels/101", delete_call[0][1])
|
|
|
|
@patch("mark_issue.api_get_all", return_value=[{"id": 1, "name": "bug"}])
|
|
@patch("mark_issue.api_request")
|
|
@patch("mark_issue.get_auth_header", return_value=FAKE_AUTH)
|
|
def test_label_not_found(self, _auth, mock_api, _get_all):
|
|
# Paginated inventory returns no status:in-progress label
|
|
rc = mark_issue.main(["15", "start"])
|
|
self.assertEqual(rc, 1)
|
|
mock_api.assert_not_called()
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|