mirror of
https://github.com/kuhyx/steam-backlog-enforcer.git
synced 2026-11-11 22:13:48 +01:00
Some checks are pending
dependency freshness / dependencies on newest stable (push) Waiting to run
md-naming / markdown naming convention (push) Waiting to run
pre-commit / pre-commit (${{ matrix.group }}) (mypy, mypy, ) (push) Waiting to run
pre-commit / pre-commit (${{ matrix.group }}) (pylint, pylint, ) (push) Waiting to run
pre-commit / pre-commit (${{ matrix.group }}) (rest, , mypy,pylint) (push) Waiting to run
Tests / test (push) Waiting to run
Tests / web-coverage (push) Waiting to run
repo-contract / repo contract (push) Waiting to run
kuhy 2026-11-11: game-design research needs Librarian (app 4197610) actually played during the 6-day total block. New root command total-block-research --until T opens ONE window per lock: <= 48 h, never past the lock end, a rerun never extends it. The command only validates and records (/var/lib/steam-backlog- enforcer, root 0700; lock id + deadline + two latches). The daemon opens and closes everything: Steam hosts names, the store block, the total-block chain (IP cache kept) and the steam package, reinstalled with only guard-lib's package-block hook masked for that one pacman call (--hookdir, proved in a guest). Lock file, hook and end date are never touched. Browser-game sites stay null-routed. Only Librarian may run: loop-local allowed set, strict mode (no whitelist exceptions, no zero-achievement games), launchers and counted games killed; state.json is never read or written. Expiry latches the record, then the ordinary tick re-applies hosts (now reconciled each tick), chain, uninstall and purge; the normal store block is restored once, unconditionally. Failure modes: missing/corrupt/unparseable record = no window and a corrupt one also refuses a new window (fail closed); a clock set back cannot reopen a latched window. Checked: 3290 tests at 100% coverage; whole-tree pylint 10/10 run in three parts (the hook exceeds the 60 s cap, so SKIP=pylint here); vmbox guest with real guardctl package-block and the real unit, window opened, Librarian-only enforced, clock moved past the deadline and the full block re-applied. Not verified on the host. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WzxdWWzuCEg1m87jpKyRrw
89 lines
3.4 KiB
Python
89 lines
3.4 KiB
Python
"""install_package: pacman -S with named hooks masked for that one call."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import logging
|
|
from pathlib import Path
|
|
import subprocess
|
|
from unittest.mock import MagicMock, patch
|
|
|
|
import pytest
|
|
|
|
from steam_backlog_enforcer import _pacman
|
|
|
|
PKG = "steam_backlog_enforcer._pacman"
|
|
HOOK = "guard-lib-block-steam-block.hook"
|
|
|
|
|
|
def _hookdirs(argv: list[str]) -> list[str]:
|
|
"""The values of every --hookdir in *argv*, in order."""
|
|
return [argv[i + 1] for i, arg in enumerate(argv) if arg == "--hookdir"]
|
|
|
|
|
|
class TestInstallPackage:
|
|
"""The command line, the mask directory and the outcomes."""
|
|
|
|
def test_plain_install_without_masks(self) -> None:
|
|
"""Test that no mask means no --hookdir at all (all hooks run)."""
|
|
run = MagicMock(return_value=MagicMock(returncode=0, stderr=""))
|
|
with patch(f"{PKG}.subprocess.run", run):
|
|
assert _pacman.install_package("steam") is True
|
|
argv = run.call_args.args[0]
|
|
assert argv[1:] == [_pacman.PACMAN, "-S", "--needed", "--noconfirm", "steam"]
|
|
|
|
def test_masked_hook_is_a_dev_null_symlink_in_the_last_hookdir(self) -> None:
|
|
"""Test that the mask wins and every other hook directory is kept."""
|
|
seen: dict[str, object] = {}
|
|
|
|
def fake_run(argv: list[str], **_kw: object) -> MagicMock:
|
|
dirs = _hookdirs(argv)
|
|
mask = Path(dirs[-1])
|
|
seen["dirs"] = dirs
|
|
seen["entries"] = {p.name: p.readlink() for p in mask.iterdir()}
|
|
seen["mask"] = mask
|
|
return MagicMock(returncode=0, stderr="")
|
|
|
|
with patch(f"{PKG}.subprocess.run", side_effect=fake_run):
|
|
assert _pacman.install_package("steam", masked_hooks=(HOOK,)) is True
|
|
dirs = seen["dirs"]
|
|
assert isinstance(dirs, list)
|
|
assert dirs[:2] == ["/usr/share/libalpm/hooks", "/etc/pacman.d/hooks"]
|
|
assert seen["entries"] == {HOOK: Path("/dev/null")}
|
|
mask = seen["mask"]
|
|
assert isinstance(mask, Path)
|
|
assert not mask.exists()
|
|
|
|
def test_nonzero_exit_is_false_and_logged(
|
|
self, caplog: pytest.LogCaptureFixture
|
|
) -> None:
|
|
"""Test that a failed transaction is reported with pacman's stderr."""
|
|
run = MagicMock(return_value=MagicMock(returncode=1, stderr="boom 404"))
|
|
with (
|
|
patch(f"{PKG}.subprocess.run", run),
|
|
caplog.at_level(logging.ERROR),
|
|
):
|
|
assert _pacman.install_package("steam", masked_hooks=(HOOK,)) is False
|
|
assert "boom 404" in caplog.text
|
|
|
|
@pytest.mark.parametrize(
|
|
"error", [OSError("no sudo"), subprocess.TimeoutExpired("pacman", 1)]
|
|
)
|
|
def test_launch_errors_are_false(self, error: Exception) -> None:
|
|
"""Test that a missing binary or a timeout never raises."""
|
|
with patch(f"{PKG}.subprocess.run", side_effect=error):
|
|
assert _pacman.install_package("steam") is False
|
|
|
|
def test_mask_dir_is_removed_even_when_pacman_fails(self) -> None:
|
|
"""Test that no temp directory outlives a failed install."""
|
|
masks: list[Path] = []
|
|
|
|
def fake_run(argv: list[str], **_kw: object) -> MagicMock:
|
|
masks.append(Path(_hookdirs(argv)[-1]))
|
|
gone = "gone"
|
|
raise OSError(gone)
|
|
|
|
with patch(f"{PKG}.subprocess.run", side_effect=fake_run):
|
|
assert _pacman.install_package("steam", masked_hooks=(HOOK,)) is False
|
|
assert masks
|
|
assert not masks[0].exists()
|