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

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

114 lines
4.3 KiB
Python

"""Tests for ``_command_params``: the typed param table and its validation."""
from __future__ import annotations
import pytest
from steam_backlog_enforcer._command_params import (
PARAMS,
InvalidParamsError,
ParamSpec,
validate_params,
)
class TestToJson:
"""Contract serialisation omits unset optional keys."""
def test_minimal(self) -> None:
"""A required spec serialises only name, label, type and required."""
spec = ParamSpec("n", "N", "int")
assert spec.to_json() == {
"name": "n",
"label": "N",
"type": "int",
"required": True,
}
def test_full(self) -> None:
"""An optional spec serialises its bounds, default and help."""
spec = ParamSpec(
"n", "N", "int", required=False, min=0, max=9, default=0, help="h"
)
assert spec.to_json() == {
"name": "n",
"label": "N",
"type": "int",
"required": False,
"min": 0,
"max": 9,
"default": 0,
"help": "h",
}
class TestValidateParams:
"""Coercion, bounds, defaults and unknown/missing params."""
def test_command_without_params(self) -> None:
"""A command with no params validates to an empty dict."""
assert validate_params("status", {}) == {}
def test_unknown_param(self) -> None:
"""Unknown params are refused, listed in sorted order."""
with pytest.raises(InvalidParamsError, match=r"Unknown param.*x, y"):
validate_params("status", {"y": 1, "x": 2})
def test_missing_required(self) -> None:
"""A missing required param is refused by name."""
with pytest.raises(InvalidParamsError, match="Missing required param: days"):
validate_params("block-gaming", {})
def test_default_filled(self) -> None:
"""An omitted param with a default gets the default."""
assert validate_params("unblock", {}) == {"minutes": 15}
def test_optional_without_default_is_omitted(self) -> None:
"""An omitted optional param with no default stays absent."""
optional = ParamSpec("o", "O", "string", required=False)
with pytest.MonkeyPatch.context() as patch:
patch.setitem(PARAMS, "probe", (optional,))
assert validate_params("probe", {}) == {}
def test_int_from_string(self) -> None:
"""Padded integer text is coerced to an int."""
assert validate_params("block-gaming", {"days": " 7 "}) == {"days": 7}
def test_string_is_stripped(self) -> None:
"""String params are stripped of surrounding whitespace."""
assert validate_params("restore-backup", {"backup_id": " b1 "}) == {
"backup_id": "b1"
}
@pytest.mark.parametrize("raw", ["", " ", 5, None])
def test_string_must_be_text(self, raw: object) -> None:
"""Blank or non-string values for a string param are refused."""
with pytest.raises(InvalidParamsError, match="non-empty string"):
validate_params("restore-backup", {"backup_id": raw})
@pytest.mark.parametrize("raw", [True, 1.5, None, [1]])
def test_int_must_be_integer_like(self, raw: object) -> None:
"""Bools, floats, None and lists for an int param are refused."""
with pytest.raises(InvalidParamsError, match="whole number"):
validate_params("block-gaming", {"days": raw})
def test_int_text_must_parse(self) -> None:
"""Non-numeric text for an int param is refused, quoting the input."""
with pytest.raises(InvalidParamsError, match="got 'abc'"):
validate_params("block-gaming", {"days": "abc"})
def test_below_min(self) -> None:
"""A value under the minimum is refused."""
with pytest.raises(InvalidParamsError, match="at least 1"):
validate_params("block-gaming", {"days": 0})
def test_above_max(self) -> None:
"""A value over the maximum is refused."""
with pytest.raises(InvalidParamsError, match="at most 30"):
validate_params("unblock", {"minutes": 31})
def test_boundaries_accepted(self) -> None:
"""Values exactly at the bounds are accepted."""
assert validate_params("unblock", {"minutes": 30}) == {"minutes": 30}
assert validate_params("enforce", {"demo": 0}) == {"demo": 0}