from __future__ import annotations import pytest from ariadne.services import hermes_infra_signals as module def _bundle(regions=None, tail=None, **jenkins): # type: ignore[no-untyped-def] payload = { "job": "titan-iac", "console_failures": [{"marker": "ERROR:", "line_number": 10, "text": text} for text in regions or []], "console_tail": tail, } payload.update(jenkins) return {"incident_id": "titan-iac/12", "jenkins": payload, "log_evidence": {"records": []}} @pytest.mark.parametrize("marker", module.INFRA_MARKERS) def test_every_marker_is_detected_in_a_region(marker) -> None: bundle = _bundle(regions=[f"+ pip install -r requirements.txt\n{marker}\nbuild step failed"]) assert module.has_transient_infra_signature(bundle) == (True, marker) @pytest.mark.parametrize("marker", module.INFRA_MARKERS) def test_every_marker_is_detected_in_the_tail(marker) -> None: bundle = _bundle(tail=f"Finished: FAILURE\n{marker.upper()}") assert module.has_transient_infra_signature(bundle) == (True, marker) def test_real_dns_failure_from_pip_install() -> None: text = ( "+ pip install -r requirements.txt\n" "WARNING: Retrying (Retry(total=4)) after connection broken by " "'NewConnectionError(...: Failed to establish a new connection: " "[Errno -3] Temporary failure in name resolution')\n" "ERROR: Could not install packages due to an OSError\n" ) matched, marker = module.has_transient_infra_signature(_bundle(regions=[text])) assert matched is True assert marker == "failed to establish a new connection" def test_real_scm_connect_failure_from_git_checkout() -> None: text = ( "+ git fetch --tags --force --progress -- https://scm.bstein.dev/bstein/titan-iac.git\n" "fatal: unable to access 'https://scm.bstein.dev/bstein/titan-iac.git/': " "Failed to connect to scm.bstein.dev port 443 after 130626 ms: Connection timed out\n" ) matched, marker = module.has_transient_infra_signature(_bundle(regions=[text])) assert matched is True assert marker == "failed to connect to" def test_matching_is_case_insensitive() -> None: bundle = _bundle(regions=["Kubelet reported ErrImagePull for the agent container"]) assert module.has_transient_infra_signature(bundle) == (True, "errimagepull") def test_earliest_region_wins_over_later_regions_and_tail() -> None: bundle = _bundle( regions=["stage one: connection refused", "stage two: 502 bad gateway"], tail="Finished: FAILURE (i/o timeout)", ) assert module.has_transient_infra_signature(bundle) == (True, "connection refused") def test_earliest_marker_within_one_region_wins() -> None: text = "could not resolve host: registry.example\nthen later a 503 service unavailable" bundle = _bundle(regions=[text]) assert module.has_transient_infra_signature(bundle) == (True, "could not resolve host") def test_regions_are_searched_before_the_tail() -> None: bundle = _bundle(regions=["nothing infrastructural here"], tail="tls handshake timeout") assert module.has_transient_infra_signature(bundle) == (True, "tls handshake timeout") def test_disk_exhaustion_is_deliberately_not_transient() -> None: text = "cp: error writing '/home/jenkins/agent/workspace/x': No space left on device" bundle = _bundle(regions=[text], tail=text) assert "no space left on device" not in module.INFRA_MARKERS assert module.has_transient_infra_signature(bundle) == (False, None) def test_ordinary_test_failure_is_not_transient() -> None: text = "FAILED tests/test_discount.py::test_bulk - AssertionError: assert 0.9 == 0.8" bundle = _bundle(regions=[text], tail="short test summary info") assert module.has_transient_infra_signature(bundle) == (False, None) @pytest.mark.parametrize( "bundle", [ {}, {"jenkins": None}, {"jenkins": {}}, {"jenkins": {"console_failures": None, "console_tail": None}}, {"jenkins": {"console_failures": "not-a-list", "console_tail": 7}}, {"jenkins": {"console_failures": ["not-a-dict", {"text": None}], "console_tail": ""}}, {"jenkins": {"console_failures": [{}]}}, ], ) def test_empty_or_malformed_bundles_never_match(bundle) -> None: assert module.has_transient_infra_signature(bundle) == (False, None) def test_non_dict_bundle_never_raises() -> None: assert module.has_transient_infra_signature(None) == (False, None) # type: ignore[arg-type] assert module.has_transient_infra_signature("connection refused") == (False, None) # type: ignore[arg-type] def test_unreadable_bundle_is_swallowed() -> None: class Exploding(dict): def get(self, *args, **kwargs): # type: ignore[no-untyped-def] raise RuntimeError("boom") assert module.has_transient_infra_signature(Exploding()) == (False, None) def test_markers_are_lowercase_and_unique() -> None: assert list(module.INFRA_MARKERS) == [marker.lower() for marker in module.INFRA_MARKERS] assert len(set(module.INFRA_MARKERS)) == len(module.INFRA_MARKERS)