diff --git a/buildbotapi.py b/buildbotapi.py index c251f537..8529650c 100644 --- a/buildbotapi.py +++ b/buildbotapi.py @@ -1,4 +1,5 @@ import json +import time from dataclasses import dataclass from typing import Any, cast @@ -6,6 +7,11 @@ JSON = dict[str, Any] +# Builders whose most recent build was more than this many days ago +# are considered inactive and ignored when checking for failures +STALE_BUILDER_DAYS = 14 +SECONDS_PER_DAY = 24 * 60 * 60 + @dataclass class Builder: @@ -66,6 +72,10 @@ async def is_builder_failing_currently(self, builder: Builder) -> bool: if not builds: return False (build,) = builds + + age_days = (time.time() - build["complete_at"]) / SECONDS_PER_DAY + if age_days > STALE_BUILDER_DAYS: + return False if build["results"] == 2: return True return False diff --git a/tests/test_buildbotapi.py b/tests/test_buildbotapi.py index 0092505e..f45e2b56 100644 --- a/tests/test_buildbotapi.py +++ b/tests/test_buildbotapi.py @@ -108,17 +108,28 @@ async def test_buildbotapi_stable_builders() -> None: assert "stable" in all_builders[3].tags +# The most recent builds in success.json and failure.json +SUCCESS_COMPLETE_AT = 1728312495 +FAILURE_COMPLETE_AT = 1734198808 +DAY = buildbotapi.SECONDS_PER_DAY + + @pytest.mark.asyncio @pytest.mark.parametrize( - ["json_data", "expected"], + ["json_data", "now", "expected"], [ - ("tests/buildbotapi/success.json", False), - ("tests/buildbotapi/failure.json", True), - ("tests/buildbotapi/no-builds.json", False), + # Recent builds: judged on their result + ("tests/buildbotapi/success.json", SUCCESS_COMPLETE_AT + DAY, False), + ("tests/buildbotapi/failure.json", FAILURE_COMPLETE_AT + DAY, True), + ("tests/buildbotapi/no-builds.json", FAILURE_COMPLETE_AT + DAY, False), + # Just inside the staleness cutoff: failure still counts + ("tests/buildbotapi/failure.json", FAILURE_COMPLETE_AT + 13 * DAY, True), + # Stale build (last run > 14 days ago): builder ignored + ("tests/buildbotapi/failure.json", FAILURE_COMPLETE_AT + 15 * DAY, False), ], ) -async def test_buildbotapi_is_builder_failing_currently_yes( - json_data: str, expected: bool +async def test_buildbotapi_is_builder_failing_currently( + monkeypatch: pytest.MonkeyPatch, json_data: str, now: int, expected: bool ) -> None: # Arrange mock_session = AsyncMock(aiohttp.ClientSession) @@ -126,6 +137,7 @@ async def test_buildbotapi_is_builder_failing_currently_yes( mock_session.get.return_value.__aenter__.return_value.text.return_value = load( json_data ) + monkeypatch.setattr("buildbotapi.time.time", lambda: now) api = buildbotapi.BuildBotAPI(mock_session) builder = buildbotapi.Builder(builderid=3)