""" Tests for engine.config module. """ import sys import tempfile from pathlib import Path from unittest.mock import patch import pytest 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 class TestActiveTheme: """Tests for ACTIVE_THEME global and set_active_theme function.""" def test_active_theme_initially_none(self): """ACTIVE_THEME should be None at module start.""" # Reset to None to test initial state original = config.ACTIVE_THEME config.ACTIVE_THEME = None try: assert config.ACTIVE_THEME is None finally: config.ACTIVE_THEME = original def test_set_active_theme_green(self): """Setting green theme works correctly.""" config.set_active_theme("green") assert config.ACTIVE_THEME is not None assert config.ACTIVE_THEME.name == "green" assert len(config.ACTIVE_THEME.main_gradient) == 12 assert len(config.ACTIVE_THEME.message_gradient) == 12 def test_set_active_theme_default(self): """Default theme is green when not specified.""" config.set_active_theme() assert config.ACTIVE_THEME is not None assert config.ACTIVE_THEME.name == "green" def test_set_active_theme_invalid(self): """Invalid theme_id raises KeyError.""" with pytest.raises(KeyError): config.set_active_theme("nonexistent") def test_set_active_theme_all_themes(self): """Verify orange and purple themes work.""" # Test orange config.set_active_theme("orange") assert config.ACTIVE_THEME.name == "orange" # Test purple config.set_active_theme("purple") assert config.ACTIVE_THEME.name == "purple" def test_set_active_theme_idempotent(self): """Calling set_active_theme multiple times works.""" config.set_active_theme("green") first_theme = config.ACTIVE_THEME config.set_active_theme("green") second_theme = config.ACTIVE_THEME assert first_theme.name == second_theme.name assert first_theme.name == "green"