204 lines
7.3 KiB
Python
204 lines
7.3 KiB
Python
"""Regression tests for empty Gitea Actions wrappers and merge validation fallback."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from unittest.mock import patch
|
|
|
|
import pytest
|
|
|
|
from packages.connector_kit import GiteaError, PageRequest
|
|
from tests.test_gitea_connector import connector, context
|
|
|
|
|
|
def _force_empty_actions_wrapper(connector_instance) -> None:
|
|
original_request = connector_instance.client._request
|
|
actions_runs_path = connector_instance.client._path("repos", "acme", "hub", "actions", "runs")
|
|
|
|
def request(path, *args, **kwargs):
|
|
if path == actions_runs_path:
|
|
return {"workflow_runs": []}
|
|
return original_request(path, *args, **kwargs)
|
|
|
|
connector_instance.client._request = request
|
|
|
|
|
|
def _add_validated_head_branch(fixture) -> None:
|
|
fixture._branches.append(
|
|
{"name": "codex/workflows", "commit": {"id": "e" * 40}, "protected": False}
|
|
)
|
|
|
|
|
|
def test_empty_actions_wrapper_uses_green_exact_head_status_evidence() -> None:
|
|
connector_instance, fixture = connector()
|
|
_add_validated_head_branch(fixture)
|
|
_force_empty_actions_wrapper(connector_instance)
|
|
try:
|
|
result = connector_instance.validation_list(
|
|
context(),
|
|
"hub-repository",
|
|
"codex/workflows",
|
|
page=PageRequest(limit=10),
|
|
)["data"]["items"]
|
|
|
|
assert len(result) == 1
|
|
assert result[0]["id"] == 0
|
|
assert result[0]["status"] == "completed"
|
|
assert result[0]["conclusion"] == "success"
|
|
assert result[0]["headSha"] == "e" * 40
|
|
finally:
|
|
connector_instance.client.close()
|
|
|
|
|
|
def test_merge_uses_status_linked_run_when_actions_wrapper_is_empty() -> None:
|
|
connector_instance, fixture = connector()
|
|
_add_validated_head_branch(fixture)
|
|
_force_empty_actions_wrapper(connector_instance)
|
|
try:
|
|
merged = connector_instance.pull_request_merge(
|
|
context(), "hub-repository", 7, "e" * 40, "squash"
|
|
)["data"]
|
|
assert merged["validationRunId"] == 42
|
|
assert fixture._pulls[0]["merged"] is True
|
|
finally:
|
|
connector_instance.client.close()
|
|
|
|
|
|
def test_merge_accepts_exact_head_managed_run_when_gitea_omits_workflow_id() -> None:
|
|
connector_instance, fixture = connector()
|
|
_add_validated_head_branch(fixture)
|
|
original_action_runs = connector_instance.client.action_runs
|
|
|
|
def action_runs_without_workflow_id(*args, **kwargs):
|
|
return [
|
|
{key: value for key, value in run.items() if key != "workflow_id"}
|
|
for run in original_action_runs(*args, **kwargs)
|
|
]
|
|
|
|
try:
|
|
with patch.object(
|
|
connector_instance.client,
|
|
"action_runs",
|
|
side_effect=action_runs_without_workflow_id,
|
|
):
|
|
review = connector_instance.change_review(
|
|
context(), "hub-repository", "codex/workflows"
|
|
)["data"]
|
|
assert review["readyToMerge"] is True
|
|
assert review["validationRuns"][0]["id"] > 0
|
|
assert review["validationRuns"][0]["workflowId"] == ""
|
|
|
|
merged = connector_instance.pull_request_merge(
|
|
context(), "hub-repository", 7, "e" * 40, "squash"
|
|
)["data"]
|
|
|
|
assert merged["merged"] is True
|
|
assert merged["validationRunId"] > 0
|
|
assert fixture._pulls[0]["merged"] is True
|
|
finally:
|
|
connector_instance.client.close()
|
|
|
|
|
|
def test_green_workflow_dispatch_cannot_authorize_pr_merge() -> None:
|
|
connector_instance, fixture = connector()
|
|
fixture._action_run["event"] = "workflow_dispatch"
|
|
try:
|
|
with pytest.raises(GiteaError) as captured:
|
|
connector_instance.pull_request_merge(
|
|
context(), "hub-repository", 7, "e" * 40, "squash"
|
|
)
|
|
assert captured.value.code == "GITEA_PR_VALIDATION_REQUIRED"
|
|
assert fixture._pulls[0].get("merged") is not True
|
|
finally:
|
|
connector_instance.client.close()
|
|
|
|
|
|
def test_green_pr_run_for_old_sha_cannot_authorize_merge() -> None:
|
|
connector_instance, fixture = connector()
|
|
fixture._action_run["head_sha"] = "d" * 40
|
|
try:
|
|
with pytest.raises(GiteaError) as captured:
|
|
connector_instance.pull_request_merge(
|
|
context(), "hub-repository", 7, "e" * 40, "squash"
|
|
)
|
|
assert captured.value.code == "GITEA_PR_VALIDATION_REQUIRED"
|
|
assert fixture._pulls[0].get("merged") is not True
|
|
finally:
|
|
connector_instance.client.close()
|
|
|
|
|
|
def test_green_pr_run_with_pending_required_context_cannot_authorize_merge() -> None:
|
|
connector_instance, fixture = connector()
|
|
fixture._statuses[0]["status"] = "pending"
|
|
try:
|
|
with pytest.raises(GiteaError) as captured:
|
|
connector_instance.pull_request_merge(
|
|
context(), "hub-repository", 7, "e" * 40, "squash"
|
|
)
|
|
assert captured.value.code == "GITEA_PR_CHECK_PENDING"
|
|
assert fixture._pulls[0].get("merged") is not True
|
|
finally:
|
|
connector_instance.client.close()
|
|
|
|
|
|
def test_status_linked_pr_run_without_workflow_id_requires_green_full_job() -> None:
|
|
connector_instance, fixture = connector()
|
|
fixture._action_run.pop("workflow_id")
|
|
fixture._action_job["conclusion"] = "failure"
|
|
try:
|
|
with pytest.raises(GiteaError) as captured:
|
|
connector_instance.pull_request_merge(
|
|
context(), "hub-repository", 7, "e" * 40, "squash"
|
|
)
|
|
assert captured.value.code == "GITEA_PR_VALIDATION_REQUIRED"
|
|
assert fixture._pulls[0].get("merged") is not True
|
|
finally:
|
|
connector_instance.client.close()
|
|
|
|
|
|
def test_empty_actions_wrapper_fails_closed_when_latest_status_is_pending() -> None:
|
|
connector_instance, fixture = connector()
|
|
_add_validated_head_branch(fixture)
|
|
fixture._statuses[0]["status"] = "pending"
|
|
_force_empty_actions_wrapper(connector_instance)
|
|
try:
|
|
validation_items = connector_instance.validation_list(
|
|
context(),
|
|
"hub-repository",
|
|
"codex/workflows",
|
|
page=PageRequest(limit=10),
|
|
)["data"]["items"]
|
|
assert validation_items == []
|
|
|
|
with pytest.raises(GiteaError, match="status is not successful"):
|
|
connector_instance.pull_request_merge(
|
|
context(), "hub-repository", 7, "e" * 40, "squash"
|
|
)
|
|
assert fixture._pulls[0].get("merged") is not True
|
|
assert not any(body.get("Do") == "squash" for body in fixture.recorded_bodies)
|
|
finally:
|
|
connector_instance.client.close()
|
|
|
|
|
|
def test_merge_reports_pending_readiness_instead_of_unsupported_capability() -> None:
|
|
connector_instance, _ = connector()
|
|
writer = connector_instance._writer()
|
|
|
|
def reject_pending(*args, **kwargs):
|
|
raise GiteaError(
|
|
"GITEA_STATE_TRANSITION_DENIED",
|
|
"Gitea rejected the requested state transition",
|
|
)
|
|
|
|
writer.merge_pull_request = reject_pending
|
|
try:
|
|
with pytest.raises(GiteaError) as captured:
|
|
connector_instance.pull_request_merge(
|
|
context(), "hub-repository", 7, "e" * 40, "squash"
|
|
)
|
|
|
|
assert captured.value.code == "GITEA_UPSTREAM_MERGE_REJECTED"
|
|
assert "after eligibility verification" in str(captured.value)
|
|
assert "unsupported" not in str(captured.value).casefold()
|
|
finally:
|
|
connector_instance.client.close()
|