mirror of
https://github.com/kuhyx/steam-backlog-enforcer.git
synced 2026-11-10 19:07:05 +01:00
buy-dlc: fixed-length phrase the daemon accepts (was an unfillable {minutes} slot)
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
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
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
341bc784ac
commit
e4c61442c2
@ -14,6 +14,8 @@ from __future__ import annotations
|
||||
from dataclasses import dataclass
|
||||
from typing import Final
|
||||
|
||||
from steam_backlog_enforcer._store_window import DEFAULT_WINDOW_MINUTES
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class Friction:
|
||||
@ -30,13 +32,19 @@ class Friction:
|
||||
|
||||
GAMING_RESET_COUNTDOWN_SECONDS: Final = 300
|
||||
|
||||
_STORE_WINDOW_PHRASE: Final = "unblock the store for {minutes} minutes"
|
||||
|
||||
FRICTION: Final[dict[str, Friction]] = {
|
||||
"gaming-reset": Friction(
|
||||
"reset today's gaming budget", GAMING_RESET_COUNTDOWN_SECONDS
|
||||
),
|
||||
"block-gaming": Friction("block all gaming for {days} days"),
|
||||
"unblock": Friction("unblock the store for {minutes} minutes"),
|
||||
"buy-dlc": Friction("unblock the store for {minutes} minutes"),
|
||||
"unblock": Friction(_STORE_WINDOW_PHRASE),
|
||||
# buy-dlc takes no params: it is always the default-length window, and the
|
||||
# daemon checks it as an ``unblock`` of that length. So its phrase is that
|
||||
# exact sentence with the number already in it, not a ``{minutes}`` slot
|
||||
# that nothing could fill.
|
||||
"buy-dlc": Friction(_STORE_WINDOW_PHRASE.format(minutes=DEFAULT_WINDOW_MINUTES)),
|
||||
"gaming-unblock": Friction("force release playtime mounts"),
|
||||
"abandon-pick": Friction("abandon {game_name}"),
|
||||
"pick-manual": Friction("lock in {game_name}"),
|
||||
|
||||
@ -2,10 +2,14 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from string import Formatter
|
||||
from unittest.mock import patch
|
||||
|
||||
from steam_backlog_enforcer import _command_catalog, _command_locks
|
||||
from steam_backlog_enforcer._friction import Friction
|
||||
from steam_backlog_enforcer._command_params import PARAMS
|
||||
from steam_backlog_enforcer._ctl_context import require_phrase
|
||||
from steam_backlog_enforcer._friction import FRICTION, Friction, expected_phrase
|
||||
from steam_backlog_enforcer._web_privileged import BUY_DLC_MINUTES
|
||||
from steam_backlog_enforcer.config import Config, State
|
||||
from steam_backlog_enforcer.jobs._specs import JobFlags
|
||||
from steam_backlog_enforcer.main import _shared
|
||||
@ -115,3 +119,28 @@ class TestCommandLocks:
|
||||
_command_locks._MANUAL_LOCK_EXEMPT_COMMANDS
|
||||
)
|
||||
assert "gaming-reset" not in _command_locks._TOTAL_BLOCK_EXEMPT_COMMANDS
|
||||
|
||||
|
||||
class TestPhrasesAreFillable:
|
||||
"""Every phrase the UI shows can be completed and is the one checked."""
|
||||
|
||||
def test_every_template_field_has_a_source(self) -> None:
|
||||
# Mirrors fillPhrase in web/src/commands/catalog.ts: a field is a
|
||||
# param, ``game_name`` from an ``app_id`` param, or ``count`` from
|
||||
# the installed list. Anything else leaves Run disabled forever.
|
||||
# The same two tables ``_spec`` builds each CommandSpec from.
|
||||
for name in _command_catalog.CATALOG:
|
||||
friction = FRICTION.get(name)
|
||||
if friction is None:
|
||||
continue
|
||||
params = {p.name for p in PARAMS.get(name, ())}
|
||||
derived = {"count"} | ({"game_name"} if "app_id" in params else set())
|
||||
template = friction.phrase_template
|
||||
fields = {f for _, f, _, _ in Formatter().parse(template) if f}
|
||||
assert fields <= params | derived, name
|
||||
|
||||
def test_buy_dlc_phrase_is_the_unblock_the_daemon_checks(self) -> None:
|
||||
shown = expected_phrase("buy-dlc")
|
||||
assert shown == expected_phrase("unblock", minutes=BUY_DLC_MINUTES)
|
||||
assert shown is not None
|
||||
require_phrase("unblock", shown, minutes=BUY_DLC_MINUTES)
|
||||
|
||||
Loading…
Reference in New Issue
Block a user