Files
ITWorx_MCP_Hub-Public/tests/test_gitea_upstream_discovery.py
T

231 lines
9.4 KiB
Python

"""Version negotiation canary: read-only diagnostics and fail-closed merge gates."""
from __future__ import annotations
import json
from unittest.mock import patch
import pytest
from connectors.gitea import FakeGiteaServer
from packages.connector_kit import GiteaError
from tests.test_gitea_connector import connector, context
from tests.test_gitea_merge_version_compat import _client
@pytest.mark.parametrize(
("version", "normalized", "field"),
[
("28.0.0", "28.0.0", "do"),
(" v28.0.0+gitea.1 ", "28.0.0", "do"),
("v1.25.5+vendor.7", "1.25.5", "Do"),
("1.26.4", "1.26.4", "do"),
("1.27.2", "1.27.2", "do"),
],
)
def test_upstream_discovery_normalizes_verified_release(version, normalized, field) -> None:
client, fixture, _ = _client(version)
try:
diagnostics = client.upstream_compatibility()
assert diagnostics["upstreamVersion"] == version.strip().split("+", 1)[0]
assert diagnostics["normalizedVersion"] == normalized
assert diagnostics["compatibility"] == "SUPPORTED"
assert diagnostics["mergeStrategyField"] == field
assert diagnostics["source"] == "/api/v1/version"
assert fixture.recorded_methods == ["GET"]
finally:
client.close()
@pytest.mark.parametrize(
"metadata",
[
"AKIA" + "A" * 16,
"glpat-" + "x" * 24,
"eyJ" + "a" * 15 + "." + "b" * 15 + "." + "c" * 15,
"fixture-token",
"a1" * 20,
],
)
@pytest.mark.parametrize("separator", ["+", "-"])
def test_version_diagnostics_exclude_secret_shaped_metadata(metadata, separator):
instance, fixture = connector(FakeGiteaServer(gitea_version="28.0.0" + separator + metadata))
try:
data = instance.capabilities_get(context())["data"]
diagnostics = data["upstreamGitea"]
assert metadata not in json.dumps(data)
assert diagnostics["upstreamVersion"] == "28.0.0"
assert diagnostics["normalizedVersion"] == "28.0.0"
assert diagnostics["compatibility"] == ("SUPPORTED" if separator == "+" else "UNSUPPORTED")
assert "POST" not in fixture.recorded_methods
finally:
instance.client.close()
@pytest.mark.parametrize(
("version", "verdict"),
[
("1.24.9", "UNSUPPORTED"),
("1.28.0", "UNSUPPORTED"),
("2.0.0", "UNSUPPORTED"),
("29.0.0", "UNSUPPORTED"),
("28.0.0-rc.1", "UNSUPPORTED"),
("", "UNKNOWN"),
("garbage", "UNKNOWN"),
("28.0.0+", "UNKNOWN"),
("28.0.0-", "UNKNOWN"),
("28.00.0", "UNKNOWN"),
("28.0.0\nforged", "UNKNOWN"),
],
)
def test_unknown_or_untested_upstream_fails_before_write(version, verdict) -> None:
client, fixture, repository = _client(version)
try:
diagnostics = client.upstream_compatibility()
assert diagnostics["compatibility"] == verdict
assert diagnostics["mergeStrategyField"] is None
with pytest.raises(GiteaError) as raised:
client.merge_pull_request(repository, 7, method="squash", head_commit_id="e" * 40)
assert raised.value.code == "GITEA_VERSION_UNSUPPORTED"
assert "POST" not in fixture.recorded_methods
finally:
client.close()
@pytest.mark.parametrize("payload", [{}, {"version": None}, {"version": 28}, []])
def test_missing_or_non_string_upstream_version_cannot_fall_back_to_connector(payload) -> None:
instance, fixture = connector()
try:
with patch.object(instance.client, "_request", return_value=payload):
data = instance.capabilities_get(context())["data"]
assert data["connectorVersion"] == "1.14.0"
assert data["upstreamGitea"]["compatibility"] == "UNKNOWN"
assert data["upstreamGitea"]["upstreamVersion"] is None
assert data["subsystems"]["upstream_gitea"] == "UNVERIFIED"
assert data["subsystems"]["merge"] == "UNAVAILABLE"
assert fixture.recorded_methods == []
finally:
instance.client.close()
def test_connector_and_server_versions_are_distinct_in_read_only_canary() -> None:
instance, fixture = connector(FakeGiteaServer(gitea_version="28.0.0"))
try:
data = instance.capabilities_get(context())["data"]
assert data["version"] == data["connectorVersion"] == "1.14.0"
assert data["upstreamGitea"]["upstreamVersion"] == "28.0.0"
assert data["upstreamGitea"]["compatibility"] == "SUPPORTED"
assert data["subsystems"]["upstream_gitea"] == "READY"
assert data["subsystems"]["merge"] == "READY"
canary = instance.selftest_run(context())["data"]
assert canary["status"] == "PASS"
assert any(item["name"] == "upstream-merge-compatibility" for item in canary["checks"])
assert "POST" not in fixture.recorded_methods
finally:
instance.client.close()
def test_diagnostics_cache_expires_and_rechecks_after_dependency_recovery() -> None:
client, fixture, _ = _client("28.0.0")
try:
with patch("packages.connector_kit.gitea.time.monotonic", return_value=100):
assert client.upstream_compatibility()["compatibility"] == "SUPPORTED"
fixture.gitea_version = "29.0.0"
with patch("packages.connector_kit.gitea.time.monotonic", return_value=120):
assert client.upstream_compatibility()["compatibility"] == "SUPPORTED"
with patch("packages.connector_kit.gitea.time.monotonic", return_value=131):
assert client.upstream_compatibility()["compatibility"] == "UNSUPPORTED"
fixture.gitea_version = "28.0.0"
with patch("packages.connector_kit.gitea.time.monotonic", return_value=162):
assert client.upstream_compatibility()["compatibility"] == "SUPPORTED"
assert fixture.recorded_paths.count("/api/v1/version") == 3
finally:
client.close()
def test_merge_rechecks_server_even_with_cached_successful_diagnostics() -> None:
client, fixture, repository = _client("28.0.0")
try:
assert client.upstream_compatibility()["compatibility"] == "SUPPORTED"
fixture.gitea_version = "29.0.0"
with pytest.raises(GiteaError) as raised:
client.merge_pull_request(repository, 7, method="squash", head_commit_id="e" * 40)
assert raised.value.code == "GITEA_VERSION_UNSUPPORTED"
assert "POST" not in fixture.recorded_methods
assert fixture.recorded_paths.count("/api/v1/version") == 2
finally:
client.close()
def test_version_outage_invalidates_cached_success_and_recovers() -> None:
client, fixture, repository = _client("28.0.0")
try:
assert client.upstream_compatibility()["compatibility"] == "SUPPORTED"
with patch.object(
client,
"_request",
side_effect=GiteaError("GITEA_UNAVAILABLE", "upstream request unavailable"),
):
with pytest.raises(GiteaError) as raised:
client.merge_pull_request(repository, 7, method="squash", head_commit_id="e" * 40)
assert raised.value.code == "GITEA_UNAVAILABLE"
assert client.upstream_compatibility()["compatibility"] == "UNAVAILABLE"
assert "POST" not in fixture.recorded_methods
assert client.upstream_compatibility(force_refresh=True)["compatibility"] == "SUPPORTED"
finally:
client.close()
def test_readiness_reports_failed_version_probe_without_disabling_read_service() -> None:
instance, _ = connector()
original_request = instance.client._request
def request(path, *args, **kwargs):
if path == "version":
raise GiteaError("GITEA_UNAVAILABLE", "upstream request unavailable")
return original_request(path, *args, **kwargs)
try:
with patch.object(instance.client, "_request", side_effect=request):
data = instance.readiness()
assert data["status"] == "ready"
assert data["upstream"] == "unavailable"
assert data["subsystems"]["upstream_gitea"] == "UNAVAILABLE"
assert data["subsystems"]["merge"] == "UNAVAILABLE"
assert data["upstreamGitea"]["errorCode"] == "GITEA_UNAVAILABLE"
finally:
instance.client.close()
@pytest.mark.parametrize("mutation", ["wrong-head", "wrong-event", "failed"])
def test_current_version_still_requires_exact_successful_pr_validation(mutation) -> None:
instance, fixture = connector(FakeGiteaServer(gitea_version="28.0.0"))
if mutation == "wrong-head":
fixture._action_run["head_sha"] = "d" * 40
elif mutation == "wrong-event":
fixture._action_run["event"] = "workflow_dispatch"
else:
fixture._action_run["conclusion"] = "failure"
try:
with pytest.raises(GiteaError):
instance.pull_request_merge(context(), "hub-repository", 7, "e" * 40, "squash")
assert "/api/v1/repos/acme/hub/pulls/7/merge" not in fixture.recorded_paths
assert fixture._pulls[0].get("merged") is not True
finally:
instance.client.close()
def test_current_release_allows_only_sha_locked_non_forced_merge() -> None:
instance, fixture = connector(FakeGiteaServer(gitea_version="28.0.0"))
try:
result = instance.pull_request_merge(context(), "hub-repository", 7, "e" * 40, "squash")[
"data"
]
assert result["validationRunId"] == 42
body = next(body for body in fixture.recorded_bodies if body.get("do") == "squash")
assert body["head_commit_id"] == "e" * 40
assert body["force_merge"] is False
assert body["merge_when_checks_succeed"] is False
finally:
instance.client.close()