Public Access
DEVX-61: feat: single-source-of-truth config via [tool.devx] in pyproject.toml
Post-merge / detect-type (push) Successful in 6s
Post-merge / validate-commit-msg (push) Successful in 7s
Post-merge / configure-repo (push) Successful in 12s
Post-merge / release (push) Failing after 48s
Post-merge / sync-wiki (push) Has been skipped
Post-merge / vikunja (push) Has been skipped
Post-merge / badges (push) Successful in 48s
Post-merge / detect-type (push) Successful in 6s
Post-merge / validate-commit-msg (push) Successful in 7s
Post-merge / configure-repo (push) Successful in 12s
Post-merge / release (push) Failing after 48s
Post-merge / sync-wiki (push) Has been skipped
Post-merge / vikunja (push) Has been skipped
Post-merge / badges (push) Successful in 48s
This commit was merged in pull request #99.
This commit is contained in:
+84
-25
@@ -1,12 +1,14 @@
|
||||
"""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,
|
||||
REPO_OWNER,
|
||||
RETRY_BACKOFF_BASE,
|
||||
RETRY_STATUS_CODES,
|
||||
TASK_ID_RE,
|
||||
@@ -20,25 +22,10 @@ class TestConfigConstants:
|
||||
assert "api/v1" in GITEA_API_URL
|
||||
assert "api/v1" in VIKUNJA_API_URL
|
||||
|
||||
def test_project_ids(self, monkeypatch: object) -> None:
|
||||
"""VIKUNJA_PROJECT_ID defaults to 6 when DEVX_VIKUNJA_PROJECT_ID is not set."""
|
||||
monkeypatch.delenv("DEVX_VIKUNJA_PROJECT_ID", raising=False)
|
||||
import importlib
|
||||
|
||||
import devx.config as cfg
|
||||
|
||||
importlib.reload(cfg)
|
||||
assert cfg.VIKUNJA_PROJECT_ID == 6
|
||||
# Restore module state
|
||||
importlib.reload(cfg)
|
||||
|
||||
def test_timeouts(self) -> None:
|
||||
assert DEFAULT_TIMEOUT == 30
|
||||
assert DEFAULT_PER_PAGE == 50
|
||||
|
||||
def test_owner(self) -> None:
|
||||
assert REPO_OWNER == ""
|
||||
|
||||
def test_task_prefix(self) -> None:
|
||||
assert TASK_PREFIX == "DEVX"
|
||||
|
||||
@@ -64,28 +51,100 @@ class TestConfigConstants:
|
||||
assert 503 in RETRY_STATUS_CODES
|
||||
assert 504 in RETRY_STATUS_CODES
|
||||
|
||||
def test_env_var_override(self, monkeypatch: object) -> None:
|
||||
"""Test that env vars override defaults at import time."""
|
||||
# We can't easily re-import the module, but we can verify
|
||||
# the constants respect env vars by checking the module source.
|
||||
|
||||
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
|
||||
|
||||
assert cfg.GITEA_API_URL # always non-empty
|
||||
assert cfg.VIKUNJA_API_URL # always non-empty
|
||||
# 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_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 importlib
|
||||
|
||||
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")
|
||||
# Restore
|
||||
monkeypatch.delenv("DEVX_TASK_PREFIX", raising=False)
|
||||
importlib.reload(cfg)
|
||||
|
||||
Reference in New Issue
Block a user