ariadne/tests/test_hermes_autotriage_repair.py

251 lines
9.4 KiB
Python
Raw Permalink Normal View History

from __future__ import annotations
from types import SimpleNamespace
from ariadne.k8s import client as k8s_client
from ariadne.services import hermes_autotriage_repair as module
INCIDENT_ID = "hermes-triage-demo/12"
CONFIGMAP_PATH = "/api/v1/namespaces/hermes-triage-demo/configmaps/hermes-triage-demo-fixture"
TARGET = "hermes-triage-demo/hermes-triage-demo-fixture"
class FakeResponse:
def __init__(self, status_code: int) -> None:
self.status_code = status_code
def _cfg(**overrides) -> dict: # type: ignore[no-untyped-def]
base = {
"namespace": "hermes-triage-demo",
"fixture_configmap": "hermes-triage-demo-fixture",
}
base.update(overrides)
return base
def _jenkins_settings(**overrides) -> SimpleNamespace: # type: ignore[no-untyped-def]
values = {
"jenkins_base_url": "https://ci.example",
"jenkins_api_user": "user",
"jenkins_api_token": "token",
"jenkins_api_timeout_sec": 5.0,
}
values.update(overrides)
return SimpleNamespace(**values)
def _install_k8s(monkeypatch, patch_exc=None) -> dict: # type: ignore[no-untyped-def]
"""Record every Kubernetes call the repair path makes."""
calls: dict = {"patches": [], "posts": [], "gets": []}
def fake_patch(path, payload): # type: ignore[no-untyped-def]
calls["patches"].append((path, payload))
if patch_exc is not None:
raise patch_exc
return {"metadata": {"name": "hermes-triage-demo-fixture"}}
def fake_post(path, payload): # type: ignore[no-untyped-def]
calls["posts"].append((path, payload))
return {}
def fake_get(path): # type: ignore[no-untyped-def]
calls["gets"].append(path)
return {}
monkeypatch.setattr(module, "patch_json", fake_patch)
monkeypatch.setattr(k8s_client, "post_json", fake_post)
monkeypatch.setattr(k8s_client, "get_json", fake_get)
return calls
def _http_error(status_code: int) -> Exception:
error = RuntimeError(f"http {status_code}")
error.response = SimpleNamespace(status_code=status_code) # type: ignore[attr-defined]
return error
def test_execute_repair_patches_the_fixture_configmap(monkeypatch) -> None:
calls = _install_k8s(monkeypatch)
result = module.execute_repair(_cfg(), INCIDENT_ID, 12)
assert result == {
"action": "configmap_patch",
"target": TARGET,
"succeeded": True,
"error": None,
}
assert calls["patches"] == [(CONFIGMAP_PATH, {"data": {"state": "healthy"}})]
def test_execute_repair_creates_no_kubernetes_job(monkeypatch) -> None:
calls = _install_k8s(monkeypatch)
module.execute_repair(_cfg(), INCIDENT_ID, 12)
assert calls["posts"] == []
assert calls["gets"] == []
assert not any("/jobs" in path for path, _ in calls["patches"])
assert not hasattr(module, "_job_payload")
assert not hasattr(module, "_wait_for_completion")
def test_execute_repair_reports_non_2xx_status(monkeypatch) -> None:
calls = _install_k8s(monkeypatch, patch_exc=_http_error(403))
result = module.execute_repair(_cfg(), INCIDENT_ID, 12)
assert result["action"] == "configmap_patch"
assert result["target"] == TARGET
assert result["succeeded"] is False
assert result["error"] == "configmap patch http 403"
assert len(calls["patches"]) == 1
def test_execute_repair_survives_a_transport_error(monkeypatch) -> None:
_install_k8s(monkeypatch, patch_exc=RuntimeError("connection reset by peer"))
result = module.execute_repair(_cfg(), INCIDENT_ID, 12)
assert result["succeeded"] is False
assert result["error"] == "configmap patch failed: connection reset by peer"
def test_execute_repair_without_namespace_issues_no_request(monkeypatch) -> None:
calls = _install_k8s(monkeypatch)
result = module.execute_repair(_cfg(namespace=""), INCIDENT_ID, 12)
assert result["succeeded"] is False
assert result["error"] == "fixture target incomplete"
assert calls["patches"] == []
def test_execute_repair_without_configmap_name_issues_no_request(monkeypatch) -> None:
calls = _install_k8s(monkeypatch)
result = module.execute_repair(_cfg(fixture_configmap=None), INCIDENT_ID, 12)
assert result["succeeded"] is False
assert result["error"] == "fixture target incomplete"
assert calls["patches"] == []
def test_execute_repair_is_idempotent_across_repeats(monkeypatch) -> None:
calls = _install_k8s(monkeypatch)
first = module.execute_repair(_cfg(), INCIDENT_ID, 12)
second = module.execute_repair(_cfg(), INCIDENT_ID, 12)
assert first == second
assert calls["patches"] == [
(CONFIGMAP_PATH, {"data": {"state": "healthy"}}),
(CONFIGMAP_PATH, {"data": {"state": "healthy"}}),
]
def _install_http(monkeypatch, response=None, exc=None) -> dict: # type: ignore[no-untyped-def]
calls: dict = {"posts": [], "kwargs": None}
class FakeClient:
def __init__(self, **kwargs) -> None: # type: ignore[no-untyped-def]
calls["kwargs"] = kwargs
def __enter__(self): # type: ignore[no-untyped-def]
return self
def __exit__(self, *args) -> None: # type: ignore[no-untyped-def]
return None
def post(self, url, data=None): # type: ignore[no-untyped-def]
calls["posts"].append((url, data))
if exc is not None:
raise exc
return response
monkeypatch.setattr(module.httpx, "Client", FakeClient)
return calls
def test_trigger_rebuild_success(monkeypatch) -> None:
calls = _install_http(monkeypatch, response=FakeResponse(201))
result = module.trigger_rebuild(_jenkins_settings(), "hermes-triage-demo")
assert result == {"requested": True, "error": None}
url, data = calls["posts"][0]
assert url == "https://ci.example/job/hermes-triage-demo/buildWithParameters"
assert data == {"SEED_FAILURE": "false"}
assert calls["kwargs"]["auth"] == ("user", "token")
assert calls["kwargs"]["timeout"] == 5.0
def test_trigger_rebuild_non_created_status(monkeypatch) -> None:
_install_http(monkeypatch, response=FakeResponse(500))
result = module.trigger_rebuild(_jenkins_settings(), "hermes-triage-demo")
assert result == {"requested": False, "error": "rebuild http 500"}
def test_trigger_rebuild_request_failure(monkeypatch) -> None:
_install_http(monkeypatch, exc=RuntimeError("connect refused"))
result = module.trigger_rebuild(_jenkins_settings(), "hermes-triage-demo")
assert result["requested"] is False
assert result["error"] == "rebuild request failed: connect refused"
def test_trigger_rebuild_without_base_url(monkeypatch) -> None:
calls = _install_http(monkeypatch, response=FakeResponse(201))
result = module.trigger_rebuild(_jenkins_settings(jenkins_base_url=""), "hermes-triage-demo")
assert result == {"requested": False, "error": "jenkins base url is empty"}
assert calls["posts"] == []
def test_trigger_rebuild_without_credentials(monkeypatch) -> None:
calls = _install_http(monkeypatch, response=FakeResponse(201))
config = _jenkins_settings(jenkins_api_user="", jenkins_api_token="")
result = module.trigger_rebuild(config, "hermes-triage-demo")
assert result["requested"] is True
assert "auth" not in calls["kwargs"]
def test_retry_build_parameterized_reuses_the_rebuild_form(monkeypatch) -> None:
calls = _install_http(monkeypatch, response=FakeResponse(201))
result = module.retry_build(_jenkins_settings(), "hermes-triage-demo", parameterized=True)
assert result == {"requested": True, "error": None}
url, data = calls["posts"][0]
assert url == "https://ci.example/job/hermes-triage-demo/buildWithParameters"
assert data == {"SEED_FAILURE": "false"}
assert calls["kwargs"]["auth"] == ("user", "token")
def test_retry_build_unparameterized_posts_no_parameters(monkeypatch) -> None:
calls = _install_http(monkeypatch, response=FakeResponse(201))
result = module.retry_build(_jenkins_settings(), "titan-iac", parameterized=False)
assert result == {"requested": True, "error": None}
url, data = calls["posts"][0]
assert url == "https://ci.example/job/titan-iac/build"
assert data is None
def test_retry_build_non_created_status(monkeypatch) -> None:
_install_http(monkeypatch, response=FakeResponse(500))
result = module.retry_build(_jenkins_settings(), "titan-iac", parameterized=False)
assert result == {"requested": False, "error": "retry http 500"}
def test_retry_build_request_failure(monkeypatch) -> None:
_install_http(monkeypatch, exc=RuntimeError("connection reset by peer"))
result = module.retry_build(_jenkins_settings(), "titan-iac", parameterized=False)
assert result == {"requested": False, "error": "retry request failed: connection reset by peer"}
def test_retry_build_without_base_url(monkeypatch) -> None:
calls = _install_http(monkeypatch, response=FakeResponse(201))
result = module.retry_build(_jenkins_settings(jenkins_base_url=""), "titan-iac", parameterized=True)
assert result == {"requested": False, "error": "jenkins base url is empty"}
assert calls["posts"] == []
def test_retry_build_without_credentials(monkeypatch) -> None:
calls = _install_http(monkeypatch, response=FakeResponse(201))
config = _jenkins_settings(jenkins_api_user="", jenkins_api_token="")
result = module.retry_build(config, "titan-iac", parameterized=False)
assert result["requested"] is True
assert "auth" not in calls["kwargs"]