steam-backlog-enforcer/steam_backlog_enforcer/tests/_stats_games.py
Krzysztof kuhy Rudnicki d2565416aa Pylint gate covers tests, no suppressions
Tests are now linted by the 10.00 gate (ignore-paths dropped), with no
per-test disables. CtlServer/DaemonControl members the tests drive are
public (renamed, behaviour unchanged); SteamAPIClient._get takes its
params explicitly, as every caller already did.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-11 12:17:07 +01:00

84 lines
2.9 KiB
Python

"""Shared game and rating builders for the ``_stats`` tests."""
from __future__ import annotations
from contextlib import contextmanager
from dataclasses import dataclass, field
from typing import TYPE_CHECKING
from unittest.mock import patch
from steam_backlog_enforcer._stats import _filter_qualifying_games
from steam_backlog_enforcer.protondb import ProtonDBRating
from steam_backlog_enforcer.tests._scanning_games import (
make_game as make_scanning_game,
)
if TYPE_CHECKING:
from collections.abc import Iterator
from unittest.mock import MagicMock
from steam_backlog_enforcer._stats import _GameTimes
from steam_backlog_enforcer.config import State
from steam_backlog_enforcer.steam_api import GameInfo
_GATHER = "steam_backlog_enforcer._stats_gathering"
def make_game(
app_id: int = 1,
name: str = "G",
hours: float = 10.0,
total: int = 10,
unlocked: int = 0,
) -> GameInfo:
"""Build a game with confident HLTB data (5 polls, 20 runs)."""
game = make_scanning_game(app_id, name, total, unlocked, hours)
game.comp_100_count = 5
game.count_comp = 20
return game
def unplayable_rating(app_id: int) -> ProtonDBRating:
"""Build a ProtonDB rating that marks the game unplayable on Linux."""
return ProtonDBRating(app_id=app_id, tier="borked")
@dataclass
class FilterInputs:
"""What _filter_qualifying_games reads from the HLTB caches and ProtonDB."""
rush_cache: dict[int, float] = field(default_factory=dict)
leisure_cache: dict[int, float] = field(default_factory=dict)
game_id_cache: dict[int, int] = field(default_factory=dict)
fail_reasons: list[str] = field(default_factory=list)
ratings: dict[int, ProtonDBRating] = field(default_factory=dict)
@contextmanager
def filter_env(inputs: FilterInputs | None = None) -> Iterator[MagicMock]:
"""Feed _filter_qualifying_games ``inputs``; yield the ProtonDB fetch mock."""
given = inputs or FilterInputs()
with (
patch(f"{_GATHER}.load_hltb_rush_cache", return_value=given.rush_cache),
patch(
f"{_GATHER}.load_hltb_leisure_100h_cache",
return_value=given.leisure_cache,
),
patch(f"{_GATHER}.load_hltb_game_id_cache", return_value=given.game_id_cache),
patch(f"{_GATHER}._apply_cached_confidence_to_candidates"),
patch(f"{_GATHER}._refresh_candidate_confidence_batch"),
patch(f"{_GATHER}._confidence_fail_reasons", return_value=given.fail_reasons),
patch(
f"{_GATHER}.fetch_protondb_ratings", return_value=given.ratings
) as proton,
):
yield proton
def run_filter(
games: list[GameInfo], state: State, inputs: FilterInputs | None = None
) -> tuple[list[_GameTimes], int, int, int]:
"""Run _filter_qualifying_games under :func:`filter_env`."""
with filter_env(inputs):
return _filter_qualifying_games(games, state)