chore: set up as standalone repo

Extracted from testsAndMisc monorepo. Changes:
- Rewrote imports from python_pkg.steam_backlog_enforcer.* → steam_backlog_enforcer.*
- Moved run.sh, install.sh, README.md, service file to repo root
- Added standalone pyproject.toml, requirements.txt, .pre-commit-config.yaml, .gitignore
- Added GitHub Actions CI workflows (tests + pre-commit)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Krzysztof kuhy Rudnicki 2026-05-28 07:21:29 +02:00
parent 48b609e1a3
commit 551b8a4f95
57 changed files with 1025 additions and 603 deletions

19
.github/workflows/pre-commit.yml vendored Normal file
View File

@ -0,0 +1,19 @@
name: pre-commit
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
pre-commit:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.12"
- name: Install dependencies
run: pip install -r requirements.txt
- uses: pre-commit/action@v3.0.1

33
.github/workflows/python-tests.yml vendored Normal file
View File

@ -0,0 +1,33 @@
name: Tests
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
test:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["3.10", "3.11", "3.12"]
steps:
- uses: actions/checkout@v4
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
run: pip install -r requirements.txt
- name: Run tests with coverage
run: |
python -m pytest steam_backlog_enforcer/tests/ \
--cov=steam_backlog_enforcer \
--cov-branch \
--cov-fail-under=100 \
-v

22
.gitignore vendored Normal file
View File

@ -0,0 +1,22 @@
__pycache__/
*.py[cod]
*$py.class
*.so
.Python
build/
dist/
*.egg-info/
.eggs/
.env
.venv/
venv/
ENV/
.pytest_cache/
.mypy_cache/
.ruff_cache/
coverage.xml
*.lcov
.coverage
htmlcov/
*.log
.DS_Store

164
.pre-commit-config.yaml Normal file
View File

@ -0,0 +1,164 @@
# Pre-commit Configuration for steam-backlog-enforcer
# Install: pre-commit install && pre-commit install --hook-type pre-push
# Run: pre-commit run --all-files
# Update: pre-commit autoupdate
default_language_version:
python: python3
default_stages: [pre-commit]
fail_fast: false
repos:
# ===========================================================================
# GENERAL HOOKS
# ===========================================================================
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.6.0
hooks:
- id: trailing-whitespace
args: [--markdown-linebreak-ext=md]
- id: end-of-file-fixer
- id: check-yaml
- id: check-json
- id: check-toml
- id: check-added-large-files
args: [--maxkb=2000]
- id: check-merge-conflict
- id: check-case-conflict
- id: detect-private-key
- id: debug-statements
- id: name-tests-test
args: [--pytest-test-first]
- id: check-ast
- id: check-builtin-literals
- id: check-docstring-first
- id: mixed-line-ending
args: [--fix=lf]
- id: requirements-txt-fixer
# ===========================================================================
# NOQA BLOCKER
# ===========================================================================
- repo: local
hooks:
- id: no-noqa
name: Block noqa comments
entry: '(?i)#\s*(noqa|type:\s*ignore)'
language: pygrep
types: [python]
- id: no-ruff-noqa
name: Block ruff noqa file-level comments
entry: '(?i)#\s*ruff:\s*noqa'
language: pygrep
types: [python]
# ===========================================================================
# RUFF - Linter + formatter
# ===========================================================================
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.15.2
hooks:
- id: ruff
args: [--fix, --unsafe-fixes, --exit-non-zero-on-fix, --show-fixes]
types_or: [python, pyi]
- id: ruff-format
types_or: [python, pyi]
# ===========================================================================
# MYPY - Type checking
# ===========================================================================
- repo: https://github.com/pre-commit/mirrors-mypy
rev: v1.13.0
hooks:
- id: mypy
args:
- --ignore-missing-imports
- --no-error-summary
- --disable-error-code=no-untyped-def
- --disable-error-code=no-untyped-call
- --disable-error-code=var-annotated
- --disable-error-code=no-any-unimported
- --disable-error-code=type-arg
- --disable-error-code=no-any-return
- --disable-error-code=misc
- --disable-error-code=unused-ignore
- --disable-error-code=unreachable
- --disable-error-code=assignment
- --disable-error-code=no-redef
- --disable-error-code=attr-defined
- --disable-error-code=arg-type
- --disable-error-code=union-attr
- --disable-error-code=call-overload
- --disable-error-code=return-value
- --disable-error-code=redundant-cast
- --disable-error-code=empty-body
- --disable-error-code=list-item
additional_dependencies:
- types-requests
# ===========================================================================
# PYLINT
# ===========================================================================
- repo: https://github.com/pylint-dev/pylint
rev: v3.3.2
hooks:
- id: pylint
args:
- --rcfile=pyproject.toml
- --fail-under=8.0
- --jobs=4
additional_dependencies:
- pytest
- requests
# ===========================================================================
# BANDIT - Security linter
# ===========================================================================
- repo: https://github.com/PyCQA/bandit
rev: 1.7.10
hooks:
- id: bandit
args:
- -c
- pyproject.toml
- --severity-level=high
- --confidence-level=medium
- --skip=B113
additional_dependencies: ["bandit[toml]"]
exclude: ^(tests/|.*test.*\.py$)
# ===========================================================================
# PYTEST + COVERAGE (push stage)
# ===========================================================================
- repo: local
hooks:
- id: pytest-coverage
name: pytest with coverage enforcement
entry: python -m pytest steam_backlog_enforcer/tests/ --cov=steam_backlog_enforcer --cov-branch --cov-fail-under=100
language: system
types: [python]
pass_filenames: false
require_serial: true
stages: [pre-push]
# ===========================================================================
# CODESPELL - Spell checking
# ===========================================================================
- repo: https://github.com/codespell-project/codespell
rev: v2.3.0
hooks:
- id: codespell
args:
- --skip=*.json,*.lock,.git,__pycache__,.venv
- --ignore-words-list=als,ans,ect,nd,som,sur,te,nam,numer,lew,sie,wil,postion,clen,ther,folow,derrive,ony,tje,noe,theses,crate,doubleclick,wile,tabel,pary,blok,bloc,proces,serwer,parametr,adres,hart,dout,metod,tekst,synonim,grup,mosty,lokal,skalar,milion,nowe,tre,hel,alph
# ===========================================================================
# SHELLCHECK - Shell script linting
# ===========================================================================
- repo: local
hooks:
- id: shellcheck
name: shellcheck
entry: bash -c 'printf "%s\0" "$@" | xargs -0 -n 40 shellcheck --severity=warning' --
language: system
types: [shell]

View File

@ -3,7 +3,6 @@
set -euo pipefail set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
REPO_ROOT="$(cd "$SCRIPT_DIR/../../.." && pwd)"
echo "=== Steam Backlog Enforcer Installer ===" echo "=== Steam Backlog Enforcer Installer ==="
echo echo
@ -24,9 +23,9 @@ if [[ "${ans,,}" == "y" ]]; then
SERVICE_SRC="$SCRIPT_DIR/steam-backlog-enforcer.service" SERVICE_SRC="$SCRIPT_DIR/steam-backlog-enforcer.service"
SERVICE_DST="/etc/systemd/system/steam-backlog-enforcer.service" SERVICE_DST="/etc/systemd/system/steam-backlog-enforcer.service"
# Set the correct working directory in the service file. # Set the correct working directory and PYTHONPATH in the service file.
sed "s|WorkingDirectory=.*|WorkingDirectory=$REPO_ROOT|" "$SERVICE_SRC" \ sed "s|WorkingDirectory=.*|WorkingDirectory=$SCRIPT_DIR|; s|PYTHONPATH=.*|PYTHONPATH=$SCRIPT_DIR|" \
> "$SERVICE_DST" "$SERVICE_SRC" > "$SERVICE_DST"
systemctl daemon-reload systemctl daemon-reload
systemctl enable steam-backlog-enforcer systemctl enable steam-backlog-enforcer
@ -38,4 +37,4 @@ fi
echo echo
echo "Done! Run manually with:" echo "Done! Run manually with:"
echo " sudo python3 -m python_pkg.steam_backlog_enforcer.main enforce" echo " python3 -m steam_backlog_enforcer.main enforce"

166
pyproject.toml Normal file
View File

@ -0,0 +1,166 @@
[project]
name = "steam-backlog-enforcer"
version = "1.0.0"
description = "Enforce your Steam backlog: one game at a time, tracked with HowLongToBeat data"
requires-python = ">=3.10"
dependencies = [
"requests>=2.0",
"howlongtobeatpy>=1.0",
]
# ============================================================================
# RUFF - Fast Python linter and formatter
# ============================================================================
[tool.ruff]
target-version = "py310"
include = ["*.py", "**/*.py"]
exclude = [".git", ".venv", "__pycache__", "build", "dist", ".eggs"]
[tool.ruff.lint]
select = ["ALL"]
ignore = [
"D203", # conflicts with D211
"D213", # conflicts with D212
"COM812", # formatter handles this
"ISC001", # formatter may create these
"S603", # prone to false positives
]
fixable = ["ALL"]
unfixable = []
[tool.ruff.lint.per-file-ignores]
"**/tests/**/*.py" = ["ARG", "D", "PLC0415", "PLR2004", "S101", "SLF001"]
"**/test_*.py" = ["ARG", "D", "PLC0415", "PLR2004", "S101", "SLF001"]
[tool.ruff.lint.pydocstyle]
convention = "google"
[tool.ruff.lint.isort]
force-single-line = false
force-sort-within-sections = true
known-first-party = ["steam_backlog_enforcer"]
[tool.ruff.lint.flake8-quotes]
docstring-quotes = "double"
inline-quotes = "double"
[tool.ruff.lint.flake8-tidy-imports]
ban-relative-imports = "all"
[tool.ruff.format]
quote-style = "double"
indent-style = "space"
skip-magic-trailing-comma = false
line-ending = "auto"
docstring-code-format = true
# ============================================================================
# MYPY - Static type checker
# ============================================================================
[tool.mypy]
python_version = "3.10"
strict = true
warn_return_any = true
warn_unused_configs = true
disallow_untyped_defs = true
disallow_incomplete_defs = true
check_untyped_defs = true
disallow_untyped_decorators = true
no_implicit_optional = true
warn_redundant_casts = true
warn_unused_ignores = true
warn_no_return = true
warn_unreachable = true
disallow_any_unimported = true
disallow_any_explicit = false
disallow_any_generics = true
disallow_subclassing_any = true
strict_equality = true
extra_checks = true
ignore_missing_imports = true
show_error_codes = true
color_output = true
exclude = [".venv/"]
# ============================================================================
# PYLINT
# ============================================================================
[tool.pylint.main]
analyse-fallback-blocks = true
persistent = true
jobs = 0
py-version = "3.10"
ignore = [".venv", "__pycache__"]
ignore-patterns = [".*\\.pyi$"]
[tool.pylint.messages_control]
enable = "all"
disable = []
[tool.pylint.design]
min-public-methods = 0
max-module-lines = 1000
max-attributes = 10
[tool.pylint.typecheck]
generated-members = [
".*\\.assert_called_once_with",
".*\\.assert_called_once",
".*\\.assert_called",
".*\\.assert_not_called",
".*\\.assert_any_call",
".*\\.call_args",
".*\\.call_args_list",
".*\\.call_count",
]
# ============================================================================
# BANDIT - Security linter
# ============================================================================
[tool.bandit]
exclude_dirs = ["tests", ".venv"]
# ============================================================================
# PYTEST
# ============================================================================
[tool.pytest.ini_options]
testpaths = ["steam_backlog_enforcer/tests"]
python_files = ["test_*.py", "*_test.py"]
python_classes = ["Test*"]
python_functions = ["test_*"]
addopts = [
"-v",
"--strict-markers",
"--strict-config",
"-ra",
"--cov=steam_backlog_enforcer",
"--cov-branch",
"--cov-report=term-missing",
"--cov-report=lcov",
]
filterwarnings = [
"error",
"ignore::DeprecationWarning",
"default::pytest.PytestUnraisableExceptionWarning",
]
# ============================================================================
# COVERAGE
# ============================================================================
[tool.coverage.run]
source = ["steam_backlog_enforcer"]
branch = true
omit = ["*/__pycache__/*", "*/tests/*", "*/.venv/*"]
[tool.coverage.report]
fail_under = 100
show_missing = true
skip_covered = false
exclude_lines = [
"pragma: no cover",
"raise NotImplementedError",
"raise AssertionError",
"if TYPE_CHECKING:",
'if __name__ == "__main__":',
]
partial_branches = ["pragma: no branch"]

19
requirements.txt Normal file
View File

@ -0,0 +1,19 @@
# Steam Backlog Enforcer — runtime + development dependencies
# Install with: pip install -r requirements.txt
bandit>=1.7.0
codespell>=2.2.0
coverage>=7.4.0
howlongtobeatpy>=1.0
mypy>=1.8.0
pre-commit>=3.6.0
pylint>=3.0.0
pytest>=8.0.0
pytest-cov>=4.1.0
pytest-randomly>=3.15.0
pytest-sugar>=1.0.0
pytest-timeout>=2.2.0
pytest-xdist>=3.5.0
requests>=2.0
ruff>=0.8.0
types-requests>=2.31.0

View File

@ -3,5 +3,5 @@
# Usage: ./run.sh [command] (defaults to "done" if no command given) # Usage: ./run.sh [command] (defaults to "done" if no command given)
set -euo pipefail set -euo pipefail
cd "$(dirname "$0")/../.." cd "$(dirname "$0")"
exec python -m python_pkg.steam_backlog_enforcer.main "${1:-done}" exec python -m steam_backlog_enforcer.main "${1:-done}"

View File

@ -5,12 +5,12 @@ Wants=network-online.target
[Service] [Service]
Type=simple Type=simple
WorkingDirectory=/home/kuhy/testsAndMisc WorkingDirectory=/opt/steam-backlog-enforcer
ExecStart=/usr/bin/python3 -m python_pkg.steam_backlog_enforcer.main enforce ExecStart=/usr/bin/python3 -m steam_backlog_enforcer.main enforce
Restart=always Restart=always
RestartSec=5 RestartSec=5
Environment=PYTHONUNBUFFERED=1 Environment=PYTHONUNBUFFERED=1
Environment=PYTHONPATH=/home/kuhy/testsAndMisc:/home/kuhy/.local/lib/python3.14/site-packages Environment=PYTHONPATH=/opt/steam-backlog-enforcer
Environment=HOME=/home/kuhy Environment=HOME=/home/kuhy
# Hardening: enforcer must not be easily killed. # Hardening: enforcer must not be easily killed.
OOMScoreAdjust=-900 OOMScoreAdjust=-900

View File

@ -5,28 +5,28 @@ from __future__ import annotations
import logging import logging
import sys import sys
from python_pkg.steam_backlog_enforcer._enforce_loop import get_all_owned_app_ids from steam_backlog_enforcer._enforce_loop import get_all_owned_app_ids
from python_pkg.steam_backlog_enforcer.config import Config, State, load_snapshot from steam_backlog_enforcer.config import Config, State, load_snapshot
from python_pkg.steam_backlog_enforcer.enforcer import ( from steam_backlog_enforcer.enforcer import (
enforce_allowed_game, enforce_allowed_game,
send_notification, send_notification,
) )
from python_pkg.steam_backlog_enforcer.game_install import ( from steam_backlog_enforcer.game_install import (
_echo, _echo,
install_game, install_game,
is_game_installed, is_game_installed,
uninstall_other_games, uninstall_other_games,
) )
from python_pkg.steam_backlog_enforcer.hltb import ( from steam_backlog_enforcer.hltb import (
fetch_hltb_confidence_cached, fetch_hltb_confidence_cached,
fetch_hltb_times_cached, fetch_hltb_times_cached,
load_hltb_cache, load_hltb_cache,
load_hltb_polls_cache, load_hltb_polls_cache,
save_hltb_cache, save_hltb_cache,
) )
from python_pkg.steam_backlog_enforcer.library_hider import hide_other_games from steam_backlog_enforcer.library_hider import hide_other_games
from python_pkg.steam_backlog_enforcer.scanning import pick_next_game from steam_backlog_enforcer.scanning import pick_next_game
from python_pkg.steam_backlog_enforcer.steam_api import GameInfo, SteamAPIClient from steam_backlog_enforcer.steam_api import GameInfo, SteamAPIClient
_REASSIGN_REFRESH_LIMIT = 50 _REASSIGN_REFRESH_LIMIT = 50
_SKIP_DAYS = 7 _SKIP_DAYS = 7

View File

@ -7,11 +7,11 @@ import logging
import time import time
from typing import Any from typing import Any
from python_pkg.steam_backlog_enforcer._whitelist import ( from steam_backlog_enforcer._whitelist import (
lock_enforcement_files, lock_enforcement_files,
promote_pending_exceptions, promote_pending_exceptions,
) )
from python_pkg.steam_backlog_enforcer.config import ( from steam_backlog_enforcer.config import (
CONFIG_DIR, CONFIG_DIR,
CONFIG_FILE, CONFIG_FILE,
Config, Config,
@ -19,11 +19,11 @@ from python_pkg.steam_backlog_enforcer.config import (
_atomic_write, _atomic_write,
load_snapshot, load_snapshot,
) )
from python_pkg.steam_backlog_enforcer.enforcer import ( from steam_backlog_enforcer.enforcer import (
enforce_allowed_game, enforce_allowed_game,
send_notification, send_notification,
) )
from python_pkg.steam_backlog_enforcer.game_install import ( from steam_backlog_enforcer.game_install import (
_echo, _echo,
get_installed_games, get_installed_games,
install_game, install_game,
@ -32,9 +32,9 @@ from python_pkg.steam_backlog_enforcer.game_install import (
uninstall_game, uninstall_game,
uninstall_other_games, uninstall_other_games,
) )
from python_pkg.steam_backlog_enforcer.library_hider import hide_other_games from steam_backlog_enforcer.library_hider import hide_other_games
from python_pkg.steam_backlog_enforcer.steam_api import SteamAPIClient from steam_backlog_enforcer.steam_api import SteamAPIClient
from python_pkg.steam_backlog_enforcer.store_blocker import block_store from steam_backlog_enforcer.store_blocker import block_store
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
_OWNED_IDS_CACHE_FILE = CONFIG_DIR / "owned_app_ids_cache.json" _OWNED_IDS_CACHE_FILE = CONFIG_DIR / "owned_app_ids_cache.json"

View File

@ -11,7 +11,7 @@ from typing import Any
import aiohttp import aiohttp
from python_pkg.steam_backlog_enforcer._hltb_types import ( from steam_backlog_enforcer._hltb_types import (
_SAVE_INTERVAL, _SAVE_INTERVAL,
HLTB_BASE_URL, HLTB_BASE_URL,
MAX_CONCURRENT, MAX_CONCURRENT,

View File

@ -15,10 +15,10 @@ from typing import Any
import aiohttp import aiohttp
from howlongtobeatpy.HTMLRequests import HTMLRequests from howlongtobeatpy.HTMLRequests import HTMLRequests
from python_pkg.steam_backlog_enforcer._hltb_detail import ( from steam_backlog_enforcer._hltb_detail import (
_fetch_leisure_times, _fetch_leisure_times,
) )
from python_pkg.steam_backlog_enforcer._hltb_types import ( from steam_backlog_enforcer._hltb_types import (
_SAVE_INTERVAL, _SAVE_INTERVAL,
_SUBSET_SUFFIXES, _SUBSET_SUFFIXES,
MAX_CONCURRENT, MAX_CONCURRENT,

View File

@ -8,7 +8,7 @@ import json
import logging import logging
from typing import Any from typing import Any
from python_pkg.steam_backlog_enforcer.config import CONFIG_DIR, _atomic_write from steam_backlog_enforcer.config import CONFIG_DIR, _atomic_write
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)

View File

@ -5,19 +5,19 @@ from __future__ import annotations
import logging import logging
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
from python_pkg.steam_backlog_enforcer._hltb_types import ( from steam_backlog_enforcer._hltb_types import (
_HLTBExtras, _HLTBExtras,
load_hltb_cache, load_hltb_cache,
load_hltb_count_comp_cache, load_hltb_count_comp_cache,
load_hltb_polls_cache, load_hltb_polls_cache,
save_hltb_cache, save_hltb_cache,
) )
from python_pkg.steam_backlog_enforcer.game_install import _echo from steam_backlog_enforcer.game_install import _echo
from python_pkg.steam_backlog_enforcer.hltb import fetch_hltb_confidence_cached from steam_backlog_enforcer.hltb import fetch_hltb_confidence_cached
if TYPE_CHECKING: if TYPE_CHECKING:
from python_pkg.steam_backlog_enforcer.config import State from steam_backlog_enforcer.config import State
from python_pkg.steam_backlog_enforcer.steam_api import GameInfo from steam_backlog_enforcer.steam_api import GameInfo
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)

View File

@ -9,29 +9,29 @@ import secrets
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
from urllib.parse import quote_plus from urllib.parse import quote_plus
from python_pkg.steam_backlog_enforcer._hltb_types import ( from steam_backlog_enforcer._hltb_types import (
HLTB_BASE_URL, HLTB_BASE_URL,
load_hltb_cache, load_hltb_cache,
load_hltb_game_id_cache, load_hltb_game_id_cache,
load_hltb_leisure_100h_cache, load_hltb_leisure_100h_cache,
load_hltb_rush_cache, load_hltb_rush_cache,
) )
from python_pkg.steam_backlog_enforcer._scanning_confidence import ( from steam_backlog_enforcer._scanning_confidence import (
_apply_cached_confidence_to_candidates, _apply_cached_confidence_to_candidates,
_confidence_fail_reasons, _confidence_fail_reasons,
_refresh_candidate_confidence_batch, _refresh_candidate_confidence_batch,
) )
from python_pkg.steam_backlog_enforcer.config import load_snapshot from steam_backlog_enforcer.config import load_snapshot
from python_pkg.steam_backlog_enforcer.game_install import _echo from steam_backlog_enforcer.game_install import _echo
from python_pkg.steam_backlog_enforcer.hltb import fetch_hltb_detail_missing from steam_backlog_enforcer.hltb import fetch_hltb_detail_missing
from python_pkg.steam_backlog_enforcer.protondb import ( from steam_backlog_enforcer.protondb import (
ProtonDBRating, ProtonDBRating,
fetch_protondb_ratings, fetch_protondb_ratings,
) )
from python_pkg.steam_backlog_enforcer.steam_api import GameInfo from steam_backlog_enforcer.steam_api import GameInfo
if TYPE_CHECKING: if TYPE_CHECKING:
from python_pkg.steam_backlog_enforcer.config import Config, State from steam_backlog_enforcer.config import Config, State
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)

View File

@ -13,7 +13,7 @@ import subprocess
import time import time
from typing import TYPE_CHECKING, cast from typing import TYPE_CHECKING, cast
from python_pkg.steam_backlog_enforcer.config import CONFIG_DIR, _atomic_write from steam_backlog_enforcer.config import CONFIG_DIR, _atomic_write
if TYPE_CHECKING: if TYPE_CHECKING:
from pathlib import Path from pathlib import Path

View File

@ -9,7 +9,7 @@ import shutil
import signal import signal
import subprocess import subprocess
from python_pkg.steam_backlog_enforcer.game_install import ( from steam_backlog_enforcer.game_install import (
is_protected_app, is_protected_app,
) )

View File

@ -13,7 +13,7 @@ import subprocess
import sys import sys
import time import time
from python_pkg.steam_backlog_enforcer._whitelist import get_approved_exception_ids from steam_backlog_enforcer._whitelist import get_approved_exception_ids
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)

View File

@ -18,14 +18,14 @@ import time
import aiohttp import aiohttp
from python_pkg.steam_backlog_enforcer._hltb_search import ( from steam_backlog_enforcer._hltb_search import (
_fetch_batch, _fetch_batch,
_get_auth_info, _get_auth_info,
_get_hltb_search_url, _get_hltb_search_url,
_search_one, _search_one,
_SearchCtx, _SearchCtx,
) )
from python_pkg.steam_backlog_enforcer._hltb_types import ( from steam_backlog_enforcer._hltb_types import (
HLTB_BASE_URL, HLTB_BASE_URL,
MAX_CONCURRENT, MAX_CONCURRENT,
HLTBResult, HLTBResult,

View File

@ -7,26 +7,26 @@ import sys
import time import time
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
from python_pkg.steam_backlog_enforcer._cmd_done import cmd_done from steam_backlog_enforcer._cmd_done import cmd_done
from python_pkg.steam_backlog_enforcer._enforce_loop import ( from steam_backlog_enforcer._enforce_loop import (
do_enforce, do_enforce,
get_all_owned_app_ids, get_all_owned_app_ids,
) )
from python_pkg.steam_backlog_enforcer._hltb_types import load_hltb_cache from steam_backlog_enforcer._hltb_types import load_hltb_cache
from python_pkg.steam_backlog_enforcer._stats import cmd_stats from steam_backlog_enforcer._stats import cmd_stats
from python_pkg.steam_backlog_enforcer._whitelist import ( from steam_backlog_enforcer._whitelist import (
WHITELIST_COOLDOWN_SECONDS, WHITELIST_COOLDOWN_SECONDS,
add_pending_exception, add_pending_exception,
list_pending_exceptions, list_pending_exceptions,
validate_reason, validate_reason,
) )
from python_pkg.steam_backlog_enforcer.config import ( from steam_backlog_enforcer.config import (
Config, Config,
State, State,
interactive_setup, interactive_setup,
load_snapshot, load_snapshot,
) )
from python_pkg.steam_backlog_enforcer.game_install import ( from steam_backlog_enforcer.game_install import (
_echo, _echo,
get_installed_games, get_installed_games,
install_game, install_game,
@ -34,18 +34,18 @@ from python_pkg.steam_backlog_enforcer.game_install import (
is_protected_app, is_protected_app,
uninstall_other_games, uninstall_other_games,
) )
from python_pkg.steam_backlog_enforcer.library_hider import ( from steam_backlog_enforcer.library_hider import (
hide_other_games, hide_other_games,
restart_steam, restart_steam,
unhide_all_games, unhide_all_games,
) )
from python_pkg.steam_backlog_enforcer.scanning import ( from steam_backlog_enforcer.scanning import (
do_check, do_check,
do_scan, do_scan,
pick_next_game, pick_next_game,
) )
from python_pkg.steam_backlog_enforcer.steam_api import GameInfo from steam_backlog_enforcer.steam_api import GameInfo
from python_pkg.steam_backlog_enforcer.store_blocker import ( from steam_backlog_enforcer.store_blocker import (
block_store, block_store,
is_store_blocked, is_store_blocked,
unblock_store, unblock_store,
@ -415,7 +415,7 @@ def main() -> None:
"""CLI entry point.""" """CLI entry point."""
if len(sys.argv) < _MIN_CLI_ARGS or sys.argv[1] not in _ALL_COMMANDS: if len(sys.argv) < _MIN_CLI_ARGS or sys.argv[1] not in _ALL_COMMANDS:
_echo("Steam Backlog Enforcer\n") _echo("Steam Backlog Enforcer\n")
_echo("Usage: python -m python_pkg.steam_backlog_enforcer.main <command>\n") _echo("Usage: python -m steam_backlog_enforcer.main <command>\n")
_echo("Commands:") _echo("Commands:")
for name, desc in _ALL_COMMANDS.items(): for name, desc in _ALL_COMMANDS.items():
_echo(f" {name:<14s} {desc}") _echo(f" {name:<14s} {desc}")

View File

@ -17,7 +17,7 @@ from typing import Any
import aiohttp import aiohttp
from python_pkg.steam_backlog_enforcer.config import CONFIG_DIR, _atomic_write from steam_backlog_enforcer.config import CONFIG_DIR, _atomic_write
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)

View File

@ -7,38 +7,38 @@ import logging
import time import time
from typing import TYPE_CHECKING, Any from typing import TYPE_CHECKING, Any
from python_pkg.steam_backlog_enforcer._hltb_types import ( from steam_backlog_enforcer._hltb_types import (
load_hltb_count_comp_cache, load_hltb_count_comp_cache,
load_hltb_polls_cache, load_hltb_polls_cache,
) )
from python_pkg.steam_backlog_enforcer._scanning_confidence import ( from steam_backlog_enforcer._scanning_confidence import (
_apply_cached_confidence_to_candidates, _apply_cached_confidence_to_candidates,
_candidate_passes_hltb_confidence, _candidate_passes_hltb_confidence,
_report_poll_confidence, _report_poll_confidence,
) )
from python_pkg.steam_backlog_enforcer.config import ( from steam_backlog_enforcer.config import (
Config, Config,
State, State,
load_snapshot, load_snapshot,
save_snapshot, save_snapshot,
) )
from python_pkg.steam_backlog_enforcer.enforcer import ( from steam_backlog_enforcer.enforcer import (
send_notification, send_notification,
) )
from python_pkg.steam_backlog_enforcer.game_install import ( from steam_backlog_enforcer.game_install import (
_echo, _echo,
install_game, install_game,
is_game_installed, is_game_installed,
uninstall_other_games, uninstall_other_games,
) )
from python_pkg.steam_backlog_enforcer.hltb import ( from steam_backlog_enforcer.hltb import (
fetch_hltb_times_cached, fetch_hltb_times_cached,
) )
from python_pkg.steam_backlog_enforcer.protondb import ( from steam_backlog_enforcer.protondb import (
ProtonDBRating, ProtonDBRating,
fetch_protondb_ratings, fetch_protondb_ratings,
) )
from python_pkg.steam_backlog_enforcer.steam_api import GameInfo, SteamAPIClient from steam_backlog_enforcer.steam_api import GameInfo, SteamAPIClient
if TYPE_CHECKING: if TYPE_CHECKING:
from collections.abc import Callable from collections.abc import Callable

View File

@ -21,7 +21,7 @@ import shutil
import socket import socket
import subprocess import subprocess
from python_pkg.steam_backlog_enforcer.config import ( from steam_backlog_enforcer.config import (
BLOCKED_DOMAINS, BLOCKED_DOMAINS,
HOSTS_FILE, HOSTS_FILE,
) )

View File

@ -39,58 +39,58 @@ def _isolate_filesystem(tmp_path: Path) -> Iterator[None]:
with ( with (
# Config / state / snapshot paths (used by State.save, Config.save, etc.) # Config / state / snapshot paths (used by State.save, Config.save, etc.)
patch( patch(
"python_pkg.steam_backlog_enforcer.config.CONFIG_DIR", "steam_backlog_enforcer.config.CONFIG_DIR",
fake_config, fake_config,
), ),
patch( patch(
"python_pkg.steam_backlog_enforcer.config.CONFIG_FILE", "steam_backlog_enforcer.config.CONFIG_FILE",
fake_config / "config.json", fake_config / "config.json",
), ),
patch( patch(
"python_pkg.steam_backlog_enforcer.config.STATE_FILE", "steam_backlog_enforcer.config.STATE_FILE",
fake_config / "state.json", fake_config / "state.json",
), ),
patch( patch(
"python_pkg.steam_backlog_enforcer.config.SNAPSHOT_FILE", "steam_backlog_enforcer.config.SNAPSHOT_FILE",
fake_config / "snapshot.json", fake_config / "snapshot.json",
), ),
# Steam game manifests / install dirs # Steam game manifests / install dirs
patch( patch(
"python_pkg.steam_backlog_enforcer.game_install.STEAMAPPS_PATH", "steam_backlog_enforcer.game_install.STEAMAPPS_PATH",
fake_steamapps, fake_steamapps,
), ),
# HLTB cache file (computed at import time from CONFIG_DIR, so # HLTB cache file (computed at import time from CONFIG_DIR, so
# patching CONFIG_DIR alone does not redirect it) # patching CONFIG_DIR alone does not redirect it)
patch( patch(
"python_pkg.steam_backlog_enforcer._hltb_types.HLTB_CACHE_FILE", "steam_backlog_enforcer._hltb_types.HLTB_CACHE_FILE",
fake_config / "hltb_cache.json", fake_config / "hltb_cache.json",
), ),
# /etc/hosts (store blocker) # /etc/hosts (store blocker)
patch( patch(
"python_pkg.steam_backlog_enforcer.store_blocker.HOSTS_FILE", "steam_backlog_enforcer.store_blocker.HOSTS_FILE",
fake_hosts, fake_hosts,
), ),
patch( patch(
"python_pkg.steam_backlog_enforcer.config.HOSTS_FILE", "steam_backlog_enforcer.config.HOSTS_FILE",
fake_hosts, fake_hosts,
), ),
# Whitelist exception files (_whitelist module-level constants) # Whitelist exception files (_whitelist module-level constants)
patch( patch(
"python_pkg.steam_backlog_enforcer._whitelist.PENDING_EXCEPTIONS_FILE", "steam_backlog_enforcer._whitelist.PENDING_EXCEPTIONS_FILE",
fake_config / "pending_exceptions.json", fake_config / "pending_exceptions.json",
), ),
patch( patch(
"python_pkg.steam_backlog_enforcer._whitelist.APPROVED_EXCEPTIONS_FILE", "steam_backlog_enforcer._whitelist.APPROVED_EXCEPTIONS_FILE",
fake_config / "approved_exceptions.json", fake_config / "approved_exceptions.json",
), ),
patch( patch(
"python_pkg.steam_backlog_enforcer._whitelist.EXCEPTION_AUDIT_LOG", "steam_backlog_enforcer._whitelist.EXCEPTION_AUDIT_LOG",
fake_config / "exception_audit.log", fake_config / "exception_audit.log",
), ),
# _enforce_loop imports CONFIG_FILE directly; patch the local binding so # _enforce_loop imports CONFIG_FILE directly; patch the local binding so
# lock_enforcement_files() uses the tmp path instead of the real one. # lock_enforcement_files() uses the tmp path instead of the real one.
patch( patch(
"python_pkg.steam_backlog_enforcer._enforce_loop.CONFIG_FILE", "steam_backlog_enforcer._enforce_loop.CONFIG_FILE",
fake_config / "config.json", fake_config / "config.json",
), ),
): ):
@ -110,27 +110,27 @@ def _block_real_subprocesses() -> Iterator[None]:
with ( with (
patch( patch(
"python_pkg.steam_backlog_enforcer.game_install.subprocess.run", "steam_backlog_enforcer.game_install.subprocess.run",
noop_run, noop_run,
), ),
patch( patch(
"python_pkg.steam_backlog_enforcer.game_install.subprocess.Popen", "steam_backlog_enforcer.game_install.subprocess.Popen",
noop_popen, noop_popen,
), ),
patch( patch(
"python_pkg.steam_backlog_enforcer.enforcer.subprocess.run", "steam_backlog_enforcer.enforcer.subprocess.run",
noop_run, noop_run,
), ),
patch( patch(
"python_pkg.steam_backlog_enforcer.store_blocker.subprocess.run", "steam_backlog_enforcer.store_blocker.subprocess.run",
noop_run, noop_run,
), ),
patch( patch(
"python_pkg.steam_backlog_enforcer.library_hider.subprocess.run", "steam_backlog_enforcer.library_hider.subprocess.run",
noop_run, noop_run,
), ),
patch( patch(
"python_pkg.steam_backlog_enforcer.library_hider.subprocess.Popen", "steam_backlog_enforcer.library_hider.subprocess.Popen",
noop_popen, noop_popen,
), ),
): ):
@ -147,9 +147,9 @@ def _no_real_sleep() -> Iterator[None]:
""" """
noop = MagicMock() noop = MagicMock()
with ( with (
patch("python_pkg.steam_backlog_enforcer.game_install.time.sleep", noop), patch("steam_backlog_enforcer.game_install.time.sleep", noop),
patch("python_pkg.steam_backlog_enforcer.library_hider.time.sleep", noop), patch("steam_backlog_enforcer.library_hider.time.sleep", noop),
patch("python_pkg.steam_backlog_enforcer.steam_api.time.sleep", noop), patch("steam_backlog_enforcer.steam_api.time.sleep", noop),
patch("python_pkg.steam_backlog_enforcer._enforce_loop.time.sleep", noop), patch("steam_backlog_enforcer._enforce_loop.time.sleep", noop),
): ):
yield yield

View File

@ -4,10 +4,10 @@ from __future__ import annotations
from unittest.mock import patch from unittest.mock import patch
from python_pkg.steam_backlog_enforcer._cmd_done import _prompt_keep_or_skip from steam_backlog_enforcer._cmd_done import _prompt_keep_or_skip
from python_pkg.steam_backlog_enforcer.steam_api import GameInfo from steam_backlog_enforcer.steam_api import GameInfo
CMD_DONE_PKG = "python_pkg.steam_backlog_enforcer._cmd_done" CMD_DONE_PKG = "steam_backlog_enforcer._cmd_done"
class TestPromptKeepOrSkip: class TestPromptKeepOrSkip:

View File

@ -8,7 +8,7 @@ from unittest.mock import patch
import pytest import pytest
from python_pkg.steam_backlog_enforcer.config import ( from steam_backlog_enforcer.config import (
Config, Config,
State, State,
_atomic_write, _atomic_write,
@ -38,7 +38,7 @@ class TestAtomicWrite:
target = tmp_path / "out.json" target = tmp_path / "out.json"
with ( with (
patch( patch(
"python_pkg.steam_backlog_enforcer.config.os.write", "steam_backlog_enforcer.config.os.write",
side_effect=OSError("disk full"), side_effect=OSError("disk full"),
), ),
pytest.raises(OSError, match="disk full"), pytest.raises(OSError, match="disk full"),
@ -81,8 +81,8 @@ class TestConfig:
config_dir = tmp_path / "cfg" config_dir = tmp_path / "cfg"
config_file = config_dir / "config.json" config_file = config_dir / "config.json"
with ( with (
patch("python_pkg.steam_backlog_enforcer.config.CONFIG_DIR", config_dir), patch("steam_backlog_enforcer.config.CONFIG_DIR", config_dir),
patch("python_pkg.steam_backlog_enforcer.config.CONFIG_FILE", config_file), patch("steam_backlog_enforcer.config.CONFIG_FILE", config_file),
): ):
cfg.save() cfg.save()
data = json.loads(config_file.read_text(encoding="utf-8")) data = json.loads(config_file.read_text(encoding="utf-8"))
@ -95,14 +95,14 @@ class TestConfig:
json.dumps({"steam_api_key": "key1", "steam_id": "id1"}) + "\n", json.dumps({"steam_api_key": "key1", "steam_id": "id1"}) + "\n",
encoding="utf-8", encoding="utf-8",
) )
with patch("python_pkg.steam_backlog_enforcer.config.CONFIG_FILE", config_file): with patch("steam_backlog_enforcer.config.CONFIG_FILE", config_file):
cfg = Config.load() cfg = Config.load()
assert cfg.steam_api_key == "key1" assert cfg.steam_api_key == "key1"
assert cfg.steam_id == "id1" assert cfg.steam_id == "id1"
def test_load_missing(self, tmp_path: Path) -> None: def test_load_missing(self, tmp_path: Path) -> None:
config_file = tmp_path / "nonexistent.json" config_file = tmp_path / "nonexistent.json"
with patch("python_pkg.steam_backlog_enforcer.config.CONFIG_FILE", config_file): with patch("steam_backlog_enforcer.config.CONFIG_FILE", config_file):
cfg = Config.load() cfg = Config.load()
assert cfg.steam_api_key == "" assert cfg.steam_api_key == ""
@ -112,7 +112,7 @@ class TestConfig:
json.dumps({"steam_api_key": "k", "unknown_field": 42}) + "\n", json.dumps({"steam_api_key": "k", "unknown_field": 42}) + "\n",
encoding="utf-8", encoding="utf-8",
) )
with patch("python_pkg.steam_backlog_enforcer.config.CONFIG_FILE", config_file): with patch("steam_backlog_enforcer.config.CONFIG_FILE", config_file):
cfg = Config.load() cfg = Config.load()
assert cfg.steam_api_key == "k" assert cfg.steam_api_key == "k"
@ -131,8 +131,8 @@ class TestState:
config_dir = tmp_path / "cfg" config_dir = tmp_path / "cfg"
state_file = config_dir / "state.json" state_file = config_dir / "state.json"
with ( with (
patch("python_pkg.steam_backlog_enforcer.config.CONFIG_DIR", config_dir), patch("steam_backlog_enforcer.config.CONFIG_DIR", config_dir),
patch("python_pkg.steam_backlog_enforcer.config.STATE_FILE", state_file), patch("steam_backlog_enforcer.config.STATE_FILE", state_file),
): ):
state.save() state.save()
data = json.loads(state_file.read_text(encoding="utf-8")) data = json.loads(state_file.read_text(encoding="utf-8"))
@ -152,21 +152,21 @@ class TestState:
+ "\n", + "\n",
encoding="utf-8", encoding="utf-8",
) )
with patch("python_pkg.steam_backlog_enforcer.config.STATE_FILE", state_file): with patch("steam_backlog_enforcer.config.STATE_FILE", state_file):
st = State.load() st = State.load()
assert st.current_app_id == 50 assert st.current_app_id == 50
assert st.finished_app_ids == [1, 2] assert st.finished_app_ids == [1, 2]
def test_load_missing(self, tmp_path: Path) -> None: def test_load_missing(self, tmp_path: Path) -> None:
state_file = tmp_path / "nonexistent.json" state_file = tmp_path / "nonexistent.json"
with patch("python_pkg.steam_backlog_enforcer.config.STATE_FILE", state_file): with patch("steam_backlog_enforcer.config.STATE_FILE", state_file):
st = State.load() st = State.load()
assert st.current_app_id is None assert st.current_app_id is None
def test_load_corrupt(self, tmp_path: Path) -> None: def test_load_corrupt(self, tmp_path: Path) -> None:
state_file = tmp_path / "state.json" state_file = tmp_path / "state.json"
state_file.write_text("not valid json{{", encoding="utf-8") state_file.write_text("not valid json{{", encoding="utf-8")
with patch("python_pkg.steam_backlog_enforcer.config.STATE_FILE", state_file): with patch("steam_backlog_enforcer.config.STATE_FILE", state_file):
st = State.load() st = State.load()
assert st.current_app_id is None assert st.current_app_id is None
assert st.current_game_name == "" assert st.current_game_name == ""
@ -215,8 +215,8 @@ class TestSnapshot:
config_dir = tmp_path / "cfg" config_dir = tmp_path / "cfg"
snap_file = config_dir / "snapshot.json" snap_file = config_dir / "snapshot.json"
with ( with (
patch("python_pkg.steam_backlog_enforcer.config.CONFIG_DIR", config_dir), patch("steam_backlog_enforcer.config.CONFIG_DIR", config_dir),
patch("python_pkg.steam_backlog_enforcer.config.SNAPSHOT_FILE", snap_file), patch("steam_backlog_enforcer.config.SNAPSHOT_FILE", snap_file),
): ):
data: list[dict[str, Any]] = [{"app_id": 1, "name": "G1"}] data: list[dict[str, Any]] = [{"app_id": 1, "name": "G1"}]
save_snapshot(data) save_snapshot(data)
@ -225,7 +225,7 @@ class TestSnapshot:
def test_load_none(self, tmp_path: Path) -> None: def test_load_none(self, tmp_path: Path) -> None:
snap_file = tmp_path / "nonexistent.json" snap_file = tmp_path / "nonexistent.json"
with patch("python_pkg.steam_backlog_enforcer.config.SNAPSHOT_FILE", snap_file): with patch("steam_backlog_enforcer.config.SNAPSHOT_FILE", snap_file):
assert load_snapshot() is None assert load_snapshot() is None
@ -236,8 +236,8 @@ class TestInteractiveSetup:
config_dir = tmp_path / "cfg" config_dir = tmp_path / "cfg"
config_file = config_dir / "config.json" config_file = config_dir / "config.json"
with ( with (
patch("python_pkg.steam_backlog_enforcer.config.CONFIG_DIR", config_dir), patch("steam_backlog_enforcer.config.CONFIG_DIR", config_dir),
patch("python_pkg.steam_backlog_enforcer.config.CONFIG_FILE", config_file), patch("steam_backlog_enforcer.config.CONFIG_FILE", config_file),
patch("builtins.input", side_effect=["mykey", "myid"]), patch("builtins.input", side_effect=["mykey", "myid"]),
): ):
cfg = interactive_setup() cfg = interactive_setup()
@ -256,8 +256,8 @@ class TestInteractiveSetup:
config_dir = tmp_path / "cfg" config_dir = tmp_path / "cfg"
config_file = config_dir / "config.json" config_file = config_dir / "config.json"
with ( with (
patch("python_pkg.steam_backlog_enforcer.config.CONFIG_DIR", config_dir), patch("steam_backlog_enforcer.config.CONFIG_DIR", config_dir),
patch("python_pkg.steam_backlog_enforcer.config.CONFIG_FILE", config_file), patch("steam_backlog_enforcer.config.CONFIG_FILE", config_file),
patch("builtins.input", side_effect=["key", ""]), patch("builtins.input", side_effect=["key", ""]),
pytest.raises(SystemExit), pytest.raises(SystemExit),
): ):

View File

@ -6,7 +6,7 @@ import json
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
from unittest.mock import MagicMock, patch from unittest.mock import MagicMock, patch
from python_pkg.steam_backlog_enforcer._enforce_loop import ( from steam_backlog_enforcer._enforce_loop import (
_enforce_auto_install, _enforce_auto_install,
_enforce_hide_games, _enforce_hide_games,
_enforce_setup, _enforce_setup,
@ -15,12 +15,12 @@ from python_pkg.steam_backlog_enforcer._enforce_loop import (
_save_owned_app_ids_cache, _save_owned_app_ids_cache,
get_all_owned_app_ids, get_all_owned_app_ids,
) )
from python_pkg.steam_backlog_enforcer.config import Config, State from steam_backlog_enforcer.config import Config, State
if TYPE_CHECKING: if TYPE_CHECKING:
from pathlib import Path from pathlib import Path
PKG = "python_pkg.steam_backlog_enforcer._enforce_loop" PKG = "steam_backlog_enforcer._enforce_loop"
class TestGetAllOwnedAppIds: class TestGetAllOwnedAppIds:

View File

@ -4,13 +4,13 @@ from __future__ import annotations
from unittest.mock import patch from unittest.mock import patch
from python_pkg.steam_backlog_enforcer._enforce_loop import ( from steam_backlog_enforcer._enforce_loop import (
_enforce_loop_iteration, _enforce_loop_iteration,
do_enforce, do_enforce,
) )
from python_pkg.steam_backlog_enforcer.config import Config, State from steam_backlog_enforcer.config import Config, State
PKG = "python_pkg.steam_backlog_enforcer._enforce_loop" PKG = "steam_backlog_enforcer._enforce_loop"
class TestEnforceLoopIteration: class TestEnforceLoopIteration:

View File

@ -5,7 +5,7 @@ from __future__ import annotations
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
from unittest.mock import patch from unittest.mock import patch
from python_pkg.steam_backlog_enforcer.enforcer import ( from steam_backlog_enforcer.enforcer import (
enforce_allowed_game, enforce_allowed_game,
get_running_steam_game_pids, get_running_steam_game_pids,
kill_process, kill_process,
@ -27,7 +27,7 @@ class TestGetRunningPids:
(pid_dir / "environ").write_bytes(environ) (pid_dir / "environ").write_bytes(environ)
with patch( with patch(
"python_pkg.steam_backlog_enforcer.enforcer.Path", "steam_backlog_enforcer.enforcer.Path",
return_value=proc_dir, return_value=proc_dir,
): ):
result = get_running_steam_game_pids() result = get_running_steam_game_pids()
@ -40,7 +40,7 @@ class TestGetRunningPids:
(proc_dir / "cpuinfo").touch() (proc_dir / "cpuinfo").touch()
with patch( with patch(
"python_pkg.steam_backlog_enforcer.enforcer.Path", "steam_backlog_enforcer.enforcer.Path",
return_value=proc_dir, return_value=proc_dir,
): ):
result = get_running_steam_game_pids() result = get_running_steam_game_pids()
@ -53,7 +53,7 @@ class TestGetRunningPids:
# No environ file -> OSError when reading # No environ file -> OSError when reading
with patch( with patch(
"python_pkg.steam_backlog_enforcer.enforcer.Path", "steam_backlog_enforcer.enforcer.Path",
return_value=proc_dir, return_value=proc_dir,
): ):
result = get_running_steam_game_pids() result = get_running_steam_game_pids()
@ -67,7 +67,7 @@ class TestGetRunningPids:
(pid_dir / "environ").write_bytes(environ) (pid_dir / "environ").write_bytes(environ)
with patch( with patch(
"python_pkg.steam_backlog_enforcer.enforcer.Path", "steam_backlog_enforcer.enforcer.Path",
return_value=proc_dir, return_value=proc_dir,
): ):
result = get_running_steam_game_pids() result = get_running_steam_game_pids()
@ -81,7 +81,7 @@ class TestGetRunningPids:
(pid_dir / "environ").write_bytes(environ) (pid_dir / "environ").write_bytes(environ)
with patch( with patch(
"python_pkg.steam_backlog_enforcer.enforcer.Path", "steam_backlog_enforcer.enforcer.Path",
return_value=proc_dir, return_value=proc_dir,
): ):
result = get_running_steam_game_pids() result = get_running_steam_game_pids()
@ -93,7 +93,7 @@ class TestEnforceAllowedGame:
def test_no_violations(self) -> None: def test_no_violations(self) -> None:
with patch( with patch(
"python_pkg.steam_backlog_enforcer.enforcer.get_running_steam_game_pids", "steam_backlog_enforcer.enforcer.get_running_steam_game_pids",
return_value={100: 440}, return_value={100: 440},
): ):
result = enforce_allowed_game(440) result = enforce_allowed_game(440)
@ -102,11 +102,11 @@ class TestEnforceAllowedGame:
def test_kills_unauthorized(self) -> None: def test_kills_unauthorized(self) -> None:
with ( with (
patch( patch(
"python_pkg.steam_backlog_enforcer.enforcer.get_running_steam_game_pids", "steam_backlog_enforcer.enforcer.get_running_steam_game_pids",
return_value={100: 570, 200: 440}, return_value={100: 570, 200: 440},
), ),
patch( patch(
"python_pkg.steam_backlog_enforcer.enforcer.kill_process" "steam_backlog_enforcer.enforcer.kill_process"
) as mock_kill, ) as mock_kill,
): ):
result = enforce_allowed_game(440, kill_unauthorized=True) result = enforce_allowed_game(440, kill_unauthorized=True)
@ -115,7 +115,7 @@ class TestEnforceAllowedGame:
def test_skips_app_id_zero(self) -> None: def test_skips_app_id_zero(self) -> None:
with patch( with patch(
"python_pkg.steam_backlog_enforcer.enforcer.get_running_steam_game_pids", "steam_backlog_enforcer.enforcer.get_running_steam_game_pids",
return_value={100: 0}, return_value={100: 0},
): ):
result = enforce_allowed_game(440) result = enforce_allowed_game(440)
@ -123,7 +123,7 @@ class TestEnforceAllowedGame:
def test_detects_without_killing(self) -> None: def test_detects_without_killing(self) -> None:
with patch( with patch(
"python_pkg.steam_backlog_enforcer.enforcer.get_running_steam_game_pids", "steam_backlog_enforcer.enforcer.get_running_steam_game_pids",
return_value={100: 570}, return_value={100: 570},
): ):
result = enforce_allowed_game(440, kill_unauthorized=False) result = enforce_allowed_game(440, kill_unauthorized=False)
@ -137,15 +137,15 @@ class TestEnforceAllowedGame:
"""Protected IDs must never be killed even if not the assigned game.""" """Protected IDs must never be killed even if not the assigned game."""
with ( with (
patch( patch(
"python_pkg.steam_backlog_enforcer.enforcer.get_running_steam_game_pids", "steam_backlog_enforcer.enforcer.get_running_steam_game_pids",
return_value={100: 1331550, 200: 440}, return_value={100: 1331550, 200: 440},
), ),
patch( patch(
"python_pkg.steam_backlog_enforcer.enforcer.is_protected_app", "steam_backlog_enforcer.enforcer.is_protected_app",
side_effect=lambda aid: aid == 1331550, side_effect=lambda aid: aid == 1331550,
), ),
patch( patch(
"python_pkg.steam_backlog_enforcer.enforcer.kill_process" "steam_backlog_enforcer.enforcer.kill_process"
) as mock_kill, ) as mock_kill,
): ):
result = enforce_allowed_game(440, kill_unauthorized=True) result = enforce_allowed_game(440, kill_unauthorized=True)
@ -157,20 +157,20 @@ class TestKillProcess:
"""Tests for kill_process.""" """Tests for kill_process."""
def test_kill_success(self) -> None: def test_kill_success(self) -> None:
with patch("python_pkg.steam_backlog_enforcer.enforcer.os.kill") as mock_kill: with patch("steam_backlog_enforcer.enforcer.os.kill") as mock_kill:
kill_process(123, 440) kill_process(123, 440)
mock_kill.assert_called_once() mock_kill.assert_called_once()
def test_process_already_gone(self) -> None: def test_process_already_gone(self) -> None:
with patch( with patch(
"python_pkg.steam_backlog_enforcer.enforcer.os.kill", "steam_backlog_enforcer.enforcer.os.kill",
side_effect=ProcessLookupError, side_effect=ProcessLookupError,
): ):
kill_process(123, 440) # Should not raise kill_process(123, 440) # Should not raise
def test_permission_error(self) -> None: def test_permission_error(self) -> None:
with patch( with patch(
"python_pkg.steam_backlog_enforcer.enforcer.os.kill", "steam_backlog_enforcer.enforcer.os.kill",
side_effect=PermissionError, side_effect=PermissionError,
): ):
kill_process(123, 440) # Should not raise kill_process(123, 440) # Should not raise
@ -181,21 +181,21 @@ class TestSendNotification:
def test_sends(self) -> None: def test_sends(self) -> None:
with patch( with patch(
"python_pkg.steam_backlog_enforcer.enforcer.subprocess.run" "steam_backlog_enforcer.enforcer.subprocess.run"
) as mock_run: ) as mock_run:
send_notification("Title", "Body") send_notification("Title", "Body")
mock_run.assert_called_once() mock_run.assert_called_once()
def test_handles_missing_notify_send(self) -> None: def test_handles_missing_notify_send(self) -> None:
with patch( with patch(
"python_pkg.steam_backlog_enforcer.enforcer.subprocess.run", "steam_backlog_enforcer.enforcer.subprocess.run",
side_effect=FileNotFoundError, side_effect=FileNotFoundError,
): ):
send_notification("Title", "Body") # Should not raise send_notification("Title", "Body") # Should not raise
def test_handles_os_error(self) -> None: def test_handles_os_error(self) -> None:
with patch( with patch(
"python_pkg.steam_backlog_enforcer.enforcer.subprocess.run", "steam_backlog_enforcer.enforcer.subprocess.run",
side_effect=OSError, side_effect=OSError,
): ):
send_notification("Title", "Body") # Should not raise send_notification("Title", "Body") # Should not raise

View File

@ -8,7 +8,7 @@ from unittest.mock import MagicMock, patch
import pytest import pytest
from python_pkg.steam_backlog_enforcer.game_install import ( from steam_backlog_enforcer.game_install import (
_assert_not_real_steam, _assert_not_real_steam,
_echo, _echo,
_ensure_steam_running, _ensure_steam_running,
@ -22,7 +22,7 @@ if TYPE_CHECKING:
from pathlib import Path from pathlib import Path
PKG = "python_pkg.steam_backlog_enforcer.game_install" PKG = "steam_backlog_enforcer.game_install"
class TestAssertNotRealSteam: class TestAssertNotRealSteam:
@ -99,7 +99,7 @@ class TestTriggerSteamInstall:
def test_success(self) -> None: def test_success(self) -> None:
with patch( with patch(
"python_pkg.steam_backlog_enforcer.game_install.subprocess.run" "steam_backlog_enforcer.game_install.subprocess.run"
) as mock_run: ) as mock_run:
result = _trigger_steam_install(440, "TF2") result = _trigger_steam_install(440, "TF2")
assert result is True assert result is True
@ -107,7 +107,7 @@ class TestTriggerSteamInstall:
def test_file_not_found(self) -> None: def test_file_not_found(self) -> None:
with patch( with patch(
"python_pkg.steam_backlog_enforcer.game_install.subprocess.run", "steam_backlog_enforcer.game_install.subprocess.run",
side_effect=FileNotFoundError, side_effect=FileNotFoundError,
): ):
result = _trigger_steam_install(440, "TF2") result = _trigger_steam_install(440, "TF2")
@ -115,7 +115,7 @@ class TestTriggerSteamInstall:
def test_os_error(self) -> None: def test_os_error(self) -> None:
with patch( with patch(
"python_pkg.steam_backlog_enforcer.game_install.subprocess.run", "steam_backlog_enforcer.game_install.subprocess.run",
side_effect=OSError, side_effect=OSError,
): ):
result = _trigger_steam_install(440, "TF2") result = _trigger_steam_install(440, "TF2")
@ -125,7 +125,7 @@ class TestTriggerSteamInstall:
import subprocess import subprocess
with patch( with patch(
"python_pkg.steam_backlog_enforcer.game_install.subprocess.run", "steam_backlog_enforcer.game_install.subprocess.run",
side_effect=subprocess.TimeoutExpired("cmd", 15), side_effect=subprocess.TimeoutExpired("cmd", 15),
): ):
result = _trigger_steam_install(440, "TF2") result = _trigger_steam_install(440, "TF2")
@ -155,14 +155,14 @@ class TestGetUidGid:
mock_pw.pw_uid = 1001 mock_pw.pw_uid = 1001
mock_pw.pw_gid = 1001 mock_pw.pw_gid = 1001
with patch( with patch(
"python_pkg.steam_backlog_enforcer.game_install.pwd.getpwnam", "steam_backlog_enforcer.game_install.pwd.getpwnam",
return_value=mock_pw, return_value=mock_pw,
): ):
assert _get_uid_gid_for_user("alice") == (1001, 1001) assert _get_uid_gid_for_user("alice") == (1001, 1001)
def test_unknown_user(self) -> None: def test_unknown_user(self) -> None:
with patch( with patch(
"python_pkg.steam_backlog_enforcer.game_install.pwd.getpwnam", "steam_backlog_enforcer.game_install.pwd.getpwnam",
side_effect=KeyError, side_effect=KeyError,
): ):
assert _get_uid_gid_for_user("nobody") == (1000, 1000) assert _get_uid_gid_for_user("nobody") == (1000, 1000)
@ -175,13 +175,13 @@ class TestIsGameInstalled:
manifest = tmp_path / "appmanifest_440.acf" manifest = tmp_path / "appmanifest_440.acf"
manifest.touch() manifest.touch()
with patch( with patch(
"python_pkg.steam_backlog_enforcer.game_install.STEAMAPPS_PATH", tmp_path "steam_backlog_enforcer.game_install.STEAMAPPS_PATH", tmp_path
): ):
assert is_game_installed(440) is True assert is_game_installed(440) is True
def test_not_installed(self, tmp_path: Path) -> None: def test_not_installed(self, tmp_path: Path) -> None:
with patch( with patch(
"python_pkg.steam_backlog_enforcer.game_install.STEAMAPPS_PATH", tmp_path "steam_backlog_enforcer.game_install.STEAMAPPS_PATH", tmp_path
): ):
assert is_game_installed(440) is False assert is_game_installed(440) is False
@ -192,7 +192,7 @@ class TestEnsureSteamRunning:
def test_already_running(self) -> None: def test_already_running(self) -> None:
mock_result = MagicMock(returncode=0) mock_result = MagicMock(returncode=0)
with patch( with patch(
"python_pkg.steam_backlog_enforcer.game_install.subprocess.run", "steam_backlog_enforcer.game_install.subprocess.run",
return_value=mock_result, return_value=mock_result,
): ):
_ensure_steam_running() _ensure_steam_running()
@ -201,17 +201,17 @@ class TestEnsureSteamRunning:
mock_result = MagicMock(returncode=1) mock_result = MagicMock(returncode=1)
with ( with (
patch( patch(
"python_pkg.steam_backlog_enforcer.game_install.subprocess.run", "steam_backlog_enforcer.game_install.subprocess.run",
return_value=mock_result, return_value=mock_result,
), ),
patch( patch(
"python_pkg.steam_backlog_enforcer.game_install.subprocess.Popen" "steam_backlog_enforcer.game_install.subprocess.Popen"
) as mock_popen, ) as mock_popen,
patch( patch(
"python_pkg.steam_backlog_enforcer.game_install.os.geteuid", "steam_backlog_enforcer.game_install.os.geteuid",
return_value=1000, return_value=1000,
), ),
patch("python_pkg.steam_backlog_enforcer.game_install.time.sleep"), patch("steam_backlog_enforcer.game_install.time.sleep"),
): ):
_ensure_steam_running() _ensure_steam_running()
mock_popen.assert_called_once() mock_popen.assert_called_once()
@ -223,25 +223,25 @@ class TestEnsureSteamRunning:
mock_pw.pw_gid = 1000 mock_pw.pw_gid = 1000
with ( with (
patch( patch(
"python_pkg.steam_backlog_enforcer.game_install.subprocess.run", "steam_backlog_enforcer.game_install.subprocess.run",
return_value=mock_result, return_value=mock_result,
), ),
patch( patch(
"python_pkg.steam_backlog_enforcer.game_install.subprocess.Popen" "steam_backlog_enforcer.game_install.subprocess.Popen"
) as mock_popen, ) as mock_popen,
patch( patch(
"python_pkg.steam_backlog_enforcer.game_install.os.geteuid", "steam_backlog_enforcer.game_install.os.geteuid",
return_value=0, return_value=0,
), ),
patch( patch(
"python_pkg.steam_backlog_enforcer.game_install._get_real_user", "steam_backlog_enforcer.game_install._get_real_user",
return_value="alice", return_value="alice",
), ),
patch( patch(
"python_pkg.steam_backlog_enforcer.game_install._get_uid_gid_for_user", "steam_backlog_enforcer.game_install._get_uid_gid_for_user",
return_value=(1000, 1000), return_value=(1000, 1000),
), ),
patch("python_pkg.steam_backlog_enforcer.game_install.time.sleep"), patch("steam_backlog_enforcer.game_install.time.sleep"),
): ):
_ensure_steam_running() _ensure_steam_running()
mock_popen.assert_called_once() mock_popen.assert_called_once()
@ -249,15 +249,15 @@ class TestEnsureSteamRunning:
def test_pgrep_not_found(self) -> None: def test_pgrep_not_found(self) -> None:
with ( with (
patch( patch(
"python_pkg.steam_backlog_enforcer.game_install.subprocess.run", "steam_backlog_enforcer.game_install.subprocess.run",
side_effect=FileNotFoundError, side_effect=FileNotFoundError,
), ),
patch("python_pkg.steam_backlog_enforcer.game_install.subprocess.Popen"), patch("steam_backlog_enforcer.game_install.subprocess.Popen"),
patch( patch(
"python_pkg.steam_backlog_enforcer.game_install.os.geteuid", "steam_backlog_enforcer.game_install.os.geteuid",
return_value=1000, return_value=1000,
), ),
patch("python_pkg.steam_backlog_enforcer.game_install.time.sleep"), patch("steam_backlog_enforcer.game_install.time.sleep"),
): ):
_ensure_steam_running() _ensure_steam_running()
@ -265,15 +265,15 @@ class TestEnsureSteamRunning:
mock_result = MagicMock(returncode=1) mock_result = MagicMock(returncode=1)
with ( with (
patch( patch(
"python_pkg.steam_backlog_enforcer.game_install.subprocess.run", "steam_backlog_enforcer.game_install.subprocess.run",
return_value=mock_result, return_value=mock_result,
), ),
patch( patch(
"python_pkg.steam_backlog_enforcer.game_install.subprocess.Popen", "steam_backlog_enforcer.game_install.subprocess.Popen",
side_effect=FileNotFoundError, side_effect=FileNotFoundError,
), ),
patch( patch(
"python_pkg.steam_backlog_enforcer.game_install.os.geteuid", "steam_backlog_enforcer.game_install.os.geteuid",
return_value=1000, return_value=1000,
), ),
): ):

View File

@ -5,7 +5,7 @@ from __future__ import annotations
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
from unittest.mock import MagicMock, patch from unittest.mock import MagicMock, patch
from python_pkg.steam_backlog_enforcer.game_install import ( from steam_backlog_enforcer.game_install import (
_remove_game_dirs, _remove_game_dirs,
uninstall_game, uninstall_game,
uninstall_other_games, uninstall_other_games,
@ -14,7 +14,7 @@ from python_pkg.steam_backlog_enforcer.game_install import (
if TYPE_CHECKING: if TYPE_CHECKING:
from pathlib import Path from pathlib import Path
PKG = "python_pkg.steam_backlog_enforcer.game_install" PKG = "steam_backlog_enforcer.game_install"
class TestRemoveGameDirs: class TestRemoveGameDirs:

View File

@ -5,7 +5,7 @@ from __future__ import annotations
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
from unittest.mock import MagicMock, patch from unittest.mock import MagicMock, patch
from python_pkg.steam_backlog_enforcer.game_install import ( from steam_backlog_enforcer.game_install import (
_read_install_dir, _read_install_dir,
_remove_manifest, _remove_manifest,
get_installed_games, get_installed_games,
@ -16,7 +16,7 @@ if TYPE_CHECKING:
from pathlib import Path from pathlib import Path
PKG = "python_pkg.steam_backlog_enforcer.game_install" PKG = "steam_backlog_enforcer.game_install"
class TestInstallGame: class TestInstallGame:
@ -26,21 +26,21 @@ class TestInstallGame:
manifest = tmp_path / "appmanifest_440.acf" manifest = tmp_path / "appmanifest_440.acf"
manifest.touch() manifest.touch()
with patch( with patch(
"python_pkg.steam_backlog_enforcer.game_install.STEAMAPPS_PATH", tmp_path "steam_backlog_enforcer.game_install.STEAMAPPS_PATH", tmp_path
): ):
assert install_game(440, "TF2", "steam123") is True assert install_game(440, "TF2", "steam123") is True
def test_use_steam_protocol_success(self, tmp_path: Path) -> None: def test_use_steam_protocol_success(self, tmp_path: Path) -> None:
with ( with (
patch( patch(
"python_pkg.steam_backlog_enforcer.game_install.STEAMAPPS_PATH", "steam_backlog_enforcer.game_install.STEAMAPPS_PATH",
tmp_path, tmp_path,
), ),
patch( patch(
"python_pkg.steam_backlog_enforcer.game_install._ensure_steam_running" "steam_backlog_enforcer.game_install._ensure_steam_running"
), ),
patch( patch(
"python_pkg.steam_backlog_enforcer.game_install._trigger_steam_install", "steam_backlog_enforcer.game_install._trigger_steam_install",
return_value=True, return_value=True,
), ),
): ):
@ -49,18 +49,18 @@ class TestInstallGame:
def test_use_steam_protocol_fallback(self, tmp_path: Path) -> None: def test_use_steam_protocol_fallback(self, tmp_path: Path) -> None:
with ( with (
patch( patch(
"python_pkg.steam_backlog_enforcer.game_install.STEAMAPPS_PATH", "steam_backlog_enforcer.game_install.STEAMAPPS_PATH",
tmp_path, tmp_path,
), ),
patch( patch(
"python_pkg.steam_backlog_enforcer.game_install._ensure_steam_running" "steam_backlog_enforcer.game_install._ensure_steam_running"
), ),
patch( patch(
"python_pkg.steam_backlog_enforcer.game_install._trigger_steam_install", "steam_backlog_enforcer.game_install._trigger_steam_install",
return_value=False, return_value=False,
), ),
patch( patch(
"python_pkg.steam_backlog_enforcer.game_install.os.geteuid", "steam_backlog_enforcer.game_install.os.geteuid",
return_value=1000, return_value=1000,
), ),
): ):
@ -70,26 +70,26 @@ class TestInstallGame:
def test_manifest_write_as_root(self, tmp_path: Path) -> None: def test_manifest_write_as_root(self, tmp_path: Path) -> None:
with ( with (
patch( patch(
"python_pkg.steam_backlog_enforcer.game_install.STEAMAPPS_PATH", "steam_backlog_enforcer.game_install.STEAMAPPS_PATH",
tmp_path, tmp_path,
), ),
patch( patch(
"python_pkg.steam_backlog_enforcer.game_install._ensure_steam_running" "steam_backlog_enforcer.game_install._ensure_steam_running"
), ),
patch( patch(
"python_pkg.steam_backlog_enforcer.game_install.os.geteuid", "steam_backlog_enforcer.game_install.os.geteuid",
return_value=0, return_value=0,
), ),
patch( patch(
"python_pkg.steam_backlog_enforcer.game_install._get_real_user", "steam_backlog_enforcer.game_install._get_real_user",
return_value="alice", return_value="alice",
), ),
patch( patch(
"python_pkg.steam_backlog_enforcer.game_install._get_uid_gid_for_user", "steam_backlog_enforcer.game_install._get_uid_gid_for_user",
return_value=(1001, 1001), return_value=(1001, 1001),
), ),
patch( patch(
"python_pkg.steam_backlog_enforcer.game_install.os.chown" "steam_backlog_enforcer.game_install.os.chown"
) as mock_chown, ) as mock_chown,
): ):
assert install_game(440, "TF2", "s1") is True assert install_game(440, "TF2", "s1") is True
@ -99,14 +99,14 @@ class TestInstallGame:
# Make steamapps path not writable # Make steamapps path not writable
with ( with (
patch( patch(
"python_pkg.steam_backlog_enforcer.game_install.STEAMAPPS_PATH", "steam_backlog_enforcer.game_install.STEAMAPPS_PATH",
tmp_path / "nonexistent" / "deep", tmp_path / "nonexistent" / "deep",
), ),
patch( patch(
"python_pkg.steam_backlog_enforcer.game_install._ensure_steam_running" "steam_backlog_enforcer.game_install._ensure_steam_running"
), ),
patch( patch(
"python_pkg.steam_backlog_enforcer.game_install.os.geteuid", "steam_backlog_enforcer.game_install.os.geteuid",
return_value=1000, return_value=1000,
), ),
): ):
@ -115,14 +115,14 @@ class TestInstallGame:
def test_empty_game_name(self, tmp_path: Path) -> None: def test_empty_game_name(self, tmp_path: Path) -> None:
with ( with (
patch( patch(
"python_pkg.steam_backlog_enforcer.game_install.STEAMAPPS_PATH", "steam_backlog_enforcer.game_install.STEAMAPPS_PATH",
tmp_path, tmp_path,
), ),
patch( patch(
"python_pkg.steam_backlog_enforcer.game_install._ensure_steam_running" "steam_backlog_enforcer.game_install._ensure_steam_running"
), ),
patch( patch(
"python_pkg.steam_backlog_enforcer.game_install.os.geteuid", "steam_backlog_enforcer.game_install.os.geteuid",
return_value=1000, return_value=1000,
), ),
): ):
@ -131,18 +131,18 @@ class TestInstallGame:
def test_manifest_not_root_no_chown(self, tmp_path: Path) -> None: def test_manifest_not_root_no_chown(self, tmp_path: Path) -> None:
with ( with (
patch( patch(
"python_pkg.steam_backlog_enforcer.game_install.STEAMAPPS_PATH", "steam_backlog_enforcer.game_install.STEAMAPPS_PATH",
tmp_path, tmp_path,
), ),
patch( patch(
"python_pkg.steam_backlog_enforcer.game_install._ensure_steam_running" "steam_backlog_enforcer.game_install._ensure_steam_running"
), ),
patch( patch(
"python_pkg.steam_backlog_enforcer.game_install.os.geteuid", "steam_backlog_enforcer.game_install.os.geteuid",
return_value=1000, return_value=1000,
), ),
patch( patch(
"python_pkg.steam_backlog_enforcer.game_install.os.chown" "steam_backlog_enforcer.game_install.os.chown"
) as mock_chown, ) as mock_chown,
): ):
assert install_game(440, "TF2", "s1") is True assert install_game(440, "TF2", "s1") is True
@ -152,22 +152,22 @@ class TestInstallGame:
"""When real user IS root, don't chown.""" """When real user IS root, don't chown."""
with ( with (
patch( patch(
"python_pkg.steam_backlog_enforcer.game_install.STEAMAPPS_PATH", "steam_backlog_enforcer.game_install.STEAMAPPS_PATH",
tmp_path, tmp_path,
), ),
patch( patch(
"python_pkg.steam_backlog_enforcer.game_install._ensure_steam_running" "steam_backlog_enforcer.game_install._ensure_steam_running"
), ),
patch( patch(
"python_pkg.steam_backlog_enforcer.game_install.os.geteuid", "steam_backlog_enforcer.game_install.os.geteuid",
return_value=0, return_value=0,
), ),
patch( patch(
"python_pkg.steam_backlog_enforcer.game_install._get_real_user", "steam_backlog_enforcer.game_install._get_real_user",
return_value="root", return_value="root",
), ),
patch( patch(
"python_pkg.steam_backlog_enforcer.game_install.os.chown" "steam_backlog_enforcer.game_install.os.chown"
) as mock_chown, ) as mock_chown,
): ):
assert install_game(440, "TF2", "s1") is True assert install_game(440, "TF2", "s1") is True
@ -181,7 +181,7 @@ class TestGetInstalledGames:
manifest = tmp_path / "appmanifest_440.acf" manifest = tmp_path / "appmanifest_440.acf"
manifest.write_text('"appid"\t\t"440"\n"name"\t\t"Team Fortress 2"\n') manifest.write_text('"appid"\t\t"440"\n"name"\t\t"Team Fortress 2"\n')
with patch( with patch(
"python_pkg.steam_backlog_enforcer.game_install.STEAMAPPS_PATH", tmp_path "steam_backlog_enforcer.game_install.STEAMAPPS_PATH", tmp_path
): ):
result = get_installed_games() result = get_installed_games()
assert result == [(440, "Team Fortress 2")] assert result == [(440, "Team Fortress 2")]
@ -190,14 +190,14 @@ class TestGetInstalledGames:
manifest = tmp_path / "appmanifest_440.acf" manifest = tmp_path / "appmanifest_440.acf"
manifest.write_text('"appid"\t\t"440"\n') manifest.write_text('"appid"\t\t"440"\n')
with patch( with patch(
"python_pkg.steam_backlog_enforcer.game_install.STEAMAPPS_PATH", tmp_path "steam_backlog_enforcer.game_install.STEAMAPPS_PATH", tmp_path
): ):
result = get_installed_games() result = get_installed_games()
assert result == [(440, "Unknown (440)")] assert result == [(440, "Unknown (440)")]
def test_empty_dir(self, tmp_path: Path) -> None: def test_empty_dir(self, tmp_path: Path) -> None:
with patch( with patch(
"python_pkg.steam_backlog_enforcer.game_install.STEAMAPPS_PATH", tmp_path "steam_backlog_enforcer.game_install.STEAMAPPS_PATH", tmp_path
): ):
result = get_installed_games() result = get_installed_games()
assert result == [] assert result == []
@ -206,7 +206,7 @@ class TestGetInstalledGames:
manifest = tmp_path / "appmanifest_440.acf" manifest = tmp_path / "appmanifest_440.acf"
manifest.write_text('"name"\t\t"NoAppId"\n') manifest.write_text('"name"\t\t"NoAppId"\n')
with patch( with patch(
"python_pkg.steam_backlog_enforcer.game_install.STEAMAPPS_PATH", tmp_path "steam_backlog_enforcer.game_install.STEAMAPPS_PATH", tmp_path
): ):
result = get_installed_games() result = get_installed_games()
assert result == [] assert result == []
@ -219,7 +219,7 @@ class TestReadInstallDir:
manifest = tmp_path / "appmanifest_440.acf" manifest = tmp_path / "appmanifest_440.acf"
manifest.write_text('"installdir"\t\t"Team Fortress 2"\n') manifest.write_text('"installdir"\t\t"Team Fortress 2"\n')
with patch( with patch(
"python_pkg.steam_backlog_enforcer.game_install.STEAMAPPS_PATH", tmp_path "steam_backlog_enforcer.game_install.STEAMAPPS_PATH", tmp_path
): ):
result = _read_install_dir(manifest) result = _read_install_dir(manifest)
assert result == tmp_path / "common" / "Team Fortress 2" assert result == tmp_path / "common" / "Team Fortress 2"
@ -228,7 +228,7 @@ class TestReadInstallDir:
manifest = tmp_path / "appmanifest_440.acf" manifest = tmp_path / "appmanifest_440.acf"
manifest.write_text('"appid"\t\t"440"\n') manifest.write_text('"appid"\t\t"440"\n')
with patch( with patch(
"python_pkg.steam_backlog_enforcer.game_install.STEAMAPPS_PATH", tmp_path "steam_backlog_enforcer.game_install.STEAMAPPS_PATH", tmp_path
): ):
assert _read_install_dir(manifest) is None assert _read_install_dir(manifest) is None

View File

@ -9,14 +9,14 @@ from unittest.mock import AsyncMock, MagicMock, patch
import aiohttp import aiohttp
from python_pkg.steam_backlog_enforcer._hltb_search import ( from steam_backlog_enforcer._hltb_search import (
_AuthInfo, _AuthInfo,
_build_search_payload, _build_search_payload,
_get_hltb_search_url, _get_hltb_search_url,
_pick_best_hltb_entry, _pick_best_hltb_entry,
_similarity, _similarity,
) )
from python_pkg.steam_backlog_enforcer.hltb import ( from steam_backlog_enforcer.hltb import (
_get_auth_info, _get_auth_info,
load_hltb_cache, load_hltb_cache,
save_hltb_cache, save_hltb_cache,
@ -33,7 +33,7 @@ class TestHltbCache:
cache_file = tmp_path / "hltb_cache.json" cache_file = tmp_path / "hltb_cache.json"
cache_file.write_text(json.dumps({"440": 10.5}), encoding="utf-8") cache_file.write_text(json.dumps({"440": 10.5}), encoding="utf-8")
with patch( with patch(
"python_pkg.steam_backlog_enforcer._hltb_types.HLTB_CACHE_FILE", cache_file "steam_backlog_enforcer._hltb_types.HLTB_CACHE_FILE", cache_file
): ):
result = load_hltb_cache() result = load_hltb_cache()
assert result == {440: 10.5} assert result == {440: 10.5}
@ -41,7 +41,7 @@ class TestHltbCache:
def test_load_cache_missing(self, tmp_path: Path) -> None: def test_load_cache_missing(self, tmp_path: Path) -> None:
cache_file = tmp_path / "nonexistent.json" cache_file = tmp_path / "nonexistent.json"
with patch( with patch(
"python_pkg.steam_backlog_enforcer._hltb_types.HLTB_CACHE_FILE", cache_file "steam_backlog_enforcer._hltb_types.HLTB_CACHE_FILE", cache_file
): ):
assert load_hltb_cache() == {} assert load_hltb_cache() == {}
@ -49,7 +49,7 @@ class TestHltbCache:
cache_file = tmp_path / "hltb_cache.json" cache_file = tmp_path / "hltb_cache.json"
cache_file.write_text("not json", encoding="utf-8") cache_file.write_text("not json", encoding="utf-8")
with patch( with patch(
"python_pkg.steam_backlog_enforcer._hltb_types.HLTB_CACHE_FILE", cache_file "steam_backlog_enforcer._hltb_types.HLTB_CACHE_FILE", cache_file
): ):
assert load_hltb_cache() == {} assert load_hltb_cache() == {}
@ -57,17 +57,17 @@ class TestHltbCache:
cache_file = tmp_path / "hltb_cache.json" cache_file = tmp_path / "hltb_cache.json"
with ( with (
patch( patch(
"python_pkg.steam_backlog_enforcer._hltb_types.HLTB_CACHE_FILE", "steam_backlog_enforcer._hltb_types.HLTB_CACHE_FILE",
cache_file, cache_file,
), ),
patch("python_pkg.steam_backlog_enforcer._hltb_types.CONFIG_DIR", tmp_path), patch("steam_backlog_enforcer._hltb_types.CONFIG_DIR", tmp_path),
): ):
save_hltb_cache({440: 10.5}) save_hltb_cache({440: 10.5})
assert cache_file.exists() assert cache_file.exists()
def test_save_cache_os_error(self, tmp_path: Path) -> None: def test_save_cache_os_error(self, tmp_path: Path) -> None:
with patch( with patch(
"python_pkg.steam_backlog_enforcer._hltb_types._atomic_write", "steam_backlog_enforcer._hltb_types._atomic_write",
side_effect=OSError("disk full"), side_effect=OSError("disk full"),
): ):
save_hltb_cache({440: 10.5}) # Should not raise save_hltb_cache({440: 10.5}) # Should not raise
@ -80,7 +80,7 @@ class TestGetHltbSearchUrl:
mock_info = MagicMock() mock_info = MagicMock()
mock_info.search_url = "/api/search/abc" mock_info.search_url = "/api/search/abc"
with patch( with patch(
"python_pkg.steam_backlog_enforcer._hltb_search.HTMLRequests" "steam_backlog_enforcer._hltb_search.HTMLRequests"
) as mock_html: ) as mock_html:
mock_html.send_website_request_getcode.return_value = mock_info mock_html.send_website_request_getcode.return_value = mock_info
mock_html.BASE_URL = "https://howlongtobeat.com" mock_html.BASE_URL = "https://howlongtobeat.com"
@ -89,7 +89,7 @@ class TestGetHltbSearchUrl:
def test_fallback_url(self) -> None: def test_fallback_url(self) -> None:
with patch( with patch(
"python_pkg.steam_backlog_enforcer._hltb_search.HTMLRequests" "steam_backlog_enforcer._hltb_search.HTMLRequests"
) as mock_html: ) as mock_html:
mock_html.send_website_request_getcode.return_value = None mock_html.send_website_request_getcode.return_value = None
url = _get_hltb_search_url() url = _get_hltb_search_url()
@ -99,7 +99,7 @@ class TestGetHltbSearchUrl:
mock_info = MagicMock() mock_info = MagicMock()
mock_info.search_url = "/api/search/xyz" mock_info.search_url = "/api/search/xyz"
with patch( with patch(
"python_pkg.steam_backlog_enforcer._hltb_search.HTMLRequests" "steam_backlog_enforcer._hltb_search.HTMLRequests"
) as mock_html: ) as mock_html:
mock_html.send_website_request_getcode.side_effect = [None, mock_info] mock_html.send_website_request_getcode.side_effect = [None, mock_info]
mock_html.BASE_URL = "https://howlongtobeat.com" mock_html.BASE_URL = "https://howlongtobeat.com"
@ -108,7 +108,7 @@ class TestGetHltbSearchUrl:
def test_exception_fallback(self) -> None: def test_exception_fallback(self) -> None:
with patch( with patch(
"python_pkg.steam_backlog_enforcer._hltb_search.HTMLRequests" "steam_backlog_enforcer._hltb_search.HTMLRequests"
) as mock_html: ) as mock_html:
mock_html.send_website_request_getcode.side_effect = RuntimeError mock_html.send_website_request_getcode.side_effect = RuntimeError
url = _get_hltb_search_url() url = _get_hltb_search_url()

View File

@ -10,7 +10,7 @@ from unittest.mock import AsyncMock, MagicMock, patch
import aiohttp import aiohttp
from typing_extensions import Self from typing_extensions import Self
from python_pkg.steam_backlog_enforcer._hltb_detail import ( from steam_backlog_enforcer._hltb_detail import (
_apply_dlc_leisure_overrides, _apply_dlc_leisure_overrides,
_as_positive_int, _as_positive_int,
_collect_dlc_relationships, _collect_dlc_relationships,
@ -22,7 +22,7 @@ from python_pkg.steam_backlog_enforcer._hltb_detail import (
_fetch_leisure_times, _fetch_leisure_times,
_process_game_detail, _process_game_detail,
) )
from python_pkg.steam_backlog_enforcer._hltb_types import ( from steam_backlog_enforcer._hltb_types import (
_SAVE_INTERVAL, _SAVE_INTERVAL,
HLTBResult, HLTBResult,
_HLTBExtras, _HLTBExtras,
@ -155,7 +155,7 @@ class TestInternalHelpers:
async def _run() -> dict[int, float]: async def _run() -> dict[int, float]:
async with aiohttp.ClientSession() as session: async with aiohttp.ClientSession() as session:
with patch( with patch(
"python_pkg.steam_backlog_enforcer._hltb_detail._fetch_detail_one", "steam_backlog_enforcer._hltb_detail._fetch_detail_one",
new_callable=AsyncMock, new_callable=AsyncMock,
return_value=None, return_value=None,
): ):
@ -176,7 +176,7 @@ class TestInternalHelpers:
async def _run() -> dict[int, float]: async def _run() -> dict[int, float]:
async with aiohttp.ClientSession() as session: async with aiohttp.ClientSession() as session:
with patch( with patch(
"python_pkg.steam_backlog_enforcer._hltb_detail._fetch_detail_one", "steam_backlog_enforcer._hltb_detail._fetch_detail_one",
new_callable=AsyncMock, new_callable=AsyncMock,
return_value=bad_dlc_data, return_value=bad_dlc_data,
): ):
@ -339,7 +339,7 @@ class TestFetchLeisureTimes:
} }
cache: dict[int, float] = {} cache: dict[int, float] = {}
with patch( with patch(
"python_pkg.steam_backlog_enforcer._hltb_detail._fetch_detail_one", "steam_backlog_enforcer._hltb_detail._fetch_detail_one",
new_callable=AsyncMock, new_callable=AsyncMock,
return_value=game_data, return_value=game_data,
): ):
@ -378,7 +378,7 @@ class TestFetchLeisureTimes:
] ]
cache: dict[int, float] = {} cache: dict[int, float] = {}
with patch( with patch(
"python_pkg.steam_backlog_enforcer._hltb_detail._fetch_detail_one", "steam_backlog_enforcer._hltb_detail._fetch_detail_one",
new_callable=AsyncMock, new_callable=AsyncMock,
return_value=None, return_value=None,
): ):
@ -399,7 +399,7 @@ class TestFetchLeisureTimes:
game_data: dict[str, Any] = {"game": [], "relationships": []} game_data: dict[str, Any] = {"game": [], "relationships": []}
cache: dict[int, float] = {} cache: dict[int, float] = {}
with patch( with patch(
"python_pkg.steam_backlog_enforcer._hltb_detail._fetch_detail_one", "steam_backlog_enforcer._hltb_detail._fetch_detail_one",
new_callable=AsyncMock, new_callable=AsyncMock,
return_value=game_data, return_value=game_data,
): ):
@ -424,7 +424,7 @@ class TestFetchLeisureTimes:
cache: dict[int, float] = {} cache: dict[int, float] = {}
cb = MagicMock() cb = MagicMock()
with patch( with patch(
"python_pkg.steam_backlog_enforcer._hltb_detail._fetch_detail_one", "steam_backlog_enforcer._hltb_detail._fetch_detail_one",
new_callable=AsyncMock, new_callable=AsyncMock,
return_value=game_data, return_value=game_data,
): ):
@ -450,12 +450,12 @@ class TestFetchLeisureTimes:
cache: dict[int, float] = {} cache: dict[int, float] = {}
with ( with (
patch( patch(
"python_pkg.steam_backlog_enforcer._hltb_detail._fetch_detail_one", "steam_backlog_enforcer._hltb_detail._fetch_detail_one",
new_callable=AsyncMock, new_callable=AsyncMock,
return_value=game_data, return_value=game_data,
), ),
patch( patch(
"python_pkg.steam_backlog_enforcer._hltb_detail.save_hltb_cache" "steam_backlog_enforcer._hltb_detail.save_hltb_cache"
) as mock_save, ) as mock_save,
): ):
asyncio.run(_fetch_leisure_times(results, cache, {}, None)) asyncio.run(_fetch_leisure_times(results, cache, {}, None))
@ -481,7 +481,7 @@ class TestFetchLeisureTimes:
} }
cache: dict[int, float] = {} cache: dict[int, float] = {}
with patch( with patch(
"python_pkg.steam_backlog_enforcer._hltb_detail._fetch_detail_one", "steam_backlog_enforcer._hltb_detail._fetch_detail_one",
new_callable=AsyncMock, new_callable=AsyncMock,
side_effect=[base_data, dlc_data], side_effect=[base_data, dlc_data],
): ):
@ -507,7 +507,7 @@ class TestFetchLeisureTimes:
} }
cache: dict[int, float] = {} cache: dict[int, float] = {}
with patch( with patch(
"python_pkg.steam_backlog_enforcer._hltb_detail._fetch_detail_one", "steam_backlog_enforcer._hltb_detail._fetch_detail_one",
new_callable=AsyncMock, new_callable=AsyncMock,
side_effect=[base_data, None], side_effect=[base_data, None],
): ):
@ -535,7 +535,7 @@ class TestFetchLeisureTimes:
cache: dict[int, float] = {} cache: dict[int, float] = {}
extras = _HLTBExtras(count_comp={440: 5}) extras = _HLTBExtras(count_comp={440: 5})
with patch( with patch(
"python_pkg.steam_backlog_enforcer._hltb_detail._fetch_detail_one", "steam_backlog_enforcer._hltb_detail._fetch_detail_one",
new_callable=AsyncMock, new_callable=AsyncMock,
return_value=game_data, return_value=game_data,
): ):
@ -561,7 +561,7 @@ class TestFetchLeisureTimes:
cache: dict[int, float] = {} cache: dict[int, float] = {}
extras = _HLTBExtras(count_comp={440: 5}) extras = _HLTBExtras(count_comp={440: 5})
with patch( with patch(
"python_pkg.steam_backlog_enforcer._hltb_detail._fetch_detail_one", "steam_backlog_enforcer._hltb_detail._fetch_detail_one",
new_callable=AsyncMock, new_callable=AsyncMock,
return_value=game_data, return_value=game_data,
): ):

View File

@ -8,8 +8,8 @@ from unittest.mock import MagicMock, patch
from typing_extensions import Self from typing_extensions import Self
from python_pkg.steam_backlog_enforcer._hltb_search import _AuthInfo from steam_backlog_enforcer._hltb_search import _AuthInfo
from python_pkg.steam_backlog_enforcer.hltb import ( from steam_backlog_enforcer.hltb import (
HLTB_BASE_URL, HLTB_BASE_URL,
HLTBResult, HLTBResult,
_fetch_batch_confidence_only, _fetch_batch_confidence_only,
@ -21,9 +21,9 @@ from python_pkg.steam_backlog_enforcer.hltb import (
) )
if TYPE_CHECKING: if TYPE_CHECKING:
from python_pkg.steam_backlog_enforcer._hltb_types import _HLTBExtras from steam_backlog_enforcer._hltb_types import _HLTBExtras
PKG = "python_pkg.steam_backlog_enforcer.hltb" PKG = "steam_backlog_enforcer.hltb"
class TestFetchHltbTimesCached: class TestFetchHltbTimesCached:

View File

@ -4,7 +4,7 @@ from __future__ import annotations
from unittest.mock import patch from unittest.mock import patch
from python_pkg.steam_backlog_enforcer.hltb import ( from steam_backlog_enforcer.hltb import (
HLTBResult, HLTBResult,
fetch_hltb_times, fetch_hltb_times,
) )
@ -21,7 +21,7 @@ class TestFetchHltbTimes:
app_id=440, game_name="TF2", completionist_hours=50.0, similarity=1.0 app_id=440, game_name="TF2", completionist_hours=50.0, similarity=1.0
) )
with patch( with patch(
"python_pkg.steam_backlog_enforcer.hltb._fetch_batch", "steam_backlog_enforcer.hltb._fetch_batch",
return_value=[mock_result], return_value=[mock_result],
): ):
results = fetch_hltb_times([(440, "TF2")]) results = fetch_hltb_times([(440, "TF2")])
@ -29,7 +29,7 @@ class TestFetchHltbTimes:
def test_none_cache(self) -> None: def test_none_cache(self) -> None:
with patch( with patch(
"python_pkg.steam_backlog_enforcer.hltb._fetch_batch", "steam_backlog_enforcer.hltb._fetch_batch",
return_value=[], return_value=[],
): ):
results = fetch_hltb_times([(440, "TF2")]) results = fetch_hltb_times([(440, "TF2")])
@ -37,7 +37,7 @@ class TestFetchHltbTimes:
def test_explicit_cache(self) -> None: def test_explicit_cache(self) -> None:
with patch( with patch(
"python_pkg.steam_backlog_enforcer.hltb._fetch_batch", "steam_backlog_enforcer.hltb._fetch_batch",
return_value=[], return_value=[],
): ):
cache: dict[int, float] = {440: 10.0} cache: dict[int, float] = {440: 10.0}

View File

@ -9,12 +9,12 @@ from unittest.mock import AsyncMock, MagicMock, patch
import aiohttp import aiohttp
from typing_extensions import Self from typing_extensions import Self
from python_pkg.steam_backlog_enforcer._hltb_search import ( from steam_backlog_enforcer._hltb_search import (
_fetch_batch, _fetch_batch,
_search_one, _search_one,
_SearchCtx, _SearchCtx,
) )
from python_pkg.steam_backlog_enforcer._hltb_types import ( from steam_backlog_enforcer._hltb_types import (
_SAVE_INTERVAL, _SAVE_INTERVAL,
) )
@ -260,7 +260,7 @@ class TestSearchOne:
ctx.counter["done"] = _SAVE_INTERVAL - 1 ctx.counter["done"] = _SAVE_INTERVAL - 1
with patch( with patch(
"python_pkg.steam_backlog_enforcer._hltb_search.save_hltb_cache" "steam_backlog_enforcer._hltb_search.save_hltb_cache"
) as mock_save: ) as mock_save:
asyncio.run(_search_one(asyncio.Semaphore(1), ctx, 440, "TF2")) asyncio.run(_search_one(asyncio.Semaphore(1), ctx, 440, "TF2"))
mock_save.assert_called_once() mock_save.assert_called_once()
@ -272,11 +272,11 @@ class TestFetchBatchHltb:
def test_no_auth(self) -> None: def test_no_auth(self) -> None:
with ( with (
patch( patch(
"python_pkg.steam_backlog_enforcer._hltb_search._get_hltb_search_url", "steam_backlog_enforcer._hltb_search._get_hltb_search_url",
return_value="https://example.com", return_value="https://example.com",
), ),
patch( patch(
"python_pkg.steam_backlog_enforcer._hltb_search._get_auth_info", "steam_backlog_enforcer._hltb_search._get_auth_info",
new_callable=AsyncMock, new_callable=AsyncMock,
return_value=None, return_value=None,
), ),

View File

@ -9,16 +9,16 @@ from unittest.mock import AsyncMock, MagicMock, patch
from typing_extensions import Self from typing_extensions import Self
from python_pkg.steam_backlog_enforcer._hltb_detail import ( from steam_backlog_enforcer._hltb_detail import (
_extract_leisure_hours, _extract_leisure_hours,
_parse_game_page, _parse_game_page,
) )
from python_pkg.steam_backlog_enforcer._hltb_search import ( from steam_backlog_enforcer._hltb_search import (
_build_search_variants, _build_search_variants,
_fetch_batch, _fetch_batch,
_pick_best_hltb_entry, _pick_best_hltb_entry,
) )
from python_pkg.steam_backlog_enforcer._hltb_types import ( from steam_backlog_enforcer._hltb_types import (
HLTBResult, HLTBResult,
_AuthInfo, _AuthInfo,
) )
@ -110,16 +110,16 @@ class TestPickBestEntry:
auth = _AuthInfo("token123", "ign_x", "ff") auth = _AuthInfo("token123", "ign_x", "ff")
with ( with (
patch( patch(
"python_pkg.steam_backlog_enforcer._hltb_search._get_hltb_search_url", "steam_backlog_enforcer._hltb_search._get_hltb_search_url",
return_value="https://example.com", return_value="https://example.com",
), ),
patch( patch(
"python_pkg.steam_backlog_enforcer._hltb_search._get_auth_info", "steam_backlog_enforcer._hltb_search._get_auth_info",
new_callable=AsyncMock, new_callable=AsyncMock,
return_value=auth, return_value=auth,
), ),
patch( patch(
"python_pkg.steam_backlog_enforcer._hltb_search._search_one", "steam_backlog_enforcer._hltb_search._search_one",
new_callable=AsyncMock, new_callable=AsyncMock,
return_value=HLTBResult( return_value=HLTBResult(
app_id=440, app_id=440,
@ -130,7 +130,7 @@ class TestPickBestEntry:
), ),
), ),
patch( patch(
"python_pkg.steam_backlog_enforcer._hltb_search._fetch_leisure_times", "steam_backlog_enforcer._hltb_search._fetch_leisure_times",
new_callable=AsyncMock, new_callable=AsyncMock,
), ),
): ):
@ -141,21 +141,21 @@ class TestPickBestEntry:
auth = _AuthInfo("tok123") auth = _AuthInfo("tok123")
with ( with (
patch( patch(
"python_pkg.steam_backlog_enforcer._hltb_search._get_hltb_search_url", "steam_backlog_enforcer._hltb_search._get_hltb_search_url",
return_value="https://example.com", return_value="https://example.com",
), ),
patch( patch(
"python_pkg.steam_backlog_enforcer._hltb_search._get_auth_info", "steam_backlog_enforcer._hltb_search._get_auth_info",
new_callable=AsyncMock, new_callable=AsyncMock,
return_value=auth, return_value=auth,
), ),
patch( patch(
"python_pkg.steam_backlog_enforcer._hltb_search._search_one", "steam_backlog_enforcer._hltb_search._search_one",
new_callable=AsyncMock, new_callable=AsyncMock,
return_value=None, return_value=None,
), ),
patch( patch(
"python_pkg.steam_backlog_enforcer._hltb_search._fetch_leisure_times", "steam_backlog_enforcer._hltb_search._fetch_leisure_times",
new_callable=AsyncMock, new_callable=AsyncMock,
), ),
): ):
@ -166,21 +166,21 @@ class TestPickBestEntry:
auth = _AuthInfo("tok123") auth = _AuthInfo("tok123")
with ( with (
patch( patch(
"python_pkg.steam_backlog_enforcer._hltb_search._get_hltb_search_url", "steam_backlog_enforcer._hltb_search._get_hltb_search_url",
return_value="https://example.com", return_value="https://example.com",
), ),
patch( patch(
"python_pkg.steam_backlog_enforcer._hltb_search._get_auth_info", "steam_backlog_enforcer._hltb_search._get_auth_info",
new_callable=AsyncMock, new_callable=AsyncMock,
return_value=auth, return_value=auth,
), ),
patch( patch(
"python_pkg.steam_backlog_enforcer._hltb_search._search_one", "steam_backlog_enforcer._hltb_search._search_one",
new_callable=AsyncMock, new_callable=AsyncMock,
return_value=None, return_value=None,
), ),
patch( patch(
"python_pkg.steam_backlog_enforcer._hltb_search._fetch_leisure_times", "steam_backlog_enforcer._hltb_search._fetch_leisure_times",
new_callable=AsyncMock, new_callable=AsyncMock,
), ),
): ):

View File

@ -8,7 +8,7 @@ from unittest.mock import AsyncMock, MagicMock, patch
import pytest import pytest
from python_pkg.steam_backlog_enforcer.library_hider import ( from steam_backlog_enforcer.library_hider import (
_cdp_result_value, _cdp_result_value,
_evaluate_js, _evaluate_js,
_evaluate_js_async, _evaluate_js_async,
@ -37,7 +37,7 @@ class TestGetSharedJsWsUrl:
mock_resp = MagicMock() mock_resp = MagicMock()
mock_resp.json.return_value = targets mock_resp.json.return_value = targets
with patch( with patch(
"python_pkg.steam_backlog_enforcer.library_hider.requests.get", "steam_backlog_enforcer.library_hider.requests.get",
return_value=mock_resp, return_value=mock_resp,
): ):
result = _get_shared_js_ws_url() result = _get_shared_js_ws_url()
@ -48,14 +48,14 @@ class TestGetSharedJsWsUrl:
mock_resp = MagicMock() mock_resp = MagicMock()
mock_resp.json.return_value = targets mock_resp.json.return_value = targets
with patch( with patch(
"python_pkg.steam_backlog_enforcer.library_hider.requests.get", "steam_backlog_enforcer.library_hider.requests.get",
return_value=mock_resp, return_value=mock_resp,
): ):
assert _get_shared_js_ws_url() is None assert _get_shared_js_ws_url() is None
def test_connection_error(self) -> None: def test_connection_error(self) -> None:
with patch( with patch(
"python_pkg.steam_backlog_enforcer.library_hider.requests.get", "steam_backlog_enforcer.library_hider.requests.get",
side_effect=OSError, side_effect=OSError,
): ):
assert _get_shared_js_ws_url() is None assert _get_shared_js_ws_url() is None
@ -74,7 +74,7 @@ class TestEvaluateJsAsync:
mock_ws.__aexit__ = AsyncMock(return_value=False) mock_ws.__aexit__ = AsyncMock(return_value=False)
with patch( with patch(
"python_pkg.steam_backlog_enforcer.library_hider.websockets.connect", "steam_backlog_enforcer.library_hider.websockets.connect",
return_value=mock_ws, return_value=mock_ws,
): ):
result = asyncio.run(_evaluate_js_async("ws://test", "1+1")) result = asyncio.run(_evaluate_js_async("ws://test", "1+1"))
@ -87,11 +87,11 @@ class TestEvaluateJs:
def test_success(self) -> None: def test_success(self) -> None:
with ( with (
patch( patch(
"python_pkg.steam_backlog_enforcer.library_hider._get_shared_js_ws_url", "steam_backlog_enforcer.library_hider._get_shared_js_ws_url",
return_value="ws://test", return_value="ws://test",
), ),
patch( patch(
"python_pkg.steam_backlog_enforcer.library_hider.asyncio.run", "steam_backlog_enforcer.library_hider.asyncio.run",
return_value={"result": {"result": {"value": "ok"}}}, return_value={"result": {"result": {"value": "ok"}}},
), ),
): ):
@ -101,7 +101,7 @@ class TestEvaluateJs:
def test_no_ws_url(self) -> None: def test_no_ws_url(self) -> None:
with ( with (
patch( patch(
"python_pkg.steam_backlog_enforcer.library_hider._get_shared_js_ws_url", "steam_backlog_enforcer.library_hider._get_shared_js_ws_url",
return_value=None, return_value=None,
), ),
pytest.raises(RuntimeError, match="SharedJSContext not found"), pytest.raises(RuntimeError, match="SharedJSContext not found"),
@ -136,7 +136,7 @@ class TestIsSteamRunning:
def test_running(self) -> None: def test_running(self) -> None:
mock_result = MagicMock(returncode=0) mock_result = MagicMock(returncode=0)
with patch( with patch(
"python_pkg.steam_backlog_enforcer.library_hider.subprocess.run", "steam_backlog_enforcer.library_hider.subprocess.run",
return_value=mock_result, return_value=mock_result,
): ):
assert _is_steam_running() is True assert _is_steam_running() is True
@ -144,7 +144,7 @@ class TestIsSteamRunning:
def test_not_running(self) -> None: def test_not_running(self) -> None:
mock_result = MagicMock(returncode=1) mock_result = MagicMock(returncode=1)
with patch( with patch(
"python_pkg.steam_backlog_enforcer.library_hider.subprocess.run", "steam_backlog_enforcer.library_hider.subprocess.run",
return_value=mock_result, return_value=mock_result,
): ):
assert _is_steam_running() is False assert _is_steam_running() is False
@ -155,14 +155,14 @@ class TestSteamHasDebugPort:
def test_has_port(self) -> None: def test_has_port(self) -> None:
with patch( with patch(
"python_pkg.steam_backlog_enforcer.library_hider._get_shared_js_ws_url", "steam_backlog_enforcer.library_hider._get_shared_js_ws_url",
return_value="ws://test", return_value="ws://test",
): ):
assert _steam_has_debug_port() is True assert _steam_has_debug_port() is True
def test_no_port(self) -> None: def test_no_port(self) -> None:
with patch( with patch(
"python_pkg.steam_backlog_enforcer.library_hider._get_shared_js_ws_url", "steam_backlog_enforcer.library_hider._get_shared_js_ws_url",
return_value=None, return_value=None,
): ):
assert _steam_has_debug_port() is False assert _steam_has_debug_port() is False
@ -173,7 +173,7 @@ class TestWaitForCdpReady:
def test_ready_immediately(self) -> None: def test_ready_immediately(self) -> None:
with patch( with patch(
"python_pkg.steam_backlog_enforcer.library_hider._get_shared_js_ws_url", "steam_backlog_enforcer.library_hider._get_shared_js_ws_url",
return_value="ws://test", return_value="ws://test",
): ):
assert _wait_for_cdp_ready() is True assert _wait_for_cdp_ready() is True
@ -181,14 +181,14 @@ class TestWaitForCdpReady:
def test_timeout(self) -> None: def test_timeout(self) -> None:
with ( with (
patch( patch(
"python_pkg.steam_backlog_enforcer.library_hider._get_shared_js_ws_url", "steam_backlog_enforcer.library_hider._get_shared_js_ws_url",
return_value=None, return_value=None,
), ),
patch( patch(
"python_pkg.steam_backlog_enforcer.library_hider.time.sleep", "steam_backlog_enforcer.library_hider.time.sleep",
), ),
patch( patch(
"python_pkg.steam_backlog_enforcer.library_hider._STEAM_STARTUP_WAIT", "steam_backlog_enforcer.library_hider._STEAM_STARTUP_WAIT",
2, 2,
), ),
): ):
@ -201,11 +201,11 @@ class TestWaitForCollectionsReady:
def test_ready(self) -> None: def test_ready(self) -> None:
with ( with (
patch( patch(
"python_pkg.steam_backlog_enforcer.library_hider._evaluate_js", "steam_backlog_enforcer.library_hider._evaluate_js",
return_value={"result": {"result": {"value": "ok"}}}, return_value={"result": {"result": {"value": "ok"}}},
), ),
patch( patch(
"python_pkg.steam_backlog_enforcer.library_hider._cdp_result_value", "steam_backlog_enforcer.library_hider._cdp_result_value",
return_value="ok", return_value="ok",
), ),
): ):
@ -214,18 +214,18 @@ class TestWaitForCollectionsReady:
def test_not_ready_then_ready(self) -> None: def test_not_ready_then_ready(self) -> None:
with ( with (
patch( patch(
"python_pkg.steam_backlog_enforcer.library_hider._evaluate_js", "steam_backlog_enforcer.library_hider._evaluate_js",
return_value={"result": {"result": {"value": "not_ready"}}}, return_value={"result": {"result": {"value": "not_ready"}}},
), ),
patch( patch(
"python_pkg.steam_backlog_enforcer.library_hider._cdp_result_value", "steam_backlog_enforcer.library_hider._cdp_result_value",
side_effect=["not_ready", "ok"], side_effect=["not_ready", "ok"],
), ),
patch( patch(
"python_pkg.steam_backlog_enforcer.library_hider.time.sleep", "steam_backlog_enforcer.library_hider.time.sleep",
), ),
patch( patch(
"python_pkg.steam_backlog_enforcer.library_hider._STEAM_STARTUP_WAIT", "steam_backlog_enforcer.library_hider._STEAM_STARTUP_WAIT",
2, 2,
), ),
): ):
@ -234,14 +234,14 @@ class TestWaitForCollectionsReady:
def test_timeout(self) -> None: def test_timeout(self) -> None:
with ( with (
patch( patch(
"python_pkg.steam_backlog_enforcer.library_hider._evaluate_js", "steam_backlog_enforcer.library_hider._evaluate_js",
side_effect=RuntimeError, side_effect=RuntimeError,
), ),
patch( patch(
"python_pkg.steam_backlog_enforcer.library_hider.time.sleep", "steam_backlog_enforcer.library_hider.time.sleep",
), ),
patch( patch(
"python_pkg.steam_backlog_enforcer.library_hider._STEAM_STARTUP_WAIT", "steam_backlog_enforcer.library_hider._STEAM_STARTUP_WAIT",
2, 2,
), ),
): ):
@ -255,10 +255,10 @@ class TestShutdownSteam:
mock_result = MagicMock(returncode=1) # Not running mock_result = MagicMock(returncode=1) # Not running
with ( with (
patch( patch(
"python_pkg.steam_backlog_enforcer.library_hider._run_as_user", "steam_backlog_enforcer.library_hider._run_as_user",
), ),
patch( patch(
"python_pkg.steam_backlog_enforcer.library_hider.subprocess.run", "steam_backlog_enforcer.library_hider.subprocess.run",
return_value=mock_result, return_value=mock_result,
), ),
): ):
@ -268,21 +268,21 @@ class TestShutdownSteam:
results = [MagicMock(returncode=0), MagicMock(returncode=1)] results = [MagicMock(returncode=0), MagicMock(returncode=1)]
with ( with (
patch( patch(
"python_pkg.steam_backlog_enforcer.library_hider._run_as_user", "steam_backlog_enforcer.library_hider._run_as_user",
), ),
patch( patch(
"python_pkg.steam_backlog_enforcer.library_hider.subprocess.run", "steam_backlog_enforcer.library_hider.subprocess.run",
side_effect=results, side_effect=results,
), ),
patch( patch(
"python_pkg.steam_backlog_enforcer.library_hider.time.sleep", "steam_backlog_enforcer.library_hider.time.sleep",
), ),
): ):
_shutdown_steam() _shutdown_steam()
def test_file_not_found(self) -> None: def test_file_not_found(self) -> None:
with patch( with patch(
"python_pkg.steam_backlog_enforcer.library_hider._run_as_user", "steam_backlog_enforcer.library_hider._run_as_user",
side_effect=FileNotFoundError, side_effect=FileNotFoundError,
): ):
_shutdown_steam() # Should not raise _shutdown_steam() # Should not raise
@ -291,14 +291,14 @@ class TestShutdownSteam:
mock_result = MagicMock(returncode=0) # Still running mock_result = MagicMock(returncode=0) # Still running
with ( with (
patch( patch(
"python_pkg.steam_backlog_enforcer.library_hider._run_as_user", "steam_backlog_enforcer.library_hider._run_as_user",
), ),
patch( patch(
"python_pkg.steam_backlog_enforcer.library_hider.subprocess.run", "steam_backlog_enforcer.library_hider.subprocess.run",
return_value=mock_result, return_value=mock_result,
), ),
patch( patch(
"python_pkg.steam_backlog_enforcer.library_hider.time.sleep", "steam_backlog_enforcer.library_hider.time.sleep",
), ),
): ):
_shutdown_steam() # Should complete loop without raising _shutdown_steam() # Should complete loop without raising
@ -309,7 +309,7 @@ class TestLaunchSteamWithDebug:
def test_launches(self) -> None: def test_launches(self) -> None:
with patch( with patch(
"python_pkg.steam_backlog_enforcer.library_hider._run_as_user", "steam_backlog_enforcer.library_hider._run_as_user",
) as mock_run: ) as mock_run:
_launch_steam_with_debug() _launch_steam_with_debug()
mock_run.assert_called_once() mock_run.assert_called_once()
@ -320,7 +320,7 @@ class TestEnsureSteamDebugPort:
def test_already_available(self) -> None: def test_already_available(self) -> None:
with patch( with patch(
"python_pkg.steam_backlog_enforcer.library_hider._steam_has_debug_port", "steam_backlog_enforcer.library_hider._steam_has_debug_port",
return_value=True, return_value=True,
): ):
ensure_steam_debug_port() ensure_steam_debug_port()
@ -328,22 +328,22 @@ class TestEnsureSteamDebugPort:
def test_starts_fresh(self) -> None: def test_starts_fresh(self) -> None:
with ( with (
patch( patch(
"python_pkg.steam_backlog_enforcer.library_hider._steam_has_debug_port", "steam_backlog_enforcer.library_hider._steam_has_debug_port",
return_value=False, return_value=False,
), ),
patch( patch(
"python_pkg.steam_backlog_enforcer.library_hider._is_steam_running", "steam_backlog_enforcer.library_hider._is_steam_running",
return_value=False, return_value=False,
), ),
patch( patch(
"python_pkg.steam_backlog_enforcer.library_hider._launch_steam_with_debug", "steam_backlog_enforcer.library_hider._launch_steam_with_debug",
), ),
patch( patch(
"python_pkg.steam_backlog_enforcer.library_hider._wait_for_cdp_ready", "steam_backlog_enforcer.library_hider._wait_for_cdp_ready",
return_value=True, return_value=True,
), ),
patch( patch(
"python_pkg.steam_backlog_enforcer.library_hider._wait_for_collections_ready", "steam_backlog_enforcer.library_hider._wait_for_collections_ready",
return_value=True, return_value=True,
), ),
): ):
@ -352,25 +352,25 @@ class TestEnsureSteamDebugPort:
def test_restarts_running_steam(self) -> None: def test_restarts_running_steam(self) -> None:
with ( with (
patch( patch(
"python_pkg.steam_backlog_enforcer.library_hider._steam_has_debug_port", "steam_backlog_enforcer.library_hider._steam_has_debug_port",
return_value=False, return_value=False,
), ),
patch( patch(
"python_pkg.steam_backlog_enforcer.library_hider._is_steam_running", "steam_backlog_enforcer.library_hider._is_steam_running",
return_value=True, return_value=True,
), ),
patch( patch(
"python_pkg.steam_backlog_enforcer.library_hider._shutdown_steam", "steam_backlog_enforcer.library_hider._shutdown_steam",
), ),
patch( patch(
"python_pkg.steam_backlog_enforcer.library_hider._launch_steam_with_debug", "steam_backlog_enforcer.library_hider._launch_steam_with_debug",
), ),
patch( patch(
"python_pkg.steam_backlog_enforcer.library_hider._wait_for_cdp_ready", "steam_backlog_enforcer.library_hider._wait_for_cdp_ready",
return_value=True, return_value=True,
), ),
patch( patch(
"python_pkg.steam_backlog_enforcer.library_hider._wait_for_collections_ready", "steam_backlog_enforcer.library_hider._wait_for_collections_ready",
return_value=True, return_value=True,
), ),
): ):
@ -379,18 +379,18 @@ class TestEnsureSteamDebugPort:
def test_cdp_timeout(self) -> None: def test_cdp_timeout(self) -> None:
with ( with (
patch( patch(
"python_pkg.steam_backlog_enforcer.library_hider._steam_has_debug_port", "steam_backlog_enforcer.library_hider._steam_has_debug_port",
return_value=False, return_value=False,
), ),
patch( patch(
"python_pkg.steam_backlog_enforcer.library_hider._is_steam_running", "steam_backlog_enforcer.library_hider._is_steam_running",
return_value=False, return_value=False,
), ),
patch( patch(
"python_pkg.steam_backlog_enforcer.library_hider._launch_steam_with_debug", "steam_backlog_enforcer.library_hider._launch_steam_with_debug",
), ),
patch( patch(
"python_pkg.steam_backlog_enforcer.library_hider._wait_for_cdp_ready", "steam_backlog_enforcer.library_hider._wait_for_cdp_ready",
return_value=False, return_value=False,
), ),
pytest.raises(RuntimeError, match="Timed out waiting for Steam CDP"), pytest.raises(RuntimeError, match="Timed out waiting for Steam CDP"),
@ -400,22 +400,22 @@ class TestEnsureSteamDebugPort:
def test_collections_timeout(self) -> None: def test_collections_timeout(self) -> None:
with ( with (
patch( patch(
"python_pkg.steam_backlog_enforcer.library_hider._steam_has_debug_port", "steam_backlog_enforcer.library_hider._steam_has_debug_port",
return_value=False, return_value=False,
), ),
patch( patch(
"python_pkg.steam_backlog_enforcer.library_hider._is_steam_running", "steam_backlog_enforcer.library_hider._is_steam_running",
return_value=False, return_value=False,
), ),
patch( patch(
"python_pkg.steam_backlog_enforcer.library_hider._launch_steam_with_debug", "steam_backlog_enforcer.library_hider._launch_steam_with_debug",
), ),
patch( patch(
"python_pkg.steam_backlog_enforcer.library_hider._wait_for_cdp_ready", "steam_backlog_enforcer.library_hider._wait_for_cdp_ready",
return_value=True, return_value=True,
), ),
patch( patch(
"python_pkg.steam_backlog_enforcer.library_hider._wait_for_collections_ready", "steam_backlog_enforcer.library_hider._wait_for_collections_ready",
return_value=False, return_value=False,
), ),
pytest.raises( pytest.raises(

View File

@ -6,14 +6,14 @@ import os
import tempfile import tempfile
from unittest.mock import MagicMock, patch from unittest.mock import MagicMock, patch
from python_pkg.steam_backlog_enforcer.library_hider import ( from steam_backlog_enforcer.library_hider import (
_run_as_user, _run_as_user,
hide_other_games, hide_other_games,
restart_steam, restart_steam,
unhide_all_games, unhide_all_games,
) )
PKG = "python_pkg.steam_backlog_enforcer.library_hider" PKG = "steam_backlog_enforcer.library_hider"
class TestRunAsUser: class TestRunAsUser:

View File

@ -9,9 +9,9 @@ from unittest.mock import patch
import pytest import pytest
from python_pkg.steam_backlog_enforcer._whitelist import WHITELIST_COOLDOWN_SECONDS from steam_backlog_enforcer._whitelist import WHITELIST_COOLDOWN_SECONDS
from python_pkg.steam_backlog_enforcer.config import Config, State from steam_backlog_enforcer.config import Config, State
from python_pkg.steam_backlog_enforcer.main import ( from steam_backlog_enforcer.main import (
cmd_add_exception, cmd_add_exception,
cmd_buy_dlc, cmd_buy_dlc,
cmd_hide, cmd_hide,
@ -27,7 +27,7 @@ from python_pkg.steam_backlog_enforcer.main import (
main, main,
) )
PKG = "python_pkg.steam_backlog_enforcer.main" PKG = "steam_backlog_enforcer.main"
def _snap( def _snap(

View File

@ -5,16 +5,16 @@ from __future__ import annotations
from typing import Any from typing import Any
from unittest.mock import MagicMock, patch from unittest.mock import MagicMock, patch
from python_pkg.steam_backlog_enforcer._cmd_done import ( from steam_backlog_enforcer._cmd_done import (
_enforce_on_done, _enforce_on_done,
_finalize_completion, _finalize_completion,
cmd_done, cmd_done,
) )
from python_pkg.steam_backlog_enforcer.config import Config, State from steam_backlog_enforcer.config import Config, State
from python_pkg.steam_backlog_enforcer.steam_api import GameInfo from steam_backlog_enforcer.steam_api import GameInfo
CMD_DONE_PKG = "python_pkg.steam_backlog_enforcer._cmd_done" CMD_DONE_PKG = "steam_backlog_enforcer._cmd_done"
PKG = "python_pkg.steam_backlog_enforcer.main" PKG = "steam_backlog_enforcer.main"
def _snap( def _snap(

View File

@ -8,15 +8,15 @@ from unittest.mock import MagicMock, patch
import pytest import pytest
from python_pkg.steam_backlog_enforcer._cmd_done import ( from steam_backlog_enforcer._cmd_done import (
cmd_done, cmd_done,
) )
from python_pkg.steam_backlog_enforcer.config import Config, State from steam_backlog_enforcer.config import Config, State
from python_pkg.steam_backlog_enforcer.main import cmd_pick, main from steam_backlog_enforcer.main import cmd_pick, main
from python_pkg.steam_backlog_enforcer.steam_api import GameInfo from steam_backlog_enforcer.steam_api import GameInfo
CMD_DONE_PKG = "python_pkg.steam_backlog_enforcer._cmd_done" CMD_DONE_PKG = "steam_backlog_enforcer._cmd_done"
PKG = "python_pkg.steam_backlog_enforcer.main" PKG = "steam_backlog_enforcer.main"
def _snap( def _snap(

View File

@ -6,8 +6,8 @@ import json
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
from unittest.mock import patch from unittest.mock import patch
from python_pkg.steam_backlog_enforcer import _cmd_done from steam_backlog_enforcer import _cmd_done
from python_pkg.steam_backlog_enforcer._hltb_types import ( from steam_backlog_enforcer._hltb_types import (
HLTBResult, HLTBResult,
_HLTBExtras, _HLTBExtras,
load_hltb_cache, load_hltb_cache,
@ -16,15 +16,15 @@ from python_pkg.steam_backlog_enforcer._hltb_types import (
load_hltb_polls_cache, load_hltb_polls_cache,
save_hltb_cache, save_hltb_cache,
) )
from python_pkg.steam_backlog_enforcer.config import State from steam_backlog_enforcer.config import State
from python_pkg.steam_backlog_enforcer.steam_api import GameInfo from steam_backlog_enforcer.steam_api import GameInfo
if TYPE_CHECKING: if TYPE_CHECKING:
from pathlib import Path from pathlib import Path
_TYPES = "python_pkg.steam_backlog_enforcer._hltb_types" _TYPES = "steam_backlog_enforcer._hltb_types"
_CMD = "python_pkg.steam_backlog_enforcer._cmd_done" _CMD = "steam_backlog_enforcer._cmd_done"
_SCAN = "python_pkg.steam_backlog_enforcer.scanning" _SCAN = "steam_backlog_enforcer.scanning"
class TestCacheSchema: class TestCacheSchema:

View File

@ -6,17 +6,17 @@ import json
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
from unittest.mock import MagicMock, patch from unittest.mock import MagicMock, patch
from python_pkg.steam_backlog_enforcer import _cmd_done, _scanning_confidence, scanning from steam_backlog_enforcer import _cmd_done, _scanning_confidence, scanning
from python_pkg.steam_backlog_enforcer.config import State from steam_backlog_enforcer.config import State
from python_pkg.steam_backlog_enforcer.steam_api import GameInfo from steam_backlog_enforcer.steam_api import GameInfo
if TYPE_CHECKING: if TYPE_CHECKING:
from pathlib import Path from pathlib import Path
_TYPES = "python_pkg.steam_backlog_enforcer._hltb_types" _TYPES = "steam_backlog_enforcer._hltb_types"
_CMD = "python_pkg.steam_backlog_enforcer._cmd_done" _CMD = "steam_backlog_enforcer._cmd_done"
_SCAN = "python_pkg.steam_backlog_enforcer.scanning" _SCAN = "steam_backlog_enforcer.scanning"
_SCANCONF = "python_pkg.steam_backlog_enforcer._scanning_confidence" _SCANCONF = "steam_backlog_enforcer._scanning_confidence"
def _state(finished: list[int], current: int | None = None) -> State: def _state(finished: list[int], current: int | None = None) -> State:
@ -231,8 +231,8 @@ class TestScanningPollsIntegration:
def test_do_scan_kept_assignment_missing_game(self) -> None: def test_do_scan_kept_assignment_missing_game(self) -> None:
"""Covers scanning.py 110->116: current_app_id set but game absent.""" """Covers scanning.py 110->116: current_app_id set but game absent."""
from python_pkg.steam_backlog_enforcer.config import Config from steam_backlog_enforcer.config import Config
from python_pkg.steam_backlog_enforcer.scanning import do_scan from steam_backlog_enforcer.scanning import do_scan
other = GameInfo( other = GameInfo(
app_id=999, app_id=999,

View File

@ -9,7 +9,7 @@ from unittest.mock import AsyncMock, MagicMock, patch
import aiohttp import aiohttp
from python_pkg.steam_backlog_enforcer.protondb import ( from steam_backlog_enforcer.protondb import (
HTTP_NOT_FOUND, HTTP_NOT_FOUND,
ProtonDBRating, ProtonDBRating,
_fetch_batch, _fetch_batch,
@ -116,7 +116,7 @@ class TestProtonDBCache:
cache_file = tmp_path / "protondb_cache.json" cache_file = tmp_path / "protondb_cache.json"
cache_file.write_text(json.dumps({"440": {"tier": "gold"}}), encoding="utf-8") cache_file.write_text(json.dumps({"440": {"tier": "gold"}}), encoding="utf-8")
with patch( with patch(
"python_pkg.steam_backlog_enforcer.protondb.PROTONDB_CACHE_FILE", "steam_backlog_enforcer.protondb.PROTONDB_CACHE_FILE",
cache_file, cache_file,
): ):
result = _load_cache() result = _load_cache()
@ -125,7 +125,7 @@ class TestProtonDBCache:
def test_load_cache_missing(self, tmp_path: Path) -> None: def test_load_cache_missing(self, tmp_path: Path) -> None:
cache_file = tmp_path / "nonexistent.json" cache_file = tmp_path / "nonexistent.json"
with patch( with patch(
"python_pkg.steam_backlog_enforcer.protondb.PROTONDB_CACHE_FILE", "steam_backlog_enforcer.protondb.PROTONDB_CACHE_FILE",
cache_file, cache_file,
): ):
assert _load_cache() == {} assert _load_cache() == {}
@ -135,10 +135,10 @@ class TestProtonDBCache:
config_dir = tmp_path config_dir = tmp_path
with ( with (
patch( patch(
"python_pkg.steam_backlog_enforcer.protondb.PROTONDB_CACHE_FILE", "steam_backlog_enforcer.protondb.PROTONDB_CACHE_FILE",
cache_file, cache_file,
), ),
patch("python_pkg.steam_backlog_enforcer.protondb.CONFIG_DIR", config_dir), patch("steam_backlog_enforcer.protondb.CONFIG_DIR", config_dir),
): ):
_save_cache({"440": {"tier": "gold"}}) _save_cache({"440": {"tier": "gold"}})
assert cache_file.exists() assert cache_file.exists()
@ -231,7 +231,7 @@ class TestFetchBatch:
def test_returns_ratings(self) -> None: def test_returns_ratings(self) -> None:
rating = ProtonDBRating(app_id=440, tier="gold") rating = ProtonDBRating(app_id=440, tier="gold")
with patch( with patch(
"python_pkg.steam_backlog_enforcer.protondb._fetch_one", "steam_backlog_enforcer.protondb._fetch_one",
new_callable=AsyncMock, new_callable=AsyncMock,
return_value=rating, return_value=rating,
): ):
@ -251,7 +251,7 @@ class TestFetchBatch:
return rating if app_id == 440 else None return rating if app_id == 440 else None
with patch( with patch(
"python_pkg.steam_backlog_enforcer.protondb._fetch_one", "steam_backlog_enforcer.protondb._fetch_one",
side_effect=mock_fetch_one, side_effect=mock_fetch_one,
): ):
result = asyncio.run(_fetch_batch([440, 999])) result = asyncio.run(_fetch_batch([440, 999]))
@ -266,7 +266,7 @@ class TestFetchProtondbRatings:
cache_file = tmp_path / "protondb_cache.json" cache_file = tmp_path / "protondb_cache.json"
cache_file.write_text(json.dumps({"440": {"tier": "gold"}}), encoding="utf-8") cache_file.write_text(json.dumps({"440": {"tier": "gold"}}), encoding="utf-8")
with patch( with patch(
"python_pkg.steam_backlog_enforcer.protondb.PROTONDB_CACHE_FILE", "steam_backlog_enforcer.protondb.PROTONDB_CACHE_FILE",
cache_file, cache_file,
): ):
result = fetch_protondb_ratings([440]) result = fetch_protondb_ratings([440])
@ -278,12 +278,12 @@ class TestFetchProtondbRatings:
config_dir = tmp_path config_dir = tmp_path
with ( with (
patch( patch(
"python_pkg.steam_backlog_enforcer.protondb.PROTONDB_CACHE_FILE", "steam_backlog_enforcer.protondb.PROTONDB_CACHE_FILE",
cache_file, cache_file,
), ),
patch("python_pkg.steam_backlog_enforcer.protondb.CONFIG_DIR", config_dir), patch("steam_backlog_enforcer.protondb.CONFIG_DIR", config_dir),
patch( patch(
"python_pkg.steam_backlog_enforcer.protondb._fetch_batch", "steam_backlog_enforcer.protondb._fetch_batch",
return_value=[ProtonDBRating(app_id=440, tier="platinum")], return_value=[ProtonDBRating(app_id=440, tier="platinum")],
), ),
): ):

View File

@ -5,14 +5,14 @@ from __future__ import annotations
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
from unittest.mock import MagicMock, patch from unittest.mock import MagicMock, patch
from python_pkg.steam_backlog_enforcer.config import Config, State from steam_backlog_enforcer.config import Config, State
from python_pkg.steam_backlog_enforcer.protondb import ProtonDBRating from steam_backlog_enforcer.protondb import ProtonDBRating
from python_pkg.steam_backlog_enforcer.scanning import ( from steam_backlog_enforcer.scanning import (
_pick_playable_candidate, _pick_playable_candidate,
do_scan, do_scan,
pick_next_game, pick_next_game,
) )
from python_pkg.steam_backlog_enforcer.steam_api import GameInfo from steam_backlog_enforcer.steam_api import GameInfo
if TYPE_CHECKING: if TYPE_CHECKING:
from collections.abc import Callable from collections.abc import Callable
@ -56,24 +56,24 @@ class TestDoScan:
mock_client.build_game_list.side_effect = build_game_list mock_client.build_game_list.side_effect = build_game_list
with ( with (
patch( patch(
"python_pkg.steam_backlog_enforcer.scanning.SteamAPIClient", "steam_backlog_enforcer.scanning.SteamAPIClient",
return_value=mock_client, return_value=mock_client,
), ),
patch( patch(
"python_pkg.steam_backlog_enforcer.scanning.fetch_hltb_times_cached", "steam_backlog_enforcer.scanning.fetch_hltb_times_cached",
side_effect=lambda _games, progress_cb=None: ( side_effect=lambda _games, progress_cb=None: (
progress_cb(1, 1, 1, "TF2") if progress_cb else None, progress_cb(1, 1, 1, "TF2") if progress_cb else None,
{440: 20.0}, {440: 20.0},
)[1], )[1],
), ),
patch( patch(
"python_pkg.steam_backlog_enforcer.scanning.save_snapshot", "steam_backlog_enforcer.scanning.save_snapshot",
), ),
patch( patch(
"python_pkg.steam_backlog_enforcer.scanning.pick_next_game", "steam_backlog_enforcer.scanning.pick_next_game",
) as mock_pick, ) as mock_pick,
patch( patch(
"python_pkg.steam_backlog_enforcer.scanning._echo", "steam_backlog_enforcer.scanning._echo",
), ),
): ):
config = Config(steam_api_key="k", steam_id="i") config = Config(steam_api_key="k", steam_id="i")
@ -97,16 +97,16 @@ class TestDoScan:
mock_client.build_game_list.side_effect = build_game_list mock_client.build_game_list.side_effect = build_game_list
with ( with (
patch( patch(
"python_pkg.steam_backlog_enforcer.scanning.SteamAPIClient", "steam_backlog_enforcer.scanning.SteamAPIClient",
return_value=mock_client, return_value=mock_client,
), ),
patch( patch(
"python_pkg.steam_backlog_enforcer.scanning.save_snapshot", "steam_backlog_enforcer.scanning.save_snapshot",
), ),
patch( patch(
"python_pkg.steam_backlog_enforcer.scanning.pick_next_game", "steam_backlog_enforcer.scanning.pick_next_game",
) as mock_pick, ) as mock_pick,
patch("python_pkg.steam_backlog_enforcer.scanning._echo"), patch("steam_backlog_enforcer.scanning._echo"),
): ):
config = Config(steam_api_key="k", steam_id="i") config = Config(steam_api_key="k", steam_id="i")
state = State() state = State()
@ -120,20 +120,20 @@ class TestDoScan:
mock_client.build_game_list.return_value = [game] mock_client.build_game_list.return_value = [game]
with ( with (
patch( patch(
"python_pkg.steam_backlog_enforcer.scanning.SteamAPIClient", "steam_backlog_enforcer.scanning.SteamAPIClient",
return_value=mock_client, return_value=mock_client,
), ),
patch( patch(
"python_pkg.steam_backlog_enforcer.scanning.fetch_hltb_times_cached", "steam_backlog_enforcer.scanning.fetch_hltb_times_cached",
return_value={440: 20.0}, return_value={440: 20.0},
), ),
patch( patch(
"python_pkg.steam_backlog_enforcer.scanning.save_snapshot", "steam_backlog_enforcer.scanning.save_snapshot",
), ),
patch( patch(
"python_pkg.steam_backlog_enforcer.scanning.pick_next_game", "steam_backlog_enforcer.scanning.pick_next_game",
) as mock_pick, ) as mock_pick,
patch("python_pkg.steam_backlog_enforcer.scanning._echo"), patch("steam_backlog_enforcer.scanning._echo"),
): ):
config = Config(steam_api_key="k", steam_id="i") config = Config(steam_api_key="k", steam_id="i")
state = State(current_app_id=440) state = State(current_app_id=440)
@ -149,12 +149,12 @@ class TestPickPlayableCandidate:
game = _game(app_id=440, name="TF2") game = _game(app_id=440, name="TF2")
with ( with (
patch( patch(
"python_pkg.steam_backlog_enforcer.scanning.fetch_protondb_ratings", "steam_backlog_enforcer.scanning.fetch_protondb_ratings",
return_value={ return_value={
440: ProtonDBRating(app_id=440, tier="gold"), 440: ProtonDBRating(app_id=440, tier="gold"),
}, },
), ),
patch("python_pkg.steam_backlog_enforcer.scanning._echo"), patch("steam_backlog_enforcer.scanning._echo"),
): ):
result = _pick_playable_candidate([game]) result = _pick_playable_candidate([game])
assert result is not None assert result is not None
@ -165,13 +165,13 @@ class TestPickPlayableCandidate:
good = _game(app_id=2, name="Good") good = _game(app_id=2, name="Good")
with ( with (
patch( patch(
"python_pkg.steam_backlog_enforcer.scanning.fetch_protondb_ratings", "steam_backlog_enforcer.scanning.fetch_protondb_ratings",
return_value={ return_value={
1: ProtonDBRating(app_id=1, tier="borked"), 1: ProtonDBRating(app_id=1, tier="borked"),
2: ProtonDBRating(app_id=2, tier="platinum"), 2: ProtonDBRating(app_id=2, tier="platinum"),
}, },
), ),
patch("python_pkg.steam_backlog_enforcer.scanning._echo"), patch("steam_backlog_enforcer.scanning._echo"),
): ):
result = _pick_playable_candidate([bad, good]) result = _pick_playable_candidate([bad, good])
assert result is not None assert result is not None
@ -181,12 +181,12 @@ class TestPickPlayableCandidate:
game = _game(app_id=1, name="Bad") game = _game(app_id=1, name="Bad")
with ( with (
patch( patch(
"python_pkg.steam_backlog_enforcer.scanning.fetch_protondb_ratings", "steam_backlog_enforcer.scanning.fetch_protondb_ratings",
return_value={ return_value={
1: ProtonDBRating(app_id=1, tier="borked"), 1: ProtonDBRating(app_id=1, tier="borked"),
}, },
), ),
patch("python_pkg.steam_backlog_enforcer.scanning._echo"), patch("steam_backlog_enforcer.scanning._echo"),
): ):
assert _pick_playable_candidate([game]) is None assert _pick_playable_candidate([game]) is None
@ -198,12 +198,12 @@ class TestPickPlayableCandidate:
game = _game(app_id=440, name="TF2") game = _game(app_id=440, name="TF2")
with ( with (
patch( patch(
"python_pkg.steam_backlog_enforcer.scanning.fetch_protondb_ratings", "steam_backlog_enforcer.scanning.fetch_protondb_ratings",
return_value={ return_value={
440: ProtonDBRating(app_id=440, tier="platinum"), 440: ProtonDBRating(app_id=440, tier="platinum"),
}, },
), ),
patch("python_pkg.steam_backlog_enforcer.scanning._echo"), patch("steam_backlog_enforcer.scanning._echo"),
): ):
result = _pick_playable_candidate([game]) result = _pick_playable_candidate([game])
assert result is not None assert result is not None
@ -219,17 +219,17 @@ class TestPickNextGame:
state = State() state = State()
with ( with (
patch( patch(
"python_pkg.steam_backlog_enforcer.scanning._pick_playable_candidate", "steam_backlog_enforcer.scanning._pick_playable_candidate",
side_effect=lambda c: c[0] if c else None, side_effect=lambda c: c[0] if c else None,
), ),
patch("python_pkg.steam_backlog_enforcer.scanning._echo"), patch("steam_backlog_enforcer.scanning._echo"),
patch("python_pkg.steam_backlog_enforcer._scanning_confidence._echo"), patch("steam_backlog_enforcer._scanning_confidence._echo"),
patch( patch(
"python_pkg.steam_backlog_enforcer.scanning.is_game_installed", "steam_backlog_enforcer.scanning.is_game_installed",
return_value=True, return_value=True,
), ),
patch( patch(
"python_pkg.steam_backlog_enforcer.scanning.uninstall_other_games", "steam_backlog_enforcer.scanning.uninstall_other_games",
return_value=0, return_value=0,
), ),
patch("builtins.input", return_value="1"), patch("builtins.input", return_value="1"),
@ -241,7 +241,7 @@ class TestPickNextGame:
g1 = _game(app_id=1, total=5, unlocked=5) g1 = _game(app_id=1, total=5, unlocked=5)
config = Config(steam_api_key="k", steam_id="i") config = Config(steam_api_key="k", steam_id="i")
state = State() state = State()
with patch("python_pkg.steam_backlog_enforcer.scanning._echo"): with patch("steam_backlog_enforcer.scanning._echo"):
pick_next_game([g1], state, config) pick_next_game([g1], state, config)
assert state.current_app_id is None assert state.current_app_id is None
@ -252,16 +252,16 @@ class TestPickNextGame:
state = State(finished_app_ids=[1]) state = State(finished_app_ids=[1])
with ( with (
patch( patch(
"python_pkg.steam_backlog_enforcer.scanning._pick_playable_candidate", "steam_backlog_enforcer.scanning._pick_playable_candidate",
side_effect=lambda c: c[0] if c else None, side_effect=lambda c: c[0] if c else None,
), ),
patch("python_pkg.steam_backlog_enforcer.scanning._echo"), patch("steam_backlog_enforcer.scanning._echo"),
patch( patch(
"python_pkg.steam_backlog_enforcer.scanning.is_game_installed", "steam_backlog_enforcer.scanning.is_game_installed",
return_value=True, return_value=True,
), ),
patch( patch(
"python_pkg.steam_backlog_enforcer.scanning.uninstall_other_games", "steam_backlog_enforcer.scanning.uninstall_other_games",
return_value=0, return_value=0,
), ),
patch("builtins.input", return_value="1"), patch("builtins.input", return_value="1"),
@ -275,10 +275,10 @@ class TestPickNextGame:
state = State() state = State()
with ( with (
patch( patch(
"python_pkg.steam_backlog_enforcer.scanning._pick_playable_candidate", "steam_backlog_enforcer.scanning._pick_playable_candidate",
return_value=None, return_value=None,
), ),
patch("python_pkg.steam_backlog_enforcer.scanning._echo"), patch("steam_backlog_enforcer.scanning._echo"),
): ):
pick_next_game([g1], state, config) pick_next_game([g1], state, config)
assert state.current_app_id is None assert state.current_app_id is None
@ -289,17 +289,17 @@ class TestPickNextGame:
state = State() state = State()
with ( with (
patch( patch(
"python_pkg.steam_backlog_enforcer.scanning._pick_playable_candidate", "steam_backlog_enforcer.scanning._pick_playable_candidate",
side_effect=lambda c: c[0] if c else None, side_effect=lambda c: c[0] if c else None,
), ),
patch("python_pkg.steam_backlog_enforcer.scanning._echo"), patch("steam_backlog_enforcer.scanning._echo"),
patch("python_pkg.steam_backlog_enforcer._scanning_confidence._echo"), patch("steam_backlog_enforcer._scanning_confidence._echo"),
patch( patch(
"python_pkg.steam_backlog_enforcer.scanning.uninstall_other_games", "steam_backlog_enforcer.scanning.uninstall_other_games",
return_value=2, return_value=2,
), ),
patch( patch(
"python_pkg.steam_backlog_enforcer.scanning.is_game_installed", "steam_backlog_enforcer.scanning.is_game_installed",
return_value=True, return_value=True,
), ),
patch("builtins.input", return_value="1"), patch("builtins.input", return_value="1"),
@ -313,17 +313,17 @@ class TestPickNextGame:
state = State() state = State()
with ( with (
patch( patch(
"python_pkg.steam_backlog_enforcer.scanning._pick_playable_candidate", "steam_backlog_enforcer.scanning._pick_playable_candidate",
side_effect=lambda c: c[0] if c else None, side_effect=lambda c: c[0] if c else None,
), ),
patch("python_pkg.steam_backlog_enforcer.scanning._echo"), patch("steam_backlog_enforcer.scanning._echo"),
patch("python_pkg.steam_backlog_enforcer._scanning_confidence._echo"), patch("steam_backlog_enforcer._scanning_confidence._echo"),
patch( patch(
"python_pkg.steam_backlog_enforcer.scanning.is_game_installed", "steam_backlog_enforcer.scanning.is_game_installed",
return_value=False, return_value=False,
), ),
patch( patch(
"python_pkg.steam_backlog_enforcer.scanning.install_game" "steam_backlog_enforcer.scanning.install_game"
) as mock_install, ) as mock_install,
patch("builtins.input", return_value="1"), patch("builtins.input", return_value="1"),
): ):
@ -337,16 +337,16 @@ class TestPickNextGame:
state = State() state = State()
with ( with (
patch( patch(
"python_pkg.steam_backlog_enforcer.scanning._pick_playable_candidate", "steam_backlog_enforcer.scanning._pick_playable_candidate",
side_effect=lambda c: c[0] if c else None, side_effect=lambda c: c[0] if c else None,
), ),
patch("python_pkg.steam_backlog_enforcer.scanning._echo"), patch("steam_backlog_enforcer.scanning._echo"),
patch( patch(
"python_pkg.steam_backlog_enforcer.scanning.is_game_installed", "steam_backlog_enforcer.scanning.is_game_installed",
return_value=True, return_value=True,
), ),
patch( patch(
"python_pkg.steam_backlog_enforcer.scanning.uninstall_other_games", "steam_backlog_enforcer.scanning.uninstall_other_games",
return_value=0, return_value=0,
), ),
patch("builtins.input", return_value="1"), patch("builtins.input", return_value="1"),
@ -361,16 +361,16 @@ class TestPickNextGame:
state = State() state = State()
with ( with (
patch( patch(
"python_pkg.steam_backlog_enforcer.scanning._pick_playable_candidate", "steam_backlog_enforcer.scanning._pick_playable_candidate",
side_effect=lambda c: c[0] if c else None, side_effect=lambda c: c[0] if c else None,
), ),
patch("python_pkg.steam_backlog_enforcer.scanning._echo"), patch("steam_backlog_enforcer.scanning._echo"),
patch( patch(
"python_pkg.steam_backlog_enforcer.scanning.is_game_installed", "steam_backlog_enforcer.scanning.is_game_installed",
return_value=True, return_value=True,
), ),
patch( patch(
"python_pkg.steam_backlog_enforcer.scanning.uninstall_other_games", "steam_backlog_enforcer.scanning.uninstall_other_games",
return_value=0, return_value=0,
), ),
patch("builtins.input", return_value="1"), patch("builtins.input", return_value="1"),
@ -390,23 +390,23 @@ class TestPickNextGame:
state = State() state = State()
with ( with (
patch( patch(
"python_pkg.steam_backlog_enforcer.scanning._pick_playable_candidate", "steam_backlog_enforcer.scanning._pick_playable_candidate",
side_effect=lambda c: c[0] if c else None, side_effect=lambda c: c[0] if c else None,
), ),
patch( patch(
"python_pkg.steam_backlog_enforcer.scanning._echo", "steam_backlog_enforcer.scanning._echo",
side_effect=lambda *a, **_: echoed.append(a[0]), side_effect=lambda *a, **_: echoed.append(a[0]),
), ),
patch( patch(
"python_pkg.steam_backlog_enforcer._scanning_confidence._echo", "steam_backlog_enforcer._scanning_confidence._echo",
side_effect=lambda *a, **_: echoed.append(a[0]), side_effect=lambda *a, **_: echoed.append(a[0]),
), ),
patch( patch(
"python_pkg.steam_backlog_enforcer.scanning.is_game_installed", "steam_backlog_enforcer.scanning.is_game_installed",
return_value=True, return_value=True,
), ),
patch( patch(
"python_pkg.steam_backlog_enforcer.scanning.uninstall_other_games", "steam_backlog_enforcer.scanning.uninstall_other_games",
return_value=0, return_value=0,
), ),
patch("builtins.input", return_value="1"), patch("builtins.input", return_value="1"),
@ -428,15 +428,15 @@ class TestPickNextGame:
state = State() state = State()
with ( with (
patch( patch(
"python_pkg.steam_backlog_enforcer.scanning._echo", "steam_backlog_enforcer.scanning._echo",
side_effect=lambda *a, **_: echoed.append(a[0]), side_effect=lambda *a, **_: echoed.append(a[0]),
), ),
patch( patch(
"python_pkg.steam_backlog_enforcer._scanning_confidence._echo", "steam_backlog_enforcer._scanning_confidence._echo",
side_effect=lambda *a, **_: echoed.append(a[0]), side_effect=lambda *a, **_: echoed.append(a[0]),
), ),
patch( patch(
"python_pkg.steam_backlog_enforcer.scanning._pick_playable_candidate", "steam_backlog_enforcer.scanning._pick_playable_candidate",
return_value=None, return_value=None,
) as mock_pick, ) as mock_pick,
): ):

View File

@ -5,13 +5,13 @@ from __future__ import annotations
from typing import Any from typing import Any
from unittest.mock import MagicMock, patch from unittest.mock import MagicMock, patch
from python_pkg.steam_backlog_enforcer.config import Config, State from steam_backlog_enforcer.config import Config, State
from python_pkg.steam_backlog_enforcer.scanning import ( from steam_backlog_enforcer.scanning import (
_check_game_tampering, _check_game_tampering,
detect_tampering, detect_tampering,
) )
PKG = "python_pkg.steam_backlog_enforcer.scanning" PKG = "steam_backlog_enforcer.scanning"
def _entry( def _entry(

View File

@ -5,9 +5,9 @@ from __future__ import annotations
import contextlib import contextlib
from unittest.mock import patch from unittest.mock import patch
from python_pkg.steam_backlog_enforcer.config import Config, State from steam_backlog_enforcer.config import Config, State
from python_pkg.steam_backlog_enforcer.scanning import pick_next_game from steam_backlog_enforcer.scanning import pick_next_game
from python_pkg.steam_backlog_enforcer.steam_api import GameInfo from steam_backlog_enforcer.steam_api import GameInfo
def _game( def _game(
@ -50,27 +50,27 @@ class TestPickNextGame:
with ( with (
patch( patch(
"python_pkg.steam_backlog_enforcer._scanning_confidence._refresh_candidate_confidence", "steam_backlog_enforcer._scanning_confidence._refresh_candidate_confidence",
side_effect=refresh_side_effect, side_effect=refresh_side_effect,
) as mock_refresh, ) as mock_refresh,
patch( patch(
"python_pkg.steam_backlog_enforcer.scanning._pick_playable_candidate", "steam_backlog_enforcer.scanning._pick_playable_candidate",
side_effect=lambda c: c[0] if c else None, side_effect=lambda c: c[0] if c else None,
), ),
patch( patch(
"python_pkg.steam_backlog_enforcer.scanning._echo", "steam_backlog_enforcer.scanning._echo",
side_effect=lambda *a, **_: echoed.append(a[0]), side_effect=lambda *a, **_: echoed.append(a[0]),
), ),
patch( patch(
"python_pkg.steam_backlog_enforcer._scanning_confidence._echo", "steam_backlog_enforcer._scanning_confidence._echo",
side_effect=lambda *a, **_: echoed.append(a[0]), side_effect=lambda *a, **_: echoed.append(a[0]),
), ),
patch( patch(
"python_pkg.steam_backlog_enforcer.scanning.is_game_installed", "steam_backlog_enforcer.scanning.is_game_installed",
return_value=True, return_value=True,
), ),
patch( patch(
"python_pkg.steam_backlog_enforcer.scanning.uninstall_other_games", "steam_backlog_enforcer.scanning.uninstall_other_games",
return_value=0, return_value=0,
), ),
patch("builtins.input", return_value="1"), patch("builtins.input", return_value="1"),
@ -93,19 +93,19 @@ class TestPickNextGame:
with ( with (
patch( patch(
"python_pkg.steam_backlog_enforcer._scanning_confidence._refresh_candidate_confidence_batch" "steam_backlog_enforcer._scanning_confidence._refresh_candidate_confidence_batch"
) as mock_refresh_batch, ) as mock_refresh_batch,
patch( patch(
"python_pkg.steam_backlog_enforcer.scanning._pick_playable_candidate", "steam_backlog_enforcer.scanning._pick_playable_candidate",
side_effect=lambda c: c[0] if c else None, side_effect=lambda c: c[0] if c else None,
), ),
patch("python_pkg.steam_backlog_enforcer.scanning._echo"), patch("steam_backlog_enforcer.scanning._echo"),
patch( patch(
"python_pkg.steam_backlog_enforcer.scanning.is_game_installed", "steam_backlog_enforcer.scanning.is_game_installed",
return_value=True, return_value=True,
), ),
patch( patch(
"python_pkg.steam_backlog_enforcer.scanning.uninstall_other_games", "steam_backlog_enforcer.scanning.uninstall_other_games",
return_value=0, return_value=0,
), ),
patch("builtins.input", return_value="1"), patch("builtins.input", return_value="1"),
@ -131,27 +131,27 @@ class TestPickNextGame:
with ( with (
patch( patch(
"python_pkg.steam_backlog_enforcer._scanning_confidence.load_hltb_polls_cache", "steam_backlog_enforcer._scanning_confidence.load_hltb_polls_cache",
return_value={1: 1, 2: 3}, return_value={1: 1, 2: 3},
), ),
patch( patch(
"python_pkg.steam_backlog_enforcer._scanning_confidence.load_hltb_count_comp_cache", "steam_backlog_enforcer._scanning_confidence.load_hltb_count_comp_cache",
return_value={1: 8, 2: 20}, return_value={1: 8, 2: 20},
), ),
patch( patch(
"python_pkg.steam_backlog_enforcer._scanning_confidence._refresh_candidate_confidence_batch" "steam_backlog_enforcer._scanning_confidence._refresh_candidate_confidence_batch"
) as mock_refresh_batch, ) as mock_refresh_batch,
patch( patch(
"python_pkg.steam_backlog_enforcer.scanning._pick_playable_candidate", "steam_backlog_enforcer.scanning._pick_playable_candidate",
side_effect=lambda c: c[0] if c else None, side_effect=lambda c: c[0] if c else None,
), ),
patch("python_pkg.steam_backlog_enforcer.scanning._echo"), patch("steam_backlog_enforcer.scanning._echo"),
patch( patch(
"python_pkg.steam_backlog_enforcer.scanning.is_game_installed", "steam_backlog_enforcer.scanning.is_game_installed",
return_value=True, return_value=True,
), ),
patch( patch(
"python_pkg.steam_backlog_enforcer.scanning.uninstall_other_games", "steam_backlog_enforcer.scanning.uninstall_other_games",
return_value=0, return_value=0,
), ),
patch("builtins.input", return_value="1"), patch("builtins.input", return_value="1"),
@ -178,16 +178,16 @@ class TestPickNextGame:
with ( with (
patch( patch(
"python_pkg.steam_backlog_enforcer.scanning._pick_playable_candidate", "steam_backlog_enforcer.scanning._pick_playable_candidate",
side_effect=playable_side_effect, side_effect=playable_side_effect,
), ),
patch("python_pkg.steam_backlog_enforcer.scanning._echo"), patch("steam_backlog_enforcer.scanning._echo"),
patch( patch(
"python_pkg.steam_backlog_enforcer.scanning.is_game_installed", "steam_backlog_enforcer.scanning.is_game_installed",
return_value=True, return_value=True,
), ),
patch( patch(
"python_pkg.steam_backlog_enforcer.scanning.uninstall_other_games", "steam_backlog_enforcer.scanning.uninstall_other_games",
return_value=0, return_value=0,
), ),
patch("builtins.input", return_value="1"), patch("builtins.input", return_value="1"),
@ -205,16 +205,16 @@ class TestPickNextGame:
state = State() state = State()
with ( with (
patch( patch(
"python_pkg.steam_backlog_enforcer.scanning._pick_playable_candidate", "steam_backlog_enforcer.scanning._pick_playable_candidate",
side_effect=lambda c: c[0] if c else None, side_effect=lambda c: c[0] if c else None,
), ),
patch("python_pkg.steam_backlog_enforcer.scanning._echo"), patch("steam_backlog_enforcer.scanning._echo"),
patch( patch(
"python_pkg.steam_backlog_enforcer.scanning.is_game_installed", "steam_backlog_enforcer.scanning.is_game_installed",
return_value=True, return_value=True,
), ),
patch( patch(
"python_pkg.steam_backlog_enforcer.scanning.uninstall_other_games", "steam_backlog_enforcer.scanning.uninstall_other_games",
return_value=0, return_value=0,
), ),
patch("builtins.input", return_value="2"), patch("builtins.input", return_value="2"),
@ -230,19 +230,19 @@ class TestPickNextGame:
echoed: list[str] = [] echoed: list[str] = []
with ( with (
patch( patch(
"python_pkg.steam_backlog_enforcer.scanning._pick_playable_candidate", "steam_backlog_enforcer.scanning._pick_playable_candidate",
side_effect=lambda c: c[0] if c else None, side_effect=lambda c: c[0] if c else None,
), ),
patch( patch(
"python_pkg.steam_backlog_enforcer.scanning._echo", "steam_backlog_enforcer.scanning._echo",
side_effect=lambda *a, **_: echoed.append(a[0]), side_effect=lambda *a, **_: echoed.append(a[0]),
), ),
patch( patch(
"python_pkg.steam_backlog_enforcer.scanning.is_game_installed", "steam_backlog_enforcer.scanning.is_game_installed",
return_value=True, return_value=True,
), ),
patch( patch(
"python_pkg.steam_backlog_enforcer.scanning.uninstall_other_games", "steam_backlog_enforcer.scanning.uninstall_other_games",
return_value=0, return_value=0,
), ),
patch("builtins.input", side_effect=["abc", "1"]), patch("builtins.input", side_effect=["abc", "1"]),
@ -259,19 +259,19 @@ class TestPickNextGame:
echoed: list[str] = [] echoed: list[str] = []
with ( with (
patch( patch(
"python_pkg.steam_backlog_enforcer.scanning._pick_playable_candidate", "steam_backlog_enforcer.scanning._pick_playable_candidate",
side_effect=lambda c: c[0] if c else None, side_effect=lambda c: c[0] if c else None,
), ),
patch( patch(
"python_pkg.steam_backlog_enforcer.scanning._echo", "steam_backlog_enforcer.scanning._echo",
side_effect=lambda *a, **_: echoed.append(a[0]), side_effect=lambda *a, **_: echoed.append(a[0]),
), ),
patch( patch(
"python_pkg.steam_backlog_enforcer.scanning.is_game_installed", "steam_backlog_enforcer.scanning.is_game_installed",
return_value=True, return_value=True,
), ),
patch( patch(
"python_pkg.steam_backlog_enforcer.scanning.uninstall_other_games", "steam_backlog_enforcer.scanning.uninstall_other_games",
return_value=0, return_value=0,
), ),
patch("builtins.input", side_effect=["99", "1"]), patch("builtins.input", side_effect=["99", "1"]),
@ -289,30 +289,30 @@ class TestPickNextGameSequential:
stack = contextlib.ExitStack() stack = contextlib.ExitStack()
stack.enter_context( stack.enter_context(
patch( patch(
"python_pkg.steam_backlog_enforcer.scanning._pick_playable_candidate", "steam_backlog_enforcer.scanning._pick_playable_candidate",
side_effect=lambda c: c[0] if c else None, side_effect=lambda c: c[0] if c else None,
) )
) )
stack.enter_context( stack.enter_context(
patch( patch(
"python_pkg.steam_backlog_enforcer.scanning._echo", "steam_backlog_enforcer.scanning._echo",
side_effect=lambda *a, **_: echoed.append(a[0]), side_effect=lambda *a, **_: echoed.append(a[0]),
) )
) )
stack.enter_context( stack.enter_context(
patch( patch(
"python_pkg.steam_backlog_enforcer.scanning.is_game_installed", "steam_backlog_enforcer.scanning.is_game_installed",
return_value=True, return_value=True,
) )
) )
stack.enter_context( stack.enter_context(
patch( patch(
"python_pkg.steam_backlog_enforcer.scanning.uninstall_other_games", "steam_backlog_enforcer.scanning.uninstall_other_games",
return_value=0, return_value=0,
) )
) )
stack.enter_context( stack.enter_context(
patch("python_pkg.steam_backlog_enforcer.config._atomic_write") patch("steam_backlog_enforcer.config._atomic_write")
) )
return stack return stack
@ -363,14 +363,14 @@ class TestPickNextGameSequential:
echoed: list[str] = [] echoed: list[str] = []
with ( with (
patch( patch(
"python_pkg.steam_backlog_enforcer.scanning._pick_playable_candidate", "steam_backlog_enforcer.scanning._pick_playable_candidate",
return_value=None, return_value=None,
), ),
patch( patch(
"python_pkg.steam_backlog_enforcer.scanning._echo", "steam_backlog_enforcer.scanning._echo",
side_effect=lambda *a, **_: echoed.append(a[0]), side_effect=lambda *a, **_: echoed.append(a[0]),
), ),
patch("python_pkg.steam_backlog_enforcer.config._atomic_write"), patch("steam_backlog_enforcer.config._atomic_write"),
): ):
pick_next_game([g1], state, config, on_select=lambda _g: True) pick_next_game([g1], state, config, on_select=lambda _g: True)
assert state.current_app_id is None assert state.current_app_id is None
@ -386,17 +386,17 @@ class TestPickNextGameSequential:
echoed: list[str] = [] echoed: list[str] = []
with ( with (
patch( patch(
"python_pkg.steam_backlog_enforcer.scanning._pick_playable_candidate", "steam_backlog_enforcer.scanning._pick_playable_candidate",
side_effect=lambda c: c[0] if c else None, side_effect=lambda c: c[0] if c else None,
), ),
patch( patch(
"python_pkg.steam_backlog_enforcer.scanning._echo", "steam_backlog_enforcer.scanning._echo",
side_effect=lambda *a, **_: echoed.append(a[0]), side_effect=lambda *a, **_: echoed.append(a[0]),
), ),
patch( patch(
"python_pkg.steam_backlog_enforcer._scanning_confidence._refresh_candidate_confidence", "steam_backlog_enforcer._scanning_confidence._refresh_candidate_confidence",
), ),
patch("python_pkg.steam_backlog_enforcer.config._atomic_write"), patch("steam_backlog_enforcer.config._atomic_write"),
): ):
pick_next_game([g1], state, config, on_select=lambda _g: True) pick_next_game([g1], state, config, on_select=lambda _g: True)
assert state.current_app_id is None assert state.current_app_id is None

View File

@ -4,18 +4,18 @@ from __future__ import annotations
from unittest.mock import MagicMock, patch from unittest.mock import MagicMock, patch
from python_pkg.steam_backlog_enforcer._scanning_confidence import ( from steam_backlog_enforcer._scanning_confidence import (
_filter_hltb_confident_candidates, _filter_hltb_confident_candidates,
_force_refresh_candidate_confidence, _force_refresh_candidate_confidence,
_refresh_candidate_confidence_batch, _refresh_candidate_confidence_batch,
) )
from python_pkg.steam_backlog_enforcer.config import Config, State from steam_backlog_enforcer.config import Config, State
from python_pkg.steam_backlog_enforcer.scanning import ( from steam_backlog_enforcer.scanning import (
_collect_top_candidates, _collect_top_candidates,
_pick_next_shortest_candidate, _pick_next_shortest_candidate,
do_check, do_check,
) )
from python_pkg.steam_backlog_enforcer.steam_api import GameInfo from steam_backlog_enforcer.steam_api import GameInfo
def _game( def _game(
@ -44,7 +44,7 @@ class TestCollectTopCandidates:
"""Returns at most n qualified candidates.""" """Returns at most n qualified candidates."""
games = [_game(app_id=i, name=f"G{i}", hours=float(i)) for i in range(1, 6)] games = [_game(app_id=i, name=f"G{i}", hours=float(i)) for i in range(1, 6)]
with patch( with patch(
"python_pkg.steam_backlog_enforcer.scanning._pick_playable_candidate", "steam_backlog_enforcer.scanning._pick_playable_candidate",
side_effect=lambda c: c[0] if c else None, side_effect=lambda c: c[0] if c else None,
): ):
qualified, conf_skip, linux_skip = _collect_top_candidates(games, n=3) qualified, conf_skip, linux_skip = _collect_top_candidates(games, n=3)
@ -59,10 +59,10 @@ class TestCollectTopCandidates:
g2 = _game(app_id=2, name="Good", hours=2.0) g2 = _game(app_id=2, name="Good", hours=2.0)
with ( with (
patch( patch(
"python_pkg.steam_backlog_enforcer.scanning._pick_playable_candidate", "steam_backlog_enforcer.scanning._pick_playable_candidate",
side_effect=lambda c: None if c[0].app_id == 1 else c[0], side_effect=lambda c: None if c[0].app_id == 1 else c[0],
), ),
patch("python_pkg.steam_backlog_enforcer.scanning._echo"), patch("steam_backlog_enforcer.scanning._echo"),
): ):
qualified, conf_skip, linux_skip = _collect_top_candidates([g1, g2], n=10) qualified, conf_skip, linux_skip = _collect_top_candidates([g1, g2], n=10)
assert [g.app_id for g in qualified] == [2] assert [g.app_id for g in qualified] == [2]
@ -80,10 +80,10 @@ class TestCollectTopCandidates:
g = _game(app_id=1, name="Good", hours=1.0) g = _game(app_id=1, name="Good", hours=1.0)
with ( with (
patch( patch(
"python_pkg.steam_backlog_enforcer.scanning._pick_playable_candidate", "steam_backlog_enforcer.scanning._pick_playable_candidate",
side_effect=lambda c: c[0] if c else None, side_effect=lambda c: c[0] if c else None,
), ),
patch("python_pkg.steam_backlog_enforcer.scanning._echo") as mock_echo, patch("steam_backlog_enforcer.scanning._echo") as mock_echo,
): ):
_collect_top_candidates([g], n=10) _collect_top_candidates([g], n=10)
mock_echo.assert_not_called() mock_echo.assert_not_called()
@ -93,7 +93,7 @@ class TestDoCheck:
"""Tests for do_check.""" """Tests for do_check."""
def test_no_assignment(self) -> None: def test_no_assignment(self) -> None:
with patch("python_pkg.steam_backlog_enforcer.scanning._echo") as mock_echo: with patch("steam_backlog_enforcer.scanning._echo") as mock_echo:
do_check(Config(), State()) do_check(Config(), State())
mock_echo.assert_called() mock_echo.assert_called()
@ -102,11 +102,11 @@ class TestDoCheck:
mock_client.refresh_single_game.return_value = None mock_client.refresh_single_game.return_value = None
with ( with (
patch( patch(
"python_pkg.steam_backlog_enforcer.scanning.SteamAPIClient", "steam_backlog_enforcer.scanning.SteamAPIClient",
return_value=mock_client, return_value=mock_client,
), ),
patch("python_pkg.steam_backlog_enforcer.scanning._echo"), patch("steam_backlog_enforcer.scanning._echo"),
patch("python_pkg.steam_backlog_enforcer.scanning.detect_tampering"), patch("steam_backlog_enforcer.scanning.detect_tampering"),
): ):
state = State(current_app_id=440, current_game_name="TF2") state = State(current_app_id=440, current_game_name="TF2")
do_check(Config(steam_api_key="k", steam_id="i"), state) do_check(Config(steam_api_key="k", steam_id="i"), state)
@ -118,7 +118,7 @@ class TestConfidenceHelpers:
def test_force_refresh_candidate_confidence_delegates(self) -> None: def test_force_refresh_candidate_confidence_delegates(self) -> None:
game = _game(app_id=10, name="A") game = _game(app_id=10, name="A")
with patch( with patch(
"python_pkg.steam_backlog_enforcer._scanning_confidence._refresh_candidate_confidence_batch", "steam_backlog_enforcer._scanning_confidence._refresh_candidate_confidence_batch",
) as mock_batch: ) as mock_batch:
_force_refresh_candidate_confidence(game) _force_refresh_candidate_confidence(game)
mock_batch.assert_called_once_with([game], force=True) mock_batch.assert_called_once_with([game], force=True)
@ -128,7 +128,7 @@ class TestConfidenceHelpers:
game.comp_100_count = 3 game.comp_100_count = 3
game.count_comp = 15 game.count_comp = 15
with patch( with patch(
"python_pkg.steam_backlog_enforcer._scanning_confidence.fetch_hltb_confidence_cached", "steam_backlog_enforcer._scanning_confidence.fetch_hltb_confidence_cached",
) as mock_fetch: ) as mock_fetch:
_refresh_candidate_confidence_batch([game], force=False) _refresh_candidate_confidence_batch([game], force=False)
mock_fetch.assert_not_called() mock_fetch.assert_not_called()
@ -139,23 +139,23 @@ class TestConfidenceHelpers:
game.count_comp = 0 game.count_comp = 0
with ( with (
patch( patch(
"python_pkg.steam_backlog_enforcer._scanning_confidence.load_hltb_cache", "steam_backlog_enforcer._scanning_confidence.load_hltb_cache",
side_effect=[{30: 9.5}, {30: -1.0}], side_effect=[{30: 9.5}, {30: -1.0}],
), ),
patch( patch(
"python_pkg.steam_backlog_enforcer._scanning_confidence.load_hltb_polls_cache", "steam_backlog_enforcer._scanning_confidence.load_hltb_polls_cache",
return_value={30: 0}, return_value={30: 0},
), ),
patch( patch(
"python_pkg.steam_backlog_enforcer._scanning_confidence.load_hltb_count_comp_cache", "steam_backlog_enforcer._scanning_confidence.load_hltb_count_comp_cache",
return_value={30: 0}, return_value={30: 0},
), ),
patch( patch(
"python_pkg.steam_backlog_enforcer._scanning_confidence.fetch_hltb_confidence_cached", "steam_backlog_enforcer._scanning_confidence.fetch_hltb_confidence_cached",
return_value={30: -1.0}, return_value={30: -1.0},
), ),
patch( patch(
"python_pkg.steam_backlog_enforcer._scanning_confidence.save_hltb_cache", "steam_backlog_enforcer._scanning_confidence.save_hltb_cache",
) as mock_save, ) as mock_save,
): ):
_refresh_candidate_confidence_batch([game], force=True) _refresh_candidate_confidence_batch([game], force=True)
@ -170,10 +170,10 @@ class TestConfidenceHelpers:
low.count_comp = 2 low.count_comp = 2
with ( with (
patch( patch(
"python_pkg.steam_backlog_enforcer._scanning_confidence._refresh_candidate_confidence_batch", "steam_backlog_enforcer._scanning_confidence._refresh_candidate_confidence_batch",
), ),
patch( patch(
"python_pkg.steam_backlog_enforcer._scanning_confidence._echo" "steam_backlog_enforcer._scanning_confidence._echo"
) as mock_echo, ) as mock_echo,
): ):
result = _filter_hltb_confident_candidates([low]) result = _filter_hltb_confident_candidates([low])
@ -190,10 +190,10 @@ class TestConfidenceHelpers:
with ( with (
patch( patch(
"python_pkg.steam_backlog_enforcer.scanning._pick_playable_candidate", "steam_backlog_enforcer.scanning._pick_playable_candidate",
side_effect=[None, good], side_effect=[None, good],
), ),
patch("python_pkg.steam_backlog_enforcer.scanning._echo") as mock_echo, patch("steam_backlog_enforcer.scanning._echo") as mock_echo,
): ):
picked, skipped_low_conf, skipped_linux = _pick_next_shortest_candidate( picked, skipped_low_conf, skipped_linux = _pick_next_shortest_candidate(
[bad, good], [bad, good],
@ -214,10 +214,10 @@ class TestConfidenceHelpers:
good = _game(app_id=51, name="Good", hours=2.0) good = _game(app_id=51, name="Good", hours=2.0)
with ( with (
patch( patch(
"python_pkg.steam_backlog_enforcer.scanning._pick_playable_candidate", "steam_backlog_enforcer.scanning._pick_playable_candidate",
return_value=good, return_value=good,
), ),
patch("python_pkg.steam_backlog_enforcer.scanning._echo") as mock_echo, patch("steam_backlog_enforcer.scanning._echo") as mock_echo,
): ):
picked, _skipped_low_conf, skipped_linux = _pick_next_shortest_candidate( picked, _skipped_low_conf, skipped_linux = _pick_next_shortest_candidate(
[good], [good],
@ -233,9 +233,9 @@ class TestConfidenceHelpers:
low_conf.count_comp = 0 low_conf.count_comp = 0
with ( with (
patch( patch(
"python_pkg.steam_backlog_enforcer._scanning_confidence._refresh_candidate_confidence" "steam_backlog_enforcer._scanning_confidence._refresh_candidate_confidence"
), ),
patch("python_pkg.steam_backlog_enforcer.scanning._echo"), patch("steam_backlog_enforcer.scanning._echo"),
): ):
picked, skipped_low_conf, skipped_linux = _pick_next_shortest_candidate( picked, skipped_low_conf, skipped_linux = _pick_next_shortest_candidate(
[low_conf], [low_conf],
@ -249,10 +249,10 @@ class TestConfidenceHelpers:
g1 = _game(app_id=10, name="Borked", hours=1.0) g1 = _game(app_id=10, name="Borked", hours=1.0)
with ( with (
patch( patch(
"python_pkg.steam_backlog_enforcer.scanning._pick_playable_candidate", "steam_backlog_enforcer.scanning._pick_playable_candidate",
return_value=None, return_value=None,
), ),
patch("python_pkg.steam_backlog_enforcer.scanning._echo") as mock_echo, patch("steam_backlog_enforcer.scanning._echo") as mock_echo,
): ):
picked, _skipped_low_conf, skipped_linux = _pick_next_shortest_candidate( picked, _skipped_low_conf, skipped_linux = _pick_next_shortest_candidate(
[g1], [g1],
@ -270,21 +270,21 @@ class TestConfidenceHelpers:
snap = [game.to_snapshot()] snap = [game.to_snapshot()]
with ( with (
patch( patch(
"python_pkg.steam_backlog_enforcer.scanning.SteamAPIClient", "steam_backlog_enforcer.scanning.SteamAPIClient",
return_value=mock_client, return_value=mock_client,
), ),
patch("python_pkg.steam_backlog_enforcer.scanning._echo"), patch("steam_backlog_enforcer.scanning._echo"),
patch( patch(
"python_pkg.steam_backlog_enforcer.scanning.send_notification", "steam_backlog_enforcer.scanning.send_notification",
), ),
patch( patch(
"python_pkg.steam_backlog_enforcer.scanning.load_snapshot", "steam_backlog_enforcer.scanning.load_snapshot",
return_value=snap, return_value=snap,
), ),
patch( patch(
"python_pkg.steam_backlog_enforcer.scanning.pick_next_game", "steam_backlog_enforcer.scanning.pick_next_game",
), ),
patch("python_pkg.steam_backlog_enforcer.scanning.detect_tampering"), patch("steam_backlog_enforcer.scanning.detect_tampering"),
): ):
state = State(current_app_id=440, current_game_name="TF2") state = State(current_app_id=440, current_game_name="TF2")
do_check(Config(steam_api_key="k", steam_id="i"), state) do_check(Config(steam_api_key="k", steam_id="i"), state)
@ -296,18 +296,18 @@ class TestConfidenceHelpers:
mock_client.refresh_single_game.return_value = game mock_client.refresh_single_game.return_value = game
with ( with (
patch( patch(
"python_pkg.steam_backlog_enforcer.scanning.SteamAPIClient", "steam_backlog_enforcer.scanning.SteamAPIClient",
return_value=mock_client, return_value=mock_client,
), ),
patch("python_pkg.steam_backlog_enforcer.scanning._echo"), patch("steam_backlog_enforcer.scanning._echo"),
patch( patch(
"python_pkg.steam_backlog_enforcer.scanning.send_notification", "steam_backlog_enforcer.scanning.send_notification",
), ),
patch( patch(
"python_pkg.steam_backlog_enforcer.scanning.load_snapshot", "steam_backlog_enforcer.scanning.load_snapshot",
return_value=None, return_value=None,
), ),
patch("python_pkg.steam_backlog_enforcer.scanning.detect_tampering"), patch("steam_backlog_enforcer.scanning.detect_tampering"),
): ):
state = State(current_app_id=440, current_game_name="TF2") state = State(current_app_id=440, current_game_name="TF2")
do_check(Config(steam_api_key="k", steam_id="i"), state) do_check(Config(steam_api_key="k", steam_id="i"), state)
@ -318,11 +318,11 @@ class TestConfidenceHelpers:
mock_client.refresh_single_game.return_value = game mock_client.refresh_single_game.return_value = game
with ( with (
patch( patch(
"python_pkg.steam_backlog_enforcer.scanning.SteamAPIClient", "steam_backlog_enforcer.scanning.SteamAPIClient",
return_value=mock_client, return_value=mock_client,
), ),
patch("python_pkg.steam_backlog_enforcer.scanning._echo"), patch("steam_backlog_enforcer.scanning._echo"),
patch("python_pkg.steam_backlog_enforcer.scanning.detect_tampering"), patch("steam_backlog_enforcer.scanning.detect_tampering"),
): ):
state = State(current_app_id=440, current_game_name="TF2") state = State(current_app_id=440, current_game_name="TF2")
do_check(Config(steam_api_key="k", steam_id="i"), state) do_check(Config(steam_api_key="k", steam_id="i"), state)

View File

@ -5,7 +5,7 @@ from __future__ import annotations
from datetime import datetime, timedelta, timezone from datetime import datetime, timedelta, timezone
from unittest.mock import patch from unittest.mock import patch
from python_pkg.steam_backlog_enforcer._stats import ( from steam_backlog_enforcer._stats import (
_ensure_rush_data, _ensure_rush_data,
_filter_qualifying_games, _filter_qualifying_games,
_format_completion_date, _format_completion_date,
@ -16,11 +16,11 @@ from python_pkg.steam_backlog_enforcer._stats import (
_sum_hours, _sum_hours,
cmd_stats, cmd_stats,
) )
from python_pkg.steam_backlog_enforcer.config import Config, State from steam_backlog_enforcer.config import Config, State
from python_pkg.steam_backlog_enforcer.protondb import ProtonDBRating from steam_backlog_enforcer.protondb import ProtonDBRating
from python_pkg.steam_backlog_enforcer.steam_api import GameInfo from steam_backlog_enforcer.steam_api import GameInfo
_PKG = "python_pkg.steam_backlog_enforcer._stats" _PKG = "steam_backlog_enforcer._stats"
def _game( def _game(

View File

@ -8,7 +8,7 @@ from unittest.mock import MagicMock, patch
import pytest import pytest
import requests import requests
from python_pkg.steam_backlog_enforcer.steam_api import ( from steam_backlog_enforcer.steam_api import (
AchievementInfo, AchievementInfo,
GameInfo, GameInfo,
SteamAPIClient, SteamAPIClient,
@ -163,7 +163,7 @@ class TestSteamAPIClient:
# Fill up the rate limit window # Fill up the rate limit window
client._request_times = [__import__("time").time()] * client._max_rps client._request_times = [__import__("time").time()] * client._max_rps
with patch( with patch(
"python_pkg.steam_backlog_enforcer.steam_api.time.sleep", "steam_backlog_enforcer.steam_api.time.sleep",
) as mock_sleep: ) as mock_sleep:
# Next call should trigger sleep then succeed # Next call should trigger sleep then succeed
client._rate_limit() client._rate_limit()

View File

@ -5,7 +5,7 @@ from __future__ import annotations
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
from unittest.mock import MagicMock, patch from unittest.mock import MagicMock, patch
from python_pkg.steam_backlog_enforcer.store_blocker import ( from steam_backlog_enforcer.store_blocker import (
_block_store_iptables, _block_store_iptables,
_block_via_hosts_install, _block_via_hosts_install,
_is_iptables_blocked, _is_iptables_blocked,
@ -27,7 +27,7 @@ class TestIsStoreBlocked:
hosts_file.write_text("0.0.0.0 store.steampowered.com\n", encoding="utf-8") hosts_file.write_text("0.0.0.0 store.steampowered.com\n", encoding="utf-8")
with ( with (
patch( patch(
"python_pkg.steam_backlog_enforcer.store_blocker.HOSTS_FILE", "steam_backlog_enforcer.store_blocker.HOSTS_FILE",
hosts_file, hosts_file,
), ),
): ):
@ -38,11 +38,11 @@ class TestIsStoreBlocked:
hosts_file.write_text("# 0.0.0.0 store.steampowered.com\n", encoding="utf-8") hosts_file.write_text("# 0.0.0.0 store.steampowered.com\n", encoding="utf-8")
with ( with (
patch( patch(
"python_pkg.steam_backlog_enforcer.store_blocker.HOSTS_FILE", "steam_backlog_enforcer.store_blocker.HOSTS_FILE",
hosts_file, hosts_file,
), ),
patch( patch(
"python_pkg.steam_backlog_enforcer.store_blocker._is_iptables_blocked", "steam_backlog_enforcer.store_blocker._is_iptables_blocked",
return_value=False, return_value=False,
), ),
): ):
@ -53,11 +53,11 @@ class TestIsStoreBlocked:
hosts_file.write_text("127.0.0.1 localhost\n", encoding="utf-8") hosts_file.write_text("127.0.0.1 localhost\n", encoding="utf-8")
with ( with (
patch( patch(
"python_pkg.steam_backlog_enforcer.store_blocker.HOSTS_FILE", "steam_backlog_enforcer.store_blocker.HOSTS_FILE",
hosts_file, hosts_file,
), ),
patch( patch(
"python_pkg.steam_backlog_enforcer.store_blocker._is_iptables_blocked", "steam_backlog_enforcer.store_blocker._is_iptables_blocked",
return_value=True, return_value=True,
), ),
): ):
@ -67,11 +67,11 @@ class TestIsStoreBlocked:
hosts_file = tmp_path / "nonexistent" hosts_file = tmp_path / "nonexistent"
with ( with (
patch( patch(
"python_pkg.steam_backlog_enforcer.store_blocker.HOSTS_FILE", "steam_backlog_enforcer.store_blocker.HOSTS_FILE",
hosts_file, hosts_file,
), ),
patch( patch(
"python_pkg.steam_backlog_enforcer.store_blocker._is_iptables_blocked", "steam_backlog_enforcer.store_blocker._is_iptables_blocked",
return_value=False, return_value=False,
), ),
): ):
@ -82,11 +82,11 @@ class TestIsStoreBlocked:
hosts_file.write_text("127.0.0.1 store.steampowered.com\n", encoding="utf-8") hosts_file.write_text("127.0.0.1 store.steampowered.com\n", encoding="utf-8")
with ( with (
patch( patch(
"python_pkg.steam_backlog_enforcer.store_blocker.HOSTS_FILE", "steam_backlog_enforcer.store_blocker.HOSTS_FILE",
hosts_file, hosts_file,
), ),
patch( patch(
"python_pkg.steam_backlog_enforcer.store_blocker._is_iptables_blocked", "steam_backlog_enforcer.store_blocker._is_iptables_blocked",
return_value=False, return_value=False,
), ),
): ):
@ -98,7 +98,7 @@ class TestBlockStore:
def test_already_blocked(self) -> None: def test_already_blocked(self) -> None:
with patch( with patch(
"python_pkg.steam_backlog_enforcer.store_blocker.is_store_blocked", "steam_backlog_enforcer.store_blocker.is_store_blocked",
return_value=True, return_value=True,
): ):
assert block_store() is True assert block_store() is True
@ -106,18 +106,18 @@ class TestBlockStore:
def test_reblock_succeeds(self) -> None: def test_reblock_succeeds(self) -> None:
with ( with (
patch( patch(
"python_pkg.steam_backlog_enforcer.store_blocker.is_store_blocked", "steam_backlog_enforcer.store_blocker.is_store_blocked",
side_effect=[False, True], side_effect=[False, True],
), ),
patch( patch(
"python_pkg.steam_backlog_enforcer.store_blocker._reblock_hosts", "steam_backlog_enforcer.store_blocker._reblock_hosts",
return_value=True, return_value=True,
), ),
patch( patch(
"python_pkg.steam_backlog_enforcer.store_blocker._block_store_iptables", "steam_backlog_enforcer.store_blocker._block_store_iptables",
), ),
patch( patch(
"python_pkg.steam_backlog_enforcer.store_blocker.flush_dns_cache", "steam_backlog_enforcer.store_blocker.flush_dns_cache",
), ),
): ):
assert block_store() is True assert block_store() is True
@ -125,23 +125,23 @@ class TestBlockStore:
def test_fallback_to_install_script(self) -> None: def test_fallback_to_install_script(self) -> None:
with ( with (
patch( patch(
"python_pkg.steam_backlog_enforcer.store_blocker.is_store_blocked", "steam_backlog_enforcer.store_blocker.is_store_blocked",
side_effect=[False, False], side_effect=[False, False],
), ),
patch( patch(
"python_pkg.steam_backlog_enforcer.store_blocker._reblock_hosts", "steam_backlog_enforcer.store_blocker._reblock_hosts",
return_value=False, return_value=False,
), ),
patch( patch(
"python_pkg.steam_backlog_enforcer.store_blocker._block_via_hosts_install", "steam_backlog_enforcer.store_blocker._block_via_hosts_install",
return_value=True, return_value=True,
), ),
patch( patch(
"python_pkg.steam_backlog_enforcer.store_blocker._block_store_iptables", "steam_backlog_enforcer.store_blocker._block_store_iptables",
return_value=False, return_value=False,
), ),
patch( patch(
"python_pkg.steam_backlog_enforcer.store_blocker.flush_dns_cache", "steam_backlog_enforcer.store_blocker.flush_dns_cache",
), ),
): ):
assert block_store() is True assert block_store() is True
@ -149,19 +149,19 @@ class TestBlockStore:
def test_all_fail(self) -> None: def test_all_fail(self) -> None:
with ( with (
patch( patch(
"python_pkg.steam_backlog_enforcer.store_blocker.is_store_blocked", "steam_backlog_enforcer.store_blocker.is_store_blocked",
side_effect=[False, False], side_effect=[False, False],
), ),
patch( patch(
"python_pkg.steam_backlog_enforcer.store_blocker._reblock_hosts", "steam_backlog_enforcer.store_blocker._reblock_hosts",
return_value=False, return_value=False,
), ),
patch( patch(
"python_pkg.steam_backlog_enforcer.store_blocker._block_via_hosts_install", "steam_backlog_enforcer.store_blocker._block_via_hosts_install",
return_value=False, return_value=False,
), ),
patch( patch(
"python_pkg.steam_backlog_enforcer.store_blocker._block_store_iptables", "steam_backlog_enforcer.store_blocker._block_store_iptables",
return_value=False, return_value=False,
), ),
): ):
@ -170,23 +170,23 @@ class TestBlockStore:
def test_iptables_only_succeeds(self) -> None: def test_iptables_only_succeeds(self) -> None:
with ( with (
patch( patch(
"python_pkg.steam_backlog_enforcer.store_blocker.is_store_blocked", "steam_backlog_enforcer.store_blocker.is_store_blocked",
side_effect=[False, False], side_effect=[False, False],
), ),
patch( patch(
"python_pkg.steam_backlog_enforcer.store_blocker._reblock_hosts", "steam_backlog_enforcer.store_blocker._reblock_hosts",
return_value=False, return_value=False,
), ),
patch( patch(
"python_pkg.steam_backlog_enforcer.store_blocker._block_via_hosts_install", "steam_backlog_enforcer.store_blocker._block_via_hosts_install",
return_value=False, return_value=False,
), ),
patch( patch(
"python_pkg.steam_backlog_enforcer.store_blocker._block_store_iptables", "steam_backlog_enforcer.store_blocker._block_store_iptables",
return_value=True, return_value=True,
), ),
patch( patch(
"python_pkg.steam_backlog_enforcer.store_blocker.flush_dns_cache", "steam_backlog_enforcer.store_blocker.flush_dns_cache",
), ),
): ):
assert block_store() is True assert block_store() is True
@ -197,7 +197,7 @@ class TestBlockViaHostsInstall:
def test_already_blocked(self) -> None: def test_already_blocked(self) -> None:
with patch( with patch(
"python_pkg.steam_backlog_enforcer.store_blocker.is_store_blocked", "steam_backlog_enforcer.store_blocker.is_store_blocked",
return_value=True, return_value=True,
): ):
assert _block_via_hosts_install() is True assert _block_via_hosts_install() is True
@ -205,11 +205,11 @@ class TestBlockViaHostsInstall:
def test_script_missing(self, tmp_path: Path) -> None: def test_script_missing(self, tmp_path: Path) -> None:
with ( with (
patch( patch(
"python_pkg.steam_backlog_enforcer.store_blocker.is_store_blocked", "steam_backlog_enforcer.store_blocker.is_store_blocked",
return_value=False, return_value=False,
), ),
patch( patch(
"python_pkg.steam_backlog_enforcer.store_blocker.HOSTS_INSTALL_SCRIPT", "steam_backlog_enforcer.store_blocker.HOSTS_INSTALL_SCRIPT",
tmp_path / "nonexistent.sh", tmp_path / "nonexistent.sh",
), ),
): ):
@ -221,15 +221,15 @@ class TestBlockViaHostsInstall:
mock_result = MagicMock(returncode=0) mock_result = MagicMock(returncode=0)
with ( with (
patch( patch(
"python_pkg.steam_backlog_enforcer.store_blocker.is_store_blocked", "steam_backlog_enforcer.store_blocker.is_store_blocked",
return_value=False, return_value=False,
), ),
patch( patch(
"python_pkg.steam_backlog_enforcer.store_blocker.HOSTS_INSTALL_SCRIPT", "steam_backlog_enforcer.store_blocker.HOSTS_INSTALL_SCRIPT",
script, script,
), ),
patch( patch(
"python_pkg.steam_backlog_enforcer.store_blocker.subprocess.run", "steam_backlog_enforcer.store_blocker.subprocess.run",
return_value=mock_result, return_value=mock_result,
), ),
): ):
@ -241,15 +241,15 @@ class TestBlockViaHostsInstall:
mock_result = MagicMock(returncode=1, stderr="error", stdout="") mock_result = MagicMock(returncode=1, stderr="error", stdout="")
with ( with (
patch( patch(
"python_pkg.steam_backlog_enforcer.store_blocker.is_store_blocked", "steam_backlog_enforcer.store_blocker.is_store_blocked",
return_value=False, return_value=False,
), ),
patch( patch(
"python_pkg.steam_backlog_enforcer.store_blocker.HOSTS_INSTALL_SCRIPT", "steam_backlog_enforcer.store_blocker.HOSTS_INSTALL_SCRIPT",
script, script,
), ),
patch( patch(
"python_pkg.steam_backlog_enforcer.store_blocker.subprocess.run", "steam_backlog_enforcer.store_blocker.subprocess.run",
return_value=mock_result, return_value=mock_result,
), ),
): ):
@ -261,15 +261,15 @@ class TestBlockViaHostsInstall:
mock_result = MagicMock(returncode=1, stderr="", stdout="out") mock_result = MagicMock(returncode=1, stderr="", stdout="out")
with ( with (
patch( patch(
"python_pkg.steam_backlog_enforcer.store_blocker.is_store_blocked", "steam_backlog_enforcer.store_blocker.is_store_blocked",
return_value=False, return_value=False,
), ),
patch( patch(
"python_pkg.steam_backlog_enforcer.store_blocker.HOSTS_INSTALL_SCRIPT", "steam_backlog_enforcer.store_blocker.HOSTS_INSTALL_SCRIPT",
script, script,
), ),
patch( patch(
"python_pkg.steam_backlog_enforcer.store_blocker.subprocess.run", "steam_backlog_enforcer.store_blocker.subprocess.run",
return_value=mock_result, return_value=mock_result,
), ),
): ):
@ -280,15 +280,15 @@ class TestBlockViaHostsInstall:
script.touch() script.touch()
with ( with (
patch( patch(
"python_pkg.steam_backlog_enforcer.store_blocker.is_store_blocked", "steam_backlog_enforcer.store_blocker.is_store_blocked",
return_value=False, return_value=False,
), ),
patch( patch(
"python_pkg.steam_backlog_enforcer.store_blocker.HOSTS_INSTALL_SCRIPT", "steam_backlog_enforcer.store_blocker.HOSTS_INSTALL_SCRIPT",
script, script,
), ),
patch( patch(
"python_pkg.steam_backlog_enforcer.store_blocker.subprocess.run", "steam_backlog_enforcer.store_blocker.subprocess.run",
side_effect=OSError, side_effect=OSError,
), ),
): ):
@ -301,7 +301,7 @@ class TestIsIptablesBlocked:
def test_blocked(self) -> None: def test_blocked(self) -> None:
mock_result = MagicMock(returncode=0, stdout="DROP blah") mock_result = MagicMock(returncode=0, stdout="DROP blah")
with patch( with patch(
"python_pkg.steam_backlog_enforcer.store_blocker.subprocess.run", "steam_backlog_enforcer.store_blocker.subprocess.run",
return_value=mock_result, return_value=mock_result,
): ):
assert _is_iptables_blocked() is True assert _is_iptables_blocked() is True
@ -309,7 +309,7 @@ class TestIsIptablesBlocked:
def test_not_blocked_no_drop(self) -> None: def test_not_blocked_no_drop(self) -> None:
mock_result = MagicMock(returncode=0, stdout="ACCEPT") mock_result = MagicMock(returncode=0, stdout="ACCEPT")
with patch( with patch(
"python_pkg.steam_backlog_enforcer.store_blocker.subprocess.run", "steam_backlog_enforcer.store_blocker.subprocess.run",
return_value=mock_result, return_value=mock_result,
): ):
assert _is_iptables_blocked() is False assert _is_iptables_blocked() is False
@ -317,14 +317,14 @@ class TestIsIptablesBlocked:
def test_not_blocked_error(self) -> None: def test_not_blocked_error(self) -> None:
mock_result = MagicMock(returncode=1, stdout="") mock_result = MagicMock(returncode=1, stdout="")
with patch( with patch(
"python_pkg.steam_backlog_enforcer.store_blocker.subprocess.run", "steam_backlog_enforcer.store_blocker.subprocess.run",
return_value=mock_result, return_value=mock_result,
): ):
assert _is_iptables_blocked() is False assert _is_iptables_blocked() is False
def test_os_error(self) -> None: def test_os_error(self) -> None:
with patch( with patch(
"python_pkg.steam_backlog_enforcer.store_blocker.subprocess.run", "steam_backlog_enforcer.store_blocker.subprocess.run",
side_effect=OSError, side_effect=OSError,
): ):
assert _is_iptables_blocked() is False assert _is_iptables_blocked() is False
@ -337,11 +337,11 @@ class TestBlockStoreIptables:
mock_result = MagicMock(returncode=0) mock_result = MagicMock(returncode=0)
with ( with (
patch( patch(
"python_pkg.steam_backlog_enforcer.store_blocker.subprocess.run", "steam_backlog_enforcer.store_blocker.subprocess.run",
return_value=mock_result, return_value=mock_result,
), ),
patch( patch(
"python_pkg.steam_backlog_enforcer.store_blocker.socket.getaddrinfo", "steam_backlog_enforcer.store_blocker.socket.getaddrinfo",
return_value=[ return_value=[
(None, None, None, None, ("1.2.3.4", 443)), (None, None, None, None, ("1.2.3.4", 443)),
], ],
@ -351,7 +351,7 @@ class TestBlockStoreIptables:
def test_os_error(self) -> None: def test_os_error(self) -> None:
with patch( with patch(
"python_pkg.steam_backlog_enforcer.store_blocker.subprocess.run", "steam_backlog_enforcer.store_blocker.subprocess.run",
side_effect=OSError, side_effect=OSError,
): ):
assert _block_store_iptables() is False assert _block_store_iptables() is False
@ -362,11 +362,11 @@ class TestBlockStoreIptables:
mock_result = MagicMock(returncode=0) mock_result = MagicMock(returncode=0)
with ( with (
patch( patch(
"python_pkg.steam_backlog_enforcer.store_blocker.subprocess.run", "steam_backlog_enforcer.store_blocker.subprocess.run",
return_value=mock_result, return_value=mock_result,
), ),
patch( patch(
"python_pkg.steam_backlog_enforcer.store_blocker.socket.getaddrinfo", "steam_backlog_enforcer.store_blocker.socket.getaddrinfo",
side_effect=socket.gaierror, side_effect=socket.gaierror,
), ),
): ):
@ -390,11 +390,11 @@ class TestBlockStoreIptables:
with ( with (
patch( patch(
"python_pkg.steam_backlog_enforcer.store_blocker.subprocess.run", "steam_backlog_enforcer.store_blocker.subprocess.run",
side_effect=side_effect, side_effect=side_effect,
), ),
patch( patch(
"python_pkg.steam_backlog_enforcer.store_blocker.socket.getaddrinfo", "steam_backlog_enforcer.store_blocker.socket.getaddrinfo",
side_effect=__import__("socket").gaierror, side_effect=__import__("socket").gaierror,
), ),
): ):
@ -407,15 +407,15 @@ class TestUnblockStore:
def test_both_succeed(self) -> None: def test_both_succeed(self) -> None:
with ( with (
patch( patch(
"python_pkg.steam_backlog_enforcer.store_blocker._unblock_store_iptables", "steam_backlog_enforcer.store_blocker._unblock_store_iptables",
return_value=True, return_value=True,
), ),
patch( patch(
"python_pkg.steam_backlog_enforcer.store_blocker._unblock_hosts", "steam_backlog_enforcer.store_blocker._unblock_hosts",
return_value=True, return_value=True,
), ),
patch( patch(
"python_pkg.steam_backlog_enforcer.store_blocker.flush_dns_cache", "steam_backlog_enforcer.store_blocker.flush_dns_cache",
), ),
): ):
assert unblock_store() is True assert unblock_store() is True
@ -423,15 +423,15 @@ class TestUnblockStore:
def test_iptables_fails(self) -> None: def test_iptables_fails(self) -> None:
with ( with (
patch( patch(
"python_pkg.steam_backlog_enforcer.store_blocker._unblock_store_iptables", "steam_backlog_enforcer.store_blocker._unblock_store_iptables",
return_value=False, return_value=False,
), ),
patch( patch(
"python_pkg.steam_backlog_enforcer.store_blocker._unblock_hosts", "steam_backlog_enforcer.store_blocker._unblock_hosts",
return_value=True, return_value=True,
), ),
patch( patch(
"python_pkg.steam_backlog_enforcer.store_blocker.flush_dns_cache", "steam_backlog_enforcer.store_blocker.flush_dns_cache",
), ),
): ):
assert unblock_store() is True assert unblock_store() is True
@ -439,15 +439,15 @@ class TestUnblockStore:
def test_both_fail(self) -> None: def test_both_fail(self) -> None:
with ( with (
patch( patch(
"python_pkg.steam_backlog_enforcer.store_blocker._unblock_store_iptables", "steam_backlog_enforcer.store_blocker._unblock_store_iptables",
return_value=False, return_value=False,
), ),
patch( patch(
"python_pkg.steam_backlog_enforcer.store_blocker._unblock_hosts", "steam_backlog_enforcer.store_blocker._unblock_hosts",
return_value=False, return_value=False,
), ),
patch( patch(
"python_pkg.steam_backlog_enforcer.store_blocker.flush_dns_cache", "steam_backlog_enforcer.store_blocker.flush_dns_cache",
), ),
): ):
assert unblock_store() is False assert unblock_store() is False
@ -458,13 +458,13 @@ class TestUnblockStoreIptables:
def test_success(self) -> None: def test_success(self) -> None:
with patch( with patch(
"python_pkg.steam_backlog_enforcer.store_blocker.subprocess.run", "steam_backlog_enforcer.store_blocker.subprocess.run",
): ):
assert _unblock_store_iptables() is True assert _unblock_store_iptables() is True
def test_os_error(self) -> None: def test_os_error(self) -> None:
with patch( with patch(
"python_pkg.steam_backlog_enforcer.store_blocker.subprocess.run", "steam_backlog_enforcer.store_blocker.subprocess.run",
side_effect=OSError, side_effect=OSError,
): ):
assert _unblock_store_iptables() is False assert _unblock_store_iptables() is False

View File

@ -5,7 +5,7 @@ from __future__ import annotations
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
from unittest.mock import MagicMock, patch from unittest.mock import MagicMock, patch
from python_pkg.steam_backlog_enforcer.store_blocker import ( from steam_backlog_enforcer.store_blocker import (
_disable_hosts_protection, _disable_hosts_protection,
_enable_hosts_protection, _enable_hosts_protection,
_reblock_hosts, _reblock_hosts,
@ -17,7 +17,7 @@ from python_pkg.steam_backlog_enforcer.store_blocker import (
if TYPE_CHECKING: if TYPE_CHECKING:
from pathlib import Path from pathlib import Path
PKG = "python_pkg.steam_backlog_enforcer.store_blocker" PKG = "steam_backlog_enforcer.store_blocker"
class TestSudoWriteHosts: class TestSudoWriteHosts:

View File

@ -8,7 +8,7 @@ from unittest.mock import patch
import pytest import pytest
from python_pkg.steam_backlog_enforcer._whitelist import ( from steam_backlog_enforcer._whitelist import (
WHITELIST_COOLDOWN_SECONDS, WHITELIST_COOLDOWN_SECONDS,
_append_audit_log, _append_audit_log,
_load_approved, _load_approved,
@ -170,7 +170,7 @@ class TestLockAndUnlock:
with ( with (
patch( patch(
"python_pkg.steam_backlog_enforcer._whitelist.APPROVED_EXCEPTIONS_FILE", "steam_backlog_enforcer._whitelist.APPROVED_EXCEPTIONS_FILE",
approved, approved,
), ),
patch("shutil.which", return_value="/usr/bin/chattr"), patch("shutil.which", return_value="/usr/bin/chattr"),
@ -206,7 +206,7 @@ class TestPersistence:
bad = tmp_path / "pending.json" bad = tmp_path / "pending.json"
bad.write_text("not json{{", encoding="utf-8") bad.write_text("not json{{", encoding="utf-8")
with patch( with patch(
"python_pkg.steam_backlog_enforcer._whitelist.PENDING_EXCEPTIONS_FILE", "steam_backlog_enforcer._whitelist.PENDING_EXCEPTIONS_FILE",
bad, bad,
): ):
assert _load_pending() == [] assert _load_pending() == []
@ -215,7 +215,7 @@ class TestPersistence:
bad = tmp_path / "pending.json" bad = tmp_path / "pending.json"
bad.write_text('{"key": "value"}', encoding="utf-8") bad.write_text('{"key": "value"}', encoding="utf-8")
with patch( with patch(
"python_pkg.steam_backlog_enforcer._whitelist.PENDING_EXCEPTIONS_FILE", "steam_backlog_enforcer._whitelist.PENDING_EXCEPTIONS_FILE",
bad, bad,
): ):
assert _load_pending() == [] assert _load_pending() == []
@ -234,7 +234,7 @@ class TestPersistence:
bad = tmp_path / "approved.json" bad = tmp_path / "approved.json"
bad.write_text("{{broken", encoding="utf-8") bad.write_text("{{broken", encoding="utf-8")
with patch( with patch(
"python_pkg.steam_backlog_enforcer._whitelist.APPROVED_EXCEPTIONS_FILE", "steam_backlog_enforcer._whitelist.APPROVED_EXCEPTIONS_FILE",
bad, bad,
): ):
assert _load_approved() == [] assert _load_approved() == []
@ -243,7 +243,7 @@ class TestPersistence:
bad = tmp_path / "approved.json" bad = tmp_path / "approved.json"
bad.write_text('"just a string"', encoding="utf-8") bad.write_text('"just a string"', encoding="utf-8")
with patch( with patch(
"python_pkg.steam_backlog_enforcer._whitelist.APPROVED_EXCEPTIONS_FILE", "steam_backlog_enforcer._whitelist.APPROVED_EXCEPTIONS_FILE",
bad, bad,
): ):
assert _load_approved() == [] assert _load_approved() == []
@ -268,7 +268,7 @@ class TestAppendAuditLog:
def test_audit_log_written(self, tmp_path: Path) -> None: def test_audit_log_written(self, tmp_path: Path) -> None:
log_file = tmp_path / "audit.log" log_file = tmp_path / "audit.log"
with patch( with patch(
"python_pkg.steam_backlog_enforcer._whitelist.EXCEPTION_AUDIT_LOG", "steam_backlog_enforcer._whitelist.EXCEPTION_AUDIT_LOG",
log_file, log_file,
): ):
_append_audit_log(440, "some reason", "REQUESTED") _append_audit_log(440, "some reason", "REQUESTED")
@ -280,7 +280,7 @@ class TestAppendAuditLog:
def test_audit_log_appends(self, tmp_path: Path) -> None: def test_audit_log_appends(self, tmp_path: Path) -> None:
log_file = tmp_path / "audit.log" log_file = tmp_path / "audit.log"
with patch( with patch(
"python_pkg.steam_backlog_enforcer._whitelist.EXCEPTION_AUDIT_LOG", "steam_backlog_enforcer._whitelist.EXCEPTION_AUDIT_LOG",
log_file, log_file,
): ):
_append_audit_log(440, "first", "REQUESTED") _append_audit_log(440, "first", "REQUESTED")