test(hermes): cover hung-build detection to the coverage contract
Build 411 failed the per-file coverage contract at 88.89% against the 95% threshold: the module shipped exercised only indirectly through the tick, so its defensive branches were never reached. Cover the unreadable, zero, negative and future timestamps, the disabled cap, the finished build, and bundle construction from an empty payload. Module now at 100%. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
1b29b56f50
commit
e327690077
96
tests/test_hermes_hung_builds.py
Normal file
96
tests/test_hermes_hung_builds.py
Normal file
@ -0,0 +1,96 @@
|
||||
"""Tests for detecting Jenkins builds that never reach a terminal result."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from ariadne.services import hermes_hung_builds as module
|
||||
|
||||
|
||||
NOW = 1_800_000_000.0
|
||||
_MINUTE_MS = 60_000
|
||||
|
||||
|
||||
def _build(minutes_ago: float, building: bool = True, **overrides): # type: ignore[no-untyped-def]
|
||||
payload = {
|
||||
"number": 272,
|
||||
"building": building,
|
||||
"timestamp": int((NOW - minutes_ago * 60) * 1000),
|
||||
"url": "https://ci.example/job/metis/272/",
|
||||
}
|
||||
payload.update(overrides)
|
||||
return payload
|
||||
|
||||
|
||||
def test_elapsed_minutes_measures_from_the_start_timestamp() -> None:
|
||||
assert module.elapsed_minutes(_build(75), now=NOW) == 75.0
|
||||
|
||||
|
||||
def test_elapsed_minutes_treats_an_unreadable_start_as_zero() -> None:
|
||||
"""An unreadable timestamp must never look like a hung build."""
|
||||
|
||||
assert module.elapsed_minutes({}, now=NOW) == 0.0
|
||||
assert module.elapsed_minutes({"timestamp": None}, now=NOW) == 0.0
|
||||
assert module.elapsed_minutes({"timestamp": "not-a-number"}, now=NOW) == 0.0
|
||||
assert module.elapsed_minutes({"timestamp": 0}, now=NOW) == 0.0
|
||||
assert module.elapsed_minutes({"timestamp": -5}, now=NOW) == 0.0
|
||||
|
||||
|
||||
def test_elapsed_minutes_never_goes_negative() -> None:
|
||||
"""A clock skewed into the future must not produce a negative age."""
|
||||
|
||||
assert module.elapsed_minutes(_build(-30), now=NOW) == 0.0
|
||||
|
||||
|
||||
def test_elapsed_minutes_defaults_to_the_wall_clock() -> None:
|
||||
assert module.elapsed_minutes(_build(0.0, timestamp=1)) > 0.0
|
||||
|
||||
|
||||
def test_a_running_build_past_the_cap_is_hung() -> None:
|
||||
assert module.is_hung(_build(75), 45.0, now=NOW) is True
|
||||
|
||||
|
||||
def test_a_running_build_within_the_cap_is_not_hung() -> None:
|
||||
assert module.is_hung(_build(10), 45.0, now=NOW) is False
|
||||
assert module.is_hung(_build(45), 45.0, now=NOW) is False
|
||||
|
||||
|
||||
def test_a_finished_build_is_never_hung() -> None:
|
||||
"""Terminal builds are the normal path's business, not this one."""
|
||||
|
||||
assert module.is_hung(_build(600, building=False), 45.0, now=NOW) is False
|
||||
assert module.is_hung({"timestamp": 1}, 45.0, now=NOW) is False
|
||||
|
||||
|
||||
def test_a_non_positive_cap_disables_the_check() -> None:
|
||||
assert module.is_hung(_build(6000), 0.0, now=NOW) is False
|
||||
assert module.is_hung(_build(6000), -1.0, now=NOW) is False
|
||||
|
||||
|
||||
def test_hung_bundle_carries_only_observed_facts() -> None:
|
||||
bundle = module.hung_bundle("metis", _build(75), 45.0)
|
||||
jenkins = bundle["jenkins"]
|
||||
|
||||
assert jenkins["job"] == "metis"
|
||||
assert jenkins["build_number"] == 272
|
||||
assert jenkins["building"] is True
|
||||
assert jenkins["cap_minutes"] == 45.0
|
||||
assert jenkins["console_failures"] == []
|
||||
assert bundle["log_evidence"] == {"records": []}
|
||||
assert "has been running for" in jenkins["console_tail"]
|
||||
assert "holding a Jenkins agent slot" in jenkins["console_tail"]
|
||||
|
||||
|
||||
def test_hung_bundle_survives_a_build_with_nothing_in_it() -> None:
|
||||
"""Bundle construction must not raise on a sparse Jenkins payload."""
|
||||
|
||||
bundle = module.hung_bundle("metis", {}, 45.0)
|
||||
|
||||
assert bundle["jenkins"]["build_number"] is None
|
||||
assert bundle["jenkins"]["url"] == ""
|
||||
assert bundle["jenkins"]["elapsed_minutes"] == 0.0
|
||||
|
||||
|
||||
def test_reason_and_classification_are_stable_labels() -> None:
|
||||
"""These land in issue titles and event details, so they are frozen."""
|
||||
|
||||
assert module.HUNG_REASON == "hung_build"
|
||||
assert module.HUNG_CLASSIFICATION == "build_exceeded_time_cap"
|
||||
Loading…
x
Reference in New Issue
Block a user