steam-backlog-enforcer/steam_backlog_enforcer/tests/_budget_dates.py
Krzysztof kuhy Rudnicki bc02f83e7e budget: price the enforced budget by the 06:00 gaming day's leisure switch
earned_time 0.16.0 made resolve(leisure=False) force pre-leisure pricing,
so the enforced call passing False would have stayed 5 h/8 h after
go-live. It now passes leisure_active(gaming day) explicitly (its own
redirectable LEISURE_FROM_PATH): the go-live gaming day is 7 h/10 h, and
00:00-05:59 on the go-live date stays pre-leisure, matching billing. The
shadow resolve keeps leisure=True.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Dsh4Znwh2KnC7q6vbGpX1N
2026-10-19 17:09:33 +02:00

47 lines
1.7 KiB
Python

"""Pin the day ``resolve_budget`` believes it is, around the 5h -> 4h base cut.
``earned_time`` takes the reading hour off the floor from its penalty start
on: ``READING.penalty_from``, delayed by maturity (0.6.0) to the day after
``confirmed_on`` when no credit is on record. A budget test that reads the
real clock would change its answer on that day, so every such test pins the
date on one side of the cut.
The module asks ``datetime.now().astimezone()``, so that chain is what the
pin answers (at noon, so the gaming day is the same date).
"""
from __future__ import annotations
from datetime import datetime, time, timedelta
from typing import TYPE_CHECKING
from unittest.mock import MagicMock, patch
import earned_time
if TYPE_CHECKING:
from contextlib import AbstractContextManager
from datetime import date
# With no ledger on record (the suite's empty LEDGER_HOME) the cut waits for
# the day after READING.confirmed_on: fail closed, never before penalty_from.
_CUT = earned_time.penalty_start(earned_time.READING, None)
assert _CUT is not None, "earned_time.READING lost its penalty_from date"
AFTER_CUT = _CUT
BEFORE_CUT = _CUT - timedelta(days=1)
def pin_today(day: date) -> AbstractContextManager[object]:
"""Make ``_budget_resolve`` see ``day`` as today.
Args:
day: The local date the module's clock should report.
Returns:
A patch context manager replacing the module's ``datetime`` binding.
"""
clock = MagicMock()
# A real aware noon: the module also takes the 06:00 gaming day from it.
noon = datetime.combine(day, time(12)).astimezone()
clock.now.return_value.astimezone.return_value = noon
return patch("steam_backlog_enforcer._budget_resolve.datetime", clock)