mirror of
https://github.com/kuhyx/steam-backlog-enforcer.git
synced 2026-11-11 16:33:49 +01:00
The serve rework was sitting untracked: a /proc-based port guard, the start-or-defer decision, and a stale-bundle rebuild. It had no tests, and it changed cmd_serve(_config, state) to cmd_serve(argv) without updating the one test that called it, so the suite was red and coverage was 251 lines short. Tests, all against fakes: - _fake_proc builds a synthetic /proc. _NET_TCP is derived from _PROC at import time, so patch_proc redirects both; patching _PROC alone would have had these tests parse the host's real process table. - _serve_guard is split across two test files to stay under the 250-line cap. os is replaced wholesale in every termination test, so no signal can reach a real process even if the guard's self-protection were removed. - _web_build mocks subprocess entirely and redirects every path constant into tmp_path. _no_subprocess now blocks _web_build.subprocess.run too: an unguarded test would fire a real `npm run build`, rewriting web/dist and perturbing frontend_is_stale() for every later test in the run. Source changes, both required rather than incidental: - _serve_guard's wildcard match was a `# noqa: S104` on a "0.0.0.0" literal, which the no-noqa hook rejects outright. It is now the all-zero hex column /proc actually prints, so the dotted-quad literal never appears. - main/__init__.py's serve dispatch arm was itself uncovered; it now has a test asserting the raw argv passes through unparsed. Also drops the document.hidden guard from useBudgetPoll: it skipped the very first load, so the budget panel sat on "Reading budget state…" forever in a background tab, which is every tab under browser automation. Verified: 1712 python tests, 100% branch coverage; 128 vitest, 100%. The guard's /proc parser was checked read-only against a real listener on :8000 and resolved the same pid as `ss`. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MKFzwzwRU1JD7SFLHtQBsz
102 lines
3.3 KiB
Python
102 lines
3.3 KiB
Python
"""Autouse guard that stops tests shelling out to real commands.
|
|
|
|
Split out of ``conftest.py`` to keep every file inside the 250-line cap;
|
|
``conftest`` imports the fixture by name, which is what registers it.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from typing import TYPE_CHECKING
|
|
from unittest.mock import MagicMock, patch
|
|
|
|
import pytest
|
|
|
|
if TYPE_CHECKING:
|
|
from collections.abc import Iterator
|
|
|
|
|
|
@pytest.fixture(autouse=True)
|
|
def _block_real_subprocesses() -> Iterator[None]:
|
|
"""Block subprocess calls that could launch real Steam or modify system.
|
|
|
|
Individual tests that need to test subprocess behaviour should
|
|
patch the specific module's ``subprocess.run`` / ``subprocess.Popen``
|
|
themselves — their local patch will override this one.
|
|
"""
|
|
noop_run = MagicMock(return_value=MagicMock(returncode=1))
|
|
noop_popen = MagicMock()
|
|
|
|
with (
|
|
patch(
|
|
"steam_backlog_enforcer.game_install.subprocess.run",
|
|
noop_run,
|
|
),
|
|
patch(
|
|
"steam_backlog_enforcer.game_install.subprocess.Popen",
|
|
noop_popen,
|
|
),
|
|
patch(
|
|
"steam_backlog_enforcer.enforcer.subprocess.run",
|
|
noop_run,
|
|
),
|
|
patch(
|
|
"steam_backlog_enforcer.store_blocker.subprocess.run",
|
|
noop_run,
|
|
),
|
|
patch(
|
|
"steam_backlog_enforcer._total_block.subprocess.run",
|
|
noop_run,
|
|
),
|
|
patch(
|
|
"steam_backlog_enforcer._pacman.subprocess.run",
|
|
noop_run,
|
|
),
|
|
patch(
|
|
"steam_backlog_enforcer._total_block_hosts.subprocess.run",
|
|
noop_run,
|
|
),
|
|
patch(
|
|
"steam_backlog_enforcer._total_block_iptables.subprocess.run",
|
|
noop_run,
|
|
),
|
|
# library_hider no longer spawns: its launch/process code lives in
|
|
# _steam_launch / _steam_process, and _steam_client starts the client.
|
|
# Every module that can spawn must be patched here, or a test could
|
|
# fire a real `sudo -u kuhy steam`.
|
|
patch(
|
|
"steam_backlog_enforcer._steam_launch.subprocess.run",
|
|
noop_run,
|
|
),
|
|
patch(
|
|
"steam_backlog_enforcer._steam_launch.subprocess.Popen",
|
|
noop_popen,
|
|
),
|
|
patch(
|
|
"steam_backlog_enforcer._steam_process.subprocess.Popen",
|
|
noop_popen,
|
|
),
|
|
patch(
|
|
"steam_backlog_enforcer._steam_client.subprocess.run",
|
|
noop_run,
|
|
),
|
|
patch(
|
|
"steam_backlog_enforcer._steam_client.subprocess.Popen",
|
|
noop_popen,
|
|
),
|
|
# The single most important patch in this file. _playtime_block._run
|
|
# executes `mount --bind` against paths that, unpatched, are the real
|
|
# /usr/bin/steam. A test that reached the real subprocess would mask
|
|
# the user's actual Steam install with a refusal stub.
|
|
patch(
|
|
"steam_backlog_enforcer._playtime_run.subprocess.run",
|
|
noop_run,
|
|
),
|
|
# A real `npm run build` here would rewrite web/dist/index.html, which
|
|
# then perturbs frontend_is_stale() for every later test in the run.
|
|
patch(
|
|
"steam_backlog_enforcer._web_build.subprocess.run",
|
|
noop_run,
|
|
),
|
|
):
|
|
yield
|