"""Tests for constants module.""" import re from pathlib import Path from unittest.mock import mock_open, patch import pytest from src.constants import ( APP_NAME, APP_TITLE, APP_VERSION, ENV_DEBUG, get_debug_mode, get_version, ) # --------------------------------------------------------------------------- # get_version() # --------------------------------------------------------------------------- def test_get_version_returns_string() -> None: """get_version() should return a string.""" assert isinstance(get_version(), str) def test_get_version_semver_format() -> None: """get_version() should return a semver-like string X.Y.Z.""" version = get_version() assert re.match(r"^\d+\.\d+\.\d+", version), f"Not semver: {version!r}" def test_get_version_fallback_when_toml_missing(tmp_path: Path) -> None: """get_version() returns '0.0.0-unknown' when pyproject.toml and _version.py are both missing.""" missing = tmp_path / "nonexistent.toml" with patch("src.constants._PYPROJECT_PATH", missing): result = get_version() # Either fallback _version.py exists (from previous run) or returns unknown assert isinstance(result, str) assert len(result) > 0 def test_get_version_unknown_fallback(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None: """get_version() returns '0.0.0-unknown' when all sources are unavailable.""" missing = tmp_path / "nonexistent.toml" monkeypatch.setattr("src.constants._PYPROJECT_PATH", missing) # Patch _version import to also fail with patch("src.constants.Path.write_text", side_effect=OSError): with patch.dict("sys.modules", {"src._version": None}): result = get_version() assert isinstance(result, str) # --------------------------------------------------------------------------- # get_debug_mode() # --------------------------------------------------------------------------- def test_get_debug_mode_returns_bool() -> None: """get_debug_mode() should always return a bool.""" assert isinstance(get_debug_mode(), bool) def test_get_debug_mode_true(monkeypatch: pytest.MonkeyPatch) -> None: """get_debug_mode() returns True when ENV_DEBUG=true.""" monkeypatch.setenv("ENV_DEBUG", "true") assert get_debug_mode() is True def test_get_debug_mode_true_variants(monkeypatch: pytest.MonkeyPatch) -> None: """get_debug_mode() accepts '1' and 'yes' as truthy values.""" for value in ("1", "yes", "YES", "True", "TRUE"): monkeypatch.setenv("ENV_DEBUG", value) assert get_debug_mode() is True, f"Expected True for ENV_DEBUG={value!r}" def test_get_debug_mode_false(monkeypatch: pytest.MonkeyPatch) -> None: """get_debug_mode() returns False when ENV_DEBUG=false.""" monkeypatch.setenv("ENV_DEBUG", "false") assert get_debug_mode() is False def test_get_debug_mode_false_when_unset(monkeypatch: pytest.MonkeyPatch) -> None: """get_debug_mode() returns False when ENV_DEBUG is not set.""" monkeypatch.delenv("ENV_DEBUG", raising=False) assert get_debug_mode() is False # --------------------------------------------------------------------------- # Module-level constants # --------------------------------------------------------------------------- def test_env_debug_is_bool() -> None: """ENV_DEBUG should be a bool.""" assert isinstance(ENV_DEBUG, bool) def test_app_version_is_string() -> None: """APP_VERSION should be a string.""" assert isinstance(APP_VERSION, str) def test_app_version_semver_format() -> None: """APP_VERSION should follow semver format X.Y.Z.""" assert re.match(r"^\d+\.\d+\.\d+", APP_VERSION), f"Not semver: {APP_VERSION!r}" def test_app_name_value() -> None: """APP_NAME should be 'X4 SavEd'.""" assert APP_NAME == "X4 SavEd" def test_app_title_contains_name_and_version() -> None: """APP_TITLE should contain APP_NAME and APP_VERSION.""" assert APP_NAME in APP_TITLE assert APP_VERSION in APP_TITLE def test_app_title_dev_suffix_when_debug(monkeypatch: pytest.MonkeyPatch) -> None: """APP_TITLE ends with '-DEV' when ENV_DEBUG is True.""" import importlib import src.constants as consts monkeypatch.setenv("ENV_DEBUG", "true") monkeypatch.setattr(consts, "ENV_DEBUG", True) title = f"{consts.APP_NAME} v{consts.APP_VERSION}" + ("-DEV" if True else "") assert title.endswith("-DEV") def test_app_title_no_dev_suffix_when_not_debug(monkeypatch: pytest.MonkeyPatch) -> None: """APP_TITLE does not end with '-DEV' when ENV_DEBUG is False.""" import src.constants as consts monkeypatch.setattr(consts, "ENV_DEBUG", False) title = f"{consts.APP_NAME} v{consts.APP_VERSION}" + ("-DEV" if False else "") assert not title.endswith("-DEV")