fix: revert review_pr.py to GiteaClient (tea v0.14.1 is interactive-only) (#70)
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
"""Unit tests for scripts/ci/review_pr.py."""
|
||||
|
||||
import http
|
||||
import json
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
@@ -7,8 +8,8 @@ import click
|
||||
import pytest
|
||||
from click.testing import CliRunner
|
||||
|
||||
from gitea_runner_manager.exceptions import APIError
|
||||
from scripts.ci.review_pr import main, parse_comments
|
||||
from scripts.gitea_cli import TeaCLIError
|
||||
|
||||
|
||||
class TestParseComments:
|
||||
@@ -59,25 +60,27 @@ class TestParseComments:
|
||||
|
||||
class TestMain:
|
||||
@patch.dict("os.environ", {"REPO_TOKEN": "tok"})
|
||||
@patch("scripts.ci.review_pr.TeaCLI")
|
||||
def test_successful_comment_review(self, mock_tea_cls: MagicMock) -> None:
|
||||
mock_tea = MagicMock()
|
||||
mock_tea_cls.return_value = mock_tea
|
||||
@patch("scripts.ci.review_pr.GiteaClient")
|
||||
def test_successful_comment_review(self, mock_client_cls: MagicMock) -> None:
|
||||
mock_client = MagicMock()
|
||||
mock_client.create_review.return_value = {"id": 42}
|
||||
mock_client_cls.return_value = mock_client
|
||||
runner = CliRunner()
|
||||
result = runner.invoke(
|
||||
main,
|
||||
["5", "owner/repo", "--event", "COMMENT", "--body", "LGTM"],
|
||||
)
|
||||
assert result.exit_code == 0
|
||||
assert "Review posted" in result.output
|
||||
mock_tea.review_pr.assert_called_once_with("owner/repo", 5, event="COMMENT", body="LGTM")
|
||||
assert "Review #42" in result.output
|
||||
mock_client.create_review.assert_called_once_with("5", event="COMMENT", body="LGTM", comments=[])
|
||||
|
||||
@patch.dict("os.environ", {"REPO_TOKEN": "tok"})
|
||||
@patch("scripts.ci.review_pr.TeaCLI")
|
||||
def test_successful_approve_review(self, mock_tea_cls: MagicMock) -> None:
|
||||
@patch("scripts.ci.review_pr.GiteaClient")
|
||||
def test_successful_approve_review(self, mock_client_cls: MagicMock) -> None:
|
||||
"""APPROVE requires --checklist-confirmed and substantive body."""
|
||||
mock_tea = MagicMock()
|
||||
mock_tea_cls.return_value = mock_tea
|
||||
mock_client = MagicMock()
|
||||
mock_client.create_review.return_value = {"id": 7}
|
||||
mock_client_cls.return_value = mock_client
|
||||
runner = CliRunner()
|
||||
result = runner.invoke(
|
||||
main,
|
||||
@@ -92,19 +95,20 @@ class TestMain:
|
||||
],
|
||||
)
|
||||
assert result.exit_code == 0
|
||||
mock_tea.review_pr.assert_called_once_with(
|
||||
"owner/repo",
|
||||
5,
|
||||
assert "Review #7" in result.output
|
||||
mock_client.create_review.assert_called_once_with(
|
||||
"5",
|
||||
event="APPROVE",
|
||||
body="All 10 checklist categories verified. Architecture OK, tests pass.",
|
||||
comments=[],
|
||||
)
|
||||
|
||||
@patch.dict("os.environ", {"REPO_TOKEN": "tok"})
|
||||
@patch("scripts.ci.review_pr.TeaCLI")
|
||||
def test_approve_without_checklist_confirmed_fails(self, mock_tea_cls: MagicMock) -> None:
|
||||
@patch("scripts.ci.review_pr.GiteaClient")
|
||||
def test_approve_without_checklist_confirmed_fails(self, mock_client_cls: MagicMock) -> None:
|
||||
"""APPROVE without --checklist-confirmed is rejected."""
|
||||
mock_tea = MagicMock()
|
||||
mock_tea_cls.return_value = mock_tea
|
||||
mock_client = MagicMock()
|
||||
mock_client_cls.return_value = mock_client
|
||||
runner = CliRunner()
|
||||
result = runner.invoke(
|
||||
main,
|
||||
@@ -112,14 +116,14 @@ class TestMain:
|
||||
)
|
||||
assert result.exit_code != 0
|
||||
assert "checklist" in result.output.lower()
|
||||
mock_tea.review_pr.assert_not_called()
|
||||
mock_client.create_review.assert_not_called()
|
||||
|
||||
@patch.dict("os.environ", {"REPO_TOKEN": "tok"})
|
||||
@patch("scripts.ci.review_pr.TeaCLI")
|
||||
def test_approve_with_trivial_body_fails(self, mock_tea_cls: MagicMock) -> None:
|
||||
@patch("scripts.ci.review_pr.GiteaClient")
|
||||
def test_approve_with_trivial_body_fails(self, mock_client_cls: MagicMock) -> None:
|
||||
"""APPROVE with trivial body (< 20 chars) and no comments is rejected."""
|
||||
mock_tea = MagicMock()
|
||||
mock_tea_cls.return_value = mock_tea
|
||||
mock_client = MagicMock()
|
||||
mock_client_cls.return_value = mock_client
|
||||
runner = CliRunner()
|
||||
result = runner.invoke(
|
||||
main,
|
||||
@@ -127,30 +131,32 @@ class TestMain:
|
||||
)
|
||||
assert result.exit_code != 0
|
||||
assert "substantive" in result.output.lower()
|
||||
mock_tea.review_pr.assert_not_called()
|
||||
mock_client.create_review.assert_not_called()
|
||||
|
||||
@patch.dict("os.environ", {"REPO_TOKEN": "tok"})
|
||||
@patch("scripts.ci.review_pr.TeaCLI")
|
||||
def test_successful_with_inline_comments(self, mock_tea_cls: MagicMock, tmp_path) -> None:
|
||||
@patch("scripts.ci.review_pr.GiteaClient")
|
||||
def test_successful_with_inline_comments(self, mock_client_cls: MagicMock, tmp_path) -> None:
|
||||
comments = [{"path": "a.py", "body": "fix", "new_position": 1}]
|
||||
f = tmp_path / "comments.json"
|
||||
f.write_text(json.dumps(comments))
|
||||
mock_tea = MagicMock()
|
||||
mock_tea_cls.return_value = mock_tea
|
||||
mock_client = MagicMock()
|
||||
mock_client.create_review.return_value = {"id": 9}
|
||||
mock_client_cls.return_value = mock_client
|
||||
runner = CliRunner()
|
||||
result = runner.invoke(
|
||||
main,
|
||||
["5", "owner/repo", "--comments-json", str(f)],
|
||||
)
|
||||
assert result.exit_code == 0
|
||||
mock_tea.review_pr.assert_called_once_with("owner/repo", 5, event="COMMENT", body="")
|
||||
mock_client.create_review.assert_called_once_with("5", event="COMMENT", body="", comments=comments)
|
||||
|
||||
@patch.dict("os.environ", {"REPO_TOKEN": "tok"})
|
||||
@patch("scripts.ci.review_pr.TeaCLI")
|
||||
def test_successful_with_stdin_comments(self, mock_tea_cls: MagicMock) -> None:
|
||||
@patch("scripts.ci.review_pr.GiteaClient")
|
||||
def test_successful_with_stdin_comments(self, mock_client_cls: MagicMock) -> None:
|
||||
comments = [{"path": "a.py", "body": "fix", "new_position": 1}]
|
||||
mock_tea = MagicMock()
|
||||
mock_tea_cls.return_value = mock_tea
|
||||
mock_client = MagicMock()
|
||||
mock_client.create_review.return_value = {"id": 11}
|
||||
mock_client_cls.return_value = mock_client
|
||||
runner = CliRunner()
|
||||
result = runner.invoke(
|
||||
main,
|
||||
@@ -158,7 +164,7 @@ class TestMain:
|
||||
input=json.dumps(comments),
|
||||
)
|
||||
assert result.exit_code == 0
|
||||
mock_tea.review_pr.assert_called_once_with("owner/repo", 5, event="COMMENT", body="")
|
||||
mock_client.create_review.assert_called_once_with("5", event="COMMENT", body="", comments=comments)
|
||||
|
||||
@patch.dict("os.environ", {"REPO_TOKEN": ""}, clear=True)
|
||||
def test_missing_token_exits(self) -> None:
|
||||
@@ -168,44 +174,44 @@ class TestMain:
|
||||
assert "REPO_TOKEN" in result.output
|
||||
|
||||
@patch.dict("os.environ", {"REPO_TOKEN": "tok"})
|
||||
@patch("scripts.ci.review_pr.TeaCLI")
|
||||
def test_no_body_or_comments_for_comment_event(self, mock_tea_cls: MagicMock) -> None:
|
||||
mock_tea = MagicMock()
|
||||
mock_tea_cls.return_value = mock_tea
|
||||
@patch("scripts.ci.review_pr.GiteaClient")
|
||||
def test_no_body_or_comments_for_comment_event(self, mock_client_cls: MagicMock) -> None:
|
||||
mock_client = MagicMock()
|
||||
mock_client_cls.return_value = mock_client
|
||||
runner = CliRunner()
|
||||
result = runner.invoke(main, ["5", "owner/repo", "--event", "COMMENT"])
|
||||
assert result.exit_code == 1
|
||||
assert "required" in result.output
|
||||
mock_tea.review_pr.assert_not_called()
|
||||
mock_client.create_review.assert_not_called()
|
||||
|
||||
@patch.dict("os.environ", {"REPO_TOKEN": "tok"})
|
||||
@patch("scripts.ci.review_pr.TeaCLI")
|
||||
def test_no_body_or_comments_for_request_changes(self, mock_tea_cls: MagicMock) -> None:
|
||||
mock_tea = MagicMock()
|
||||
mock_tea_cls.return_value = mock_tea
|
||||
@patch("scripts.ci.review_pr.GiteaClient")
|
||||
def test_no_body_or_comments_for_request_changes(self, mock_client_cls: MagicMock) -> None:
|
||||
mock_client = MagicMock()
|
||||
mock_client_cls.return_value = mock_client
|
||||
runner = CliRunner()
|
||||
result = runner.invoke(main, ["5", "owner/repo", "--event", "REQUEST_CHANGES"])
|
||||
assert result.exit_code == 1
|
||||
assert "required" in result.output
|
||||
mock_tea.review_pr.assert_not_called()
|
||||
mock_client.create_review.assert_not_called()
|
||||
|
||||
@patch.dict("os.environ", {"REPO_TOKEN": "tok"})
|
||||
@patch("scripts.ci.review_pr.TeaCLI")
|
||||
def test_tea_error_raises_click(self, mock_tea_cls: MagicMock) -> None:
|
||||
mock_tea = MagicMock()
|
||||
mock_tea.review_pr.side_effect = TeaCLIError("tea command failed")
|
||||
mock_tea_cls.return_value = mock_tea
|
||||
@patch("scripts.ci.review_pr.GiteaClient")
|
||||
def test_api_error_raises_click(self, mock_client_cls: MagicMock) -> None:
|
||||
mock_client = MagicMock()
|
||||
mock_client.create_review.side_effect = APIError(http.HTTPStatus.INTERNAL_SERVER_ERROR, "server error")
|
||||
mock_client_cls.return_value = mock_client
|
||||
runner = CliRunner()
|
||||
result = runner.invoke(main, ["5", "owner/repo", "--body", "x"])
|
||||
assert result.exit_code == 1
|
||||
assert "Failed to post review" in result.output
|
||||
assert "HTTP" in result.output
|
||||
|
||||
@patch.dict("os.environ", {"REPO_TOKEN": "tok"})
|
||||
@patch("scripts.ci.review_pr.TeaCLI")
|
||||
def test_invalid_event_choice(self, mock_tea_cls: MagicMock) -> None:
|
||||
mock_tea = MagicMock()
|
||||
mock_tea_cls.return_value = mock_tea
|
||||
@patch("scripts.ci.review_pr.GiteaClient")
|
||||
def test_invalid_event_choice(self, mock_client_cls: MagicMock) -> None:
|
||||
mock_client = MagicMock()
|
||||
mock_client_cls.return_value = mock_client
|
||||
runner = CliRunner()
|
||||
result = runner.invoke(main, ["5", "owner/repo", "--event", "Bogus"])
|
||||
assert result.exit_code != 0
|
||||
mock_tea.review_pr.assert_not_called()
|
||||
mock_client.create_review.assert_not_called()
|
||||
|
||||
Reference in New Issue
Block a user