blob: 14c8211ac1e789a19510da19dac68212d4ae0865 [file]
import asyncio
import json
import unittest
from unittest.mock import AsyncMock, patch
from gerrit_mcp_server import main
def _make_related_entry(
change_number,
change_id,
project,
subject,
commit_sha,
parents,
status,
revision_number,
current_revision_number,
):
return {
"_change_number": change_number,
"change_id": change_id,
"project": project,
"commit": {
"commit": commit_sha,
"subject": subject,
"parents": [{"commit": p} for p in parents],
},
"status": status,
"_revision_number": revision_number,
"_current_revision_number": current_revision_number,
}
GERRIT_BASE_URL = "https://gerrit.example.com"
ENTRY_BASE = _make_related_entry(
change_number=6871420,
change_id="Iabc123base",
project="coreplatform/wasp/core.wasp",
subject="base: refactor shell init",
commit_sha="9f3cabc",
parents=["1a2bdef"],
status="MERGED",
revision_number=3,
current_revision_number=3,
)
ENTRY_QUERIED = _make_related_entry(
change_number=6871423,
change_id="Iabc123queried",
project="coreplatform/wasp/core.wasp",
subject="feature: add new thing",
commit_sha="deadbeef",
parents=["9f3cabc"],
status="NEW",
revision_number=2,
current_revision_number=2,
)
ENTRY_DESCENDANT = _make_related_entry(
change_number=6871425,
change_id="Iabc123desc",
project="coreplatform/wasp/core.wasp",
subject="followup: cleanup",
commit_sha="cafebabe",
parents=["deadbeef"],
status="NEW",
revision_number=1,
current_revision_number=1,
)
class TestGetRelatedChanges(unittest.TestCase):
@patch("gerrit_mcp_server.main.run_curl", new_callable=AsyncMock)
def test_returns_mapped_fields(self, mock_run_curl):
async def run_test():
mock_response = {"changes": [ENTRY_DESCENDANT, ENTRY_QUERIED, ENTRY_BASE]}
mock_run_curl.return_value = json.dumps(mock_response)
result = await main.get_related_changes(
"6871423", gerrit_base_url=GERRIT_BASE_URL
)
data = result
self.assertEqual(data["change_id"], "6871423")
self.assertEqual(data["revision_id"], "current")
related = data["related_changes"]
self.assertEqual(len(related), 3)
# Ordering preserved: descendant first, queried second, base last
self.assertEqual(related[0]["change_number"], 6871425)
self.assertEqual(related[0]["commit_sha"], "cafebabe")
self.assertEqual(related[0]["parents"], ["deadbeef"])
self.assertEqual(related[0]["status"], "NEW")
self.assertEqual(related[0]["subject"], "followup: cleanup")
self.assertEqual(related[0]["project"], "coreplatform/wasp/core.wasp")
self.assertEqual(related[0]["revision_number"], 1)
self.assertEqual(related[0]["current_revision_number"], 1)
self.assertEqual(related[1]["change_number"], 6871423)
self.assertEqual(related[2]["change_number"], 6871420)
self.assertEqual(related[2]["status"], "MERGED")
asyncio.run(run_test())
@patch("gerrit_mcp_server.main.run_curl", new_callable=AsyncMock)
def test_custom_revision_id(self, mock_run_curl):
async def run_test():
mock_response = {"changes": [ENTRY_QUERIED]}
mock_run_curl.return_value = json.dumps(mock_response)
result = await main.get_related_changes(
"6871423",
revision_id="abc123sha",
gerrit_base_url=GERRIT_BASE_URL,
)
data = result
self.assertEqual(data["revision_id"], "abc123sha")
# Verify the URL used the custom revision_id
call_args = mock_run_curl.call_args[0][0]
self.assertIn("abc123sha", call_args[0])
asyncio.run(run_test())
@patch("gerrit_mcp_server.main.run_curl", new_callable=AsyncMock)
def test_empty_changes_returns_note(self, mock_run_curl):
async def run_test():
mock_response = {"changes": []}
mock_run_curl.return_value = json.dumps(mock_response)
result = await main.get_related_changes(
"6871423", gerrit_base_url=GERRIT_BASE_URL
)
data = result
self.assertEqual(data["related_changes"], [])
self.assertIn("note", data)
asyncio.run(run_test())
@patch("gerrit_mcp_server.main.run_curl", new_callable=AsyncMock)
def test_stale_parent_fields_preserved(self, mock_run_curl):
async def run_test():
stale_entry = _make_related_entry(
change_number=6871420,
change_id="Istale",
project="myproject",
subject="stale parent",
commit_sha="stalesha",
parents=["rootsha"],
status="NEW",
revision_number=1,
current_revision_number=3,
)
mock_response = {"changes": [ENTRY_QUERIED, stale_entry]}
mock_run_curl.return_value = json.dumps(mock_response)
result = await main.get_related_changes(
"6871423", gerrit_base_url=GERRIT_BASE_URL
)
data = result
base = data["related_changes"][1]
self.assertEqual(base["revision_number"], 1)
self.assertEqual(base["current_revision_number"], 3)
asyncio.run(run_test())
@patch("gerrit_mcp_server.main.run_curl", new_callable=AsyncMock)
def test_json_decode_error(self, mock_run_curl):
async def run_test():
mock_run_curl.return_value = "not valid json"
with self.assertRaises(ValueError) as ctx:
await main.get_related_changes(
"6871423", gerrit_base_url=GERRIT_BASE_URL
)
self.assertIn("Failed to parse", str(ctx.exception))
asyncio.run(run_test())
@patch("gerrit_mcp_server.main.run_curl", new_callable=AsyncMock)
def test_curl_exception(self, mock_run_curl):
async def run_test():
mock_run_curl.side_effect = Exception("404 Not Found")
with self.assertRaises(Exception) as ctx:
await main.get_related_changes(
"6871423", gerrit_base_url=GERRIT_BASE_URL
)
self.assertIn("6871423", str(ctx.exception))
self.assertIn("404 Not Found", str(ctx.exception))
asyncio.run(run_test())
if __name__ == "__main__":
unittest.main()