Public Access
Post-merge / detect-type (push) Successful in 19s
Post-merge / validate-commit-msg (push) Failing after 14s
Post-merge / configure-repo (push) Successful in 17s
Post-merge / release (push) Successful in 1m6s
Post-merge / vikunja (push) Successful in 20s
Post-merge / sync-wiki (push) Successful in 48s
Post-merge / badges (push) Successful in 1m12s
173 lines
6.5 KiB
Python
173 lines
6.5 KiB
Python
"""Unit tests for config module constants."""
|
|
|
|
import importlib
|
|
from pathlib import Path
|
|
|
|
from devx.config import (
|
|
CONVENTIONAL_RE,
|
|
DEFAULT_PER_PAGE,
|
|
DEFAULT_TIMEOUT,
|
|
GITEA_API_URL,
|
|
MAX_RETRIES,
|
|
RETRY_BACKOFF_BASE,
|
|
RETRY_STATUS_CODES,
|
|
TASK_ID_RE,
|
|
TASK_PREFIX,
|
|
VIKUNJA_API_URL,
|
|
)
|
|
|
|
|
|
class TestConfigConstants:
|
|
def test_api_urls(self) -> None:
|
|
assert "api/v1" in GITEA_API_URL
|
|
assert "api/v1" in VIKUNJA_API_URL
|
|
|
|
def test_timeouts(self) -> None:
|
|
assert DEFAULT_TIMEOUT == 30
|
|
assert DEFAULT_PER_PAGE == 50
|
|
|
|
def test_task_prefix(self) -> None:
|
|
assert TASK_PREFIX == "DEVX"
|
|
|
|
def test_task_id_re(self) -> None:
|
|
assert TASK_ID_RE.search("DEVX-1")
|
|
assert TASK_ID_RE.search("DEVX-123")
|
|
assert not TASK_ID_RE.search("DEVX-")
|
|
assert not TASK_ID_RE.search("other text")
|
|
|
|
def test_conventional_re(self) -> None:
|
|
assert CONVENTIONAL_RE.match("feat: add feature")
|
|
assert CONVENTIONAL_RE.match("fix(scope): bug fix")
|
|
assert not CONVENTIONAL_RE.match("random message")
|
|
assert not CONVENTIONAL_RE.match("feat:")
|
|
assert not CONVENTIONAL_RE.match("BREAKING CHANGE: something")
|
|
|
|
def test_retry_config(self) -> None:
|
|
assert MAX_RETRIES == 3
|
|
assert RETRY_BACKOFF_BASE == 2
|
|
assert 429 in RETRY_STATUS_CODES
|
|
assert 500 in RETRY_STATUS_CODES
|
|
assert 502 in RETRY_STATUS_CODES
|
|
assert 503 in RETRY_STATUS_CODES
|
|
assert 504 in RETRY_STATUS_CODES
|
|
|
|
|
|
class TestPyprojectReading:
|
|
"""Test that config.py reads [tool.devx] from pyproject.toml."""
|
|
|
|
def test_pyproject_provides_values(self) -> None:
|
|
"""When pyproject.toml has [tool.devx], values are read from it."""
|
|
import devx.config as cfg
|
|
|
|
# devx's own pyproject.toml has task_prefix=DEVX, vikunja_project_id=8
|
|
assert cfg.TASK_PREFIX == "DEVX"
|
|
assert cfg.VIKUNJA_PROJECT_ID == 8
|
|
assert cfg.REPO_OWNER == "oblachno-oss"
|
|
|
|
def test_env_overrides_pyproject(self, monkeypatch: object) -> None:
|
|
"""Env vars take priority over pyproject.toml."""
|
|
monkeypatch.setenv("DEVX_TASK_PREFIX", "CUSTOM")
|
|
import devx.config as cfg
|
|
|
|
importlib.reload(cfg)
|
|
assert cfg.TASK_PREFIX == "CUSTOM"
|
|
assert cfg.TASK_ID_RE.search("CUSTOM-42")
|
|
monkeypatch.delenv("DEVX_TASK_PREFIX", raising=False)
|
|
importlib.reload(cfg)
|
|
|
|
def test_no_pyproject_falls_back_to_defaults(self, monkeypatch: object, tmp_path: Path) -> None:
|
|
"""When no pyproject.toml exists, defaults are used."""
|
|
monkeypatch.chdir(tmp_path)
|
|
monkeypatch.delenv("DEVX_TASK_PREFIX", raising=False)
|
|
monkeypatch.delenv("DEVX_VIKUNJA_PROJECT_ID", raising=False)
|
|
monkeypatch.delenv("DEVX_REPO_OWNER", raising=False)
|
|
import devx.config as cfg
|
|
|
|
importlib.reload(cfg)
|
|
assert cfg.TASK_PREFIX == "DEVX"
|
|
assert cfg.VIKUNJA_PROJECT_ID == 6
|
|
assert cfg.REPO_OWNER == ""
|
|
importlib.reload(cfg)
|
|
|
|
def test_invalid_toml_falls_back_to_defaults(self, monkeypatch: object, tmp_path: Path) -> None:
|
|
"""When pyproject.toml is invalid TOML, defaults are used."""
|
|
(tmp_path / "pyproject.toml").write_text("invalid toml {{{")
|
|
monkeypatch.chdir(tmp_path)
|
|
monkeypatch.delenv("DEVX_TASK_PREFIX", raising=False)
|
|
import devx.config as cfg
|
|
|
|
importlib.reload(cfg)
|
|
assert cfg.TASK_PREFIX == "DEVX"
|
|
importlib.reload(cfg)
|
|
|
|
def test_no_devx_section_falls_back_to_defaults(self, monkeypatch: object, tmp_path: Path) -> None:
|
|
"""When pyproject.toml has no [tool.devx], defaults are used."""
|
|
(tmp_path / "pyproject.toml").write_text('[project]\nname = "test"\n')
|
|
monkeypatch.chdir(tmp_path)
|
|
monkeypatch.delenv("DEVX_TASK_PREFIX", raising=False)
|
|
monkeypatch.delenv("DEVX_VIKUNJA_PROJECT_ID", raising=False)
|
|
import devx.config as cfg
|
|
|
|
importlib.reload(cfg)
|
|
assert cfg.TASK_PREFIX == "DEVX"
|
|
assert cfg.VIKUNJA_PROJECT_ID == 6
|
|
importlib.reload(cfg)
|
|
|
|
def test_pyproject_int_value_used(self, monkeypatch: object, tmp_path: Path) -> None:
|
|
"""When pyproject.toml has an int value, it is used (covers _get_int return)."""
|
|
(tmp_path / "pyproject.toml").write_text('[project]\nname = "test"\n[tool.devx]\nvikunja_project_id = 42\n')
|
|
monkeypatch.chdir(tmp_path)
|
|
monkeypatch.delenv("DEVX_VIKUNJA_PROJECT_ID", raising=False)
|
|
import devx.config as cfg
|
|
|
|
importlib.reload(cfg)
|
|
assert cfg.VIKUNJA_PROJECT_ID == 42
|
|
importlib.reload(cfg)
|
|
|
|
def test_env_int_override(self, monkeypatch: object, tmp_path: Path) -> None:
|
|
"""Env var override for int config takes priority over pyproject.toml."""
|
|
(tmp_path / "pyproject.toml").write_text('[project]\nname = "test"\n[tool.devx]\nvikunja_project_id = 42\n')
|
|
monkeypatch.chdir(tmp_path)
|
|
monkeypatch.setenv("DEVX_VIKUNJA_PROJECT_ID", "99")
|
|
import devx.config as cfg
|
|
|
|
importlib.reload(cfg)
|
|
assert cfg.VIKUNJA_PROJECT_ID == 99
|
|
importlib.reload(cfg)
|
|
|
|
def test_tool_not_dict_falls_back_to_defaults(self, monkeypatch: object, tmp_path: Path) -> None:
|
|
"""When [tool] is not a dict, defaults are used."""
|
|
(tmp_path / "pyproject.toml").write_text('tool = "not a dict"\n')
|
|
monkeypatch.chdir(tmp_path)
|
|
monkeypatch.delenv("DEVX_TASK_PREFIX", raising=False)
|
|
import devx.config as cfg
|
|
|
|
importlib.reload(cfg)
|
|
assert cfg.TASK_PREFIX == "DEVX"
|
|
importlib.reload(cfg)
|
|
|
|
def test_devx_not_dict_falls_back_to_defaults(self, monkeypatch: object, tmp_path: Path) -> None:
|
|
"""When [tool.devx] is not a dict, defaults are used."""
|
|
(tmp_path / "pyproject.toml").write_text('[tool]\ndevx = "not a dict"\n')
|
|
monkeypatch.chdir(tmp_path)
|
|
monkeypatch.delenv("DEVX_TASK_PREFIX", raising=False)
|
|
import devx.config as cfg
|
|
|
|
importlib.reload(cfg)
|
|
assert cfg.TASK_PREFIX == "DEVX"
|
|
importlib.reload(cfg)
|
|
|
|
|
|
class TestTaskPrefixOverride:
|
|
def test_task_prefix_from_env(self, monkeypatch: object) -> None:
|
|
"""Verify TASK_PREFIX reads from DEVX_TASK_PREFIX env var."""
|
|
monkeypatch.setenv("DEVX_TASK_PREFIX", "INFRA")
|
|
import devx.config as cfg
|
|
|
|
importlib.reload(cfg)
|
|
assert cfg.TASK_PREFIX == "INFRA"
|
|
assert cfg.TASK_ID_RE.search("INFRA-42")
|
|
assert not cfg.TASK_ID_RE.search("DEVX-42")
|
|
monkeypatch.delenv("DEVX_TASK_PREFIX", raising=False)
|
|
importlib.reload(cfg)
|