Files
Mainline/tests/test_config.py
David Gwilliam b9ea51e7fd feat: modernize project with uv, add pytest test suite
- Add pyproject.toml with modern Python packaging (PEP 517/518)
- Add uv-based dependency management replacing inline venv bootstrap
- Add requirements.txt and requirements-dev.txt for compatibility
- Add mise.toml with dev tasks (test, lint, run, sync, ci)
- Add .python-version pinned to Python 3.12
- Add comprehensive pytest test suite (73 tests) for:
  - engine/config, filter, terminal, sources, mic, ntfy modules
- Configure pytest with coverage reporting (16% total, 100% on tested modules)
- Configure ruff for linting with Python 3.10+ target
- Remove redundant venv bootstrap code from mainline.py
- Update .gitignore for uv/venv artifacts

Run 'uv sync' to install dependencies, 'uv run pytest' to test.
2026-03-15 14:20:29 -07:00

163 lines
5.3 KiB
Python

"""
Tests for engine.config module.
"""
import sys
import tempfile
from pathlib import Path
from unittest.mock import patch
from engine import config
class TestArgValue:
"""Tests for _arg_value helper."""
def test_returns_value_when_flag_present(self):
"""Returns the value following the flag."""
with patch.object(sys, "argv", ["prog", "--font-file", "test.otf"]):
result = config._arg_value("--font-file")
assert result == "test.otf"
def test_returns_none_when_flag_missing(self):
"""Returns None when flag is not present."""
with patch.object(sys, "argv", ["prog"]):
result = config._arg_value("--font-file")
assert result is None
def test_returns_none_when_no_value(self):
"""Returns None when flag is last."""
with patch.object(sys, "argv", ["prog", "--font-file"]):
result = config._arg_value("--font-file")
assert result is None
class TestArgInt:
"""Tests for _arg_int helper."""
def test_parses_valid_int(self):
"""Parses valid integer."""
with patch.object(sys, "argv", ["prog", "--font-index", "5"]):
result = config._arg_int("--font-index", 0)
assert result == 5
def test_returns_default_on_invalid(self):
"""Returns default on invalid input."""
with patch.object(sys, "argv", ["prog", "--font-index", "abc"]):
result = config._arg_int("--font-index", 0)
assert result == 0
def test_returns_default_when_missing(self):
"""Returns default when flag missing."""
with patch.object(sys, "argv", ["prog"]):
result = config._arg_int("--font-index", 10)
assert result == 10
class TestResolveFontPath:
"""Tests for _resolve_font_path helper."""
def test_returns_absolute_paths(self):
"""Absolute paths are returned as-is."""
result = config._resolve_font_path("/absolute/path.otf")
assert result == "/absolute/path.otf"
def test_resolves_relative_paths(self):
"""Relative paths are resolved to repo root."""
result = config._resolve_font_path("fonts/test.otf")
assert str(config._REPO_ROOT) in result
def test_expands_user_home(self):
"""Tilde paths are expanded."""
with patch("pathlib.Path.expanduser", return_value=Path("/home/user/fonts")):
result = config._resolve_font_path("~/fonts/test.otf")
assert isinstance(result, str)
class TestListFontFiles:
"""Tests for _list_font_files helper."""
def test_returns_empty_for_missing_dir(self):
"""Returns empty list for missing directory."""
result = config._list_font_files("/nonexistent/directory")
assert result == []
def test_filters_by_extension(self):
"""Only returns valid font extensions."""
with tempfile.TemporaryDirectory() as tmpdir:
Path(tmpdir, "valid.otf").touch()
Path(tmpdir, "valid.ttf").touch()
Path(tmpdir, "invalid.txt").touch()
Path(tmpdir, "image.png").touch()
result = config._list_font_files(tmpdir)
assert len(result) == 2
assert all(f.endswith((".otf", ".ttf")) for f in result)
def test_sorts_alphabetically(self):
"""Results are sorted alphabetically."""
with tempfile.TemporaryDirectory() as tmpdir:
Path(tmpdir, "zfont.otf").touch()
Path(tmpdir, "afont.otf").touch()
result = config._list_font_files(tmpdir)
filenames = [Path(f).name for f in result]
assert filenames == ["afont.otf", "zfont.otf"]
class TestDefaults:
"""Tests for default configuration values."""
def test_headline_limit(self):
"""HEADLINE_LIMIT has sensible default."""
assert config.HEADLINE_LIMIT > 0
def test_feed_timeout(self):
"""FEED_TIMEOUT has sensible default."""
assert config.FEED_TIMEOUT > 0
def test_font_extensions(self):
"""Font extensions are defined."""
assert ".otf" in config._FONT_EXTENSIONS
assert ".ttf" in config._FONT_EXTENSIONS
assert ".ttc" in config._FONT_EXTENSIONS
class TestGlyphs:
"""Tests for glyph constants."""
def test_glitch_glyphs_defined(self):
"""GLITCH glyphs are defined."""
assert len(config.GLITCH) > 0
def test_kata_glyphs_defined(self):
"""KATA glyphs are defined."""
assert len(config.KATA) > 0
class TestSetFontSelection:
"""Tests for set_font_selection function."""
def test_updates_font_path(self):
"""Updates FONT_PATH globally."""
original = config.FONT_PATH
config.set_font_selection(font_path="/new/path.otf")
assert config.FONT_PATH == "/new/path.otf"
config.FONT_PATH = original
def test_updates_font_index(self):
"""Updates FONT_INDEX globally."""
original = config.FONT_INDEX
config.set_font_selection(font_index=5)
assert config.FONT_INDEX == 5
config.FONT_INDEX = original
def test_handles_none_values(self):
"""Handles None values gracefully."""
original_path = config.FONT_PATH
original_index = config.FONT_INDEX
config.set_font_selection(font_path=None, font_index=None)
assert original_path == config.FONT_PATH
assert original_index == config.FONT_INDEX