Files
shd.build/tools/game-data-capture/tests/unit/test_config.py
T

82 lines
2.7 KiB
Python

from __future__ import annotations
from pathlib import Path
from stat import S_IMODE
import pytest
from game_data_capture.config import ConfigError, ServerConfig, parse_config
from game_data_capture.paths import (
PathConfigurationError,
prepare_private_directories,
prepare_private_file,
resolve_runtime_paths,
)
def test_config_uses_typed_defaults() -> None:
config = parse_config({})
assert config.server == ServerConfig(host="127.0.0.1", port=8765)
assert config.lifecycle.instance_name == "shd-capture"
assert config.limits.max_queue_items == 64
@pytest.mark.parametrize("host", ["0.0.0.0", "192.168.1.2", "localhost", "example.test"])
def test_config_rejects_non_loopback_binding(host: str) -> None:
with pytest.raises(ConfigError, match="loopback IP literal"):
parse_config({"server": {"host": host}})
@pytest.mark.parametrize(
("document", "message"),
[
({"server": {"port": 80}}, "server.port"),
({"limits": {"max_queue_items": 257}}, "limits.max_queue_items"),
({"lifecycle": {"instance_name": "SHD Capture"}}, "lifecycle.instance_name"),
({"openai": {"api_key_env": "openai-key"}}, "openai.api_key_env"),
],
)
def test_config_rejects_invalid_bounded_values(document: dict[str, object], message: str) -> None:
with pytest.raises(ConfigError, match=message):
parse_config(document)
def test_xdg_paths_are_resolved_without_writing(tmp_path: Path) -> None:
root = tmp_path / "xdg"
paths = resolve_runtime_paths(
{
"HOME": str(tmp_path / "home"),
"XDG_CONFIG_HOME": str(root / "config"),
"XDG_DATA_HOME": str(root / "data"),
"XDG_CACHE_HOME": str(root / "cache"),
"XDG_RUNTIME_DIR": str(root / "runtime"),
}
)
assert paths.runtime_dir == root / "runtime" / "shd-capture"
assert not root.exists()
def test_xdg_paths_reject_relative_runtime_path() -> None:
with pytest.raises(PathConfigurationError, match="XDG runtime"):
resolve_runtime_paths({"HOME": "/tmp/home", "XDG_RUNTIME_DIR": "relative"})
def test_runtime_directories_and_lock_file_are_owner_only(tmp_path: Path) -> None:
paths = resolve_runtime_paths(
{
"HOME": str(tmp_path),
"XDG_CONFIG_HOME": str(tmp_path / "config"),
"XDG_DATA_HOME": str(tmp_path / "data"),
"XDG_CACHE_HOME": str(tmp_path / "cache"),
"XDG_RUNTIME_DIR": str(tmp_path / "runtime"),
}
)
prepare_private_directories(paths)
prepare_private_file(paths.instance_lock_file)
assert S_IMODE(paths.runtime_dir.stat().st_mode) == 0o700
assert S_IMODE(paths.instance_lock_file.stat().st_mode) == 0o600