Files
redmine-reporter/tests/test_yaml_config.py
Кокос Артем Николаевич c962a93f30 feat: YAML config support (~/.config/redmine-reporter/config.yml)
- Add AppConfig/SmtpConfig/EmailConfig dataclasses with from_yaml()/from_env()
- Add yaml_config.py: ${VAR} resolver, 0700/0600 permission helpers
- Config.get_*() methods gain YAML fallback in priority chain
- Priority: CLI > env > .env > YAML > code defaults
- CLI --init-config generates YAML from current environment
- --force flag allows overwriting existing config
- Secrets default to ${VAR} references, plaintext allowed
- Full backward compatibility with existing .env setups

Closes #46
2026-07-03 18:00:28 +07:00

81 lines
2.7 KiB
Python

import logging
import os
import tempfile
from pathlib import Path
from unittest import mock
from redmine_reporter.yaml_config import (
check_file_permissions,
ensure_config_dir,
resolve_env_vars,
)
class TestResolveEnvVars:
"""Tests for ${VAR} resolution."""
@mock.patch.dict(os.environ, {"MY_VAR": "hello"}, clear=True)
def test_replaces_var_with_env_value(self):
assert resolve_env_vars("prefix_${MY_VAR}_suffix") == "prefix_hello_suffix"
@mock.patch.dict(os.environ, {}, clear=True)
def test_missing_var_returns_empty_and_warns(self, caplog):
with caplog.at_level(logging.WARNING):
result = resolve_env_vars("${MISSING_VAR}")
assert result == ""
assert "MISSING_VAR" in caplog.text
def test_no_braces_returns_unchanged(self):
assert resolve_env_vars("plain text no vars") == "plain text no vars"
@mock.patch.dict(os.environ, {"A": "${B}", "B": "final"}, clear=True)
def test_non_recursive_no_double_resolution(self):
# A → literal "${B}", B → "final". Non-recursive means we get "${B}", not "final".
result = resolve_env_vars("${A}")
assert result == "${B}"
class TestEnsureConfigDir:
"""Tests for config directory creation."""
def test_creates_dir_with_0700(self):
with tempfile.TemporaryDirectory() as tmp:
config_dir = Path(tmp) / "test_config"
result = ensure_config_dir(config_dir)
assert result == config_dir
assert result.exists()
assert result.is_dir()
mode = result.stat().st_mode & 0o777
assert mode == 0o700
def test_noop_if_dir_exists(self):
with tempfile.TemporaryDirectory() as tmp:
config_dir = Path(tmp) / "existing"
config_dir.mkdir(mode=0o700)
mtime_before = config_dir.stat().st_mtime
result = ensure_config_dir(config_dir)
assert result == config_dir
assert config_dir.stat().st_mtime == mtime_before
class TestCheckFilePermissions:
"""Tests for config file permission checks."""
def test_warns_on_permissive_file(self):
with tempfile.TemporaryDirectory() as tmp:
f = Path(tmp) / "open.yml"
f.write_text("key: value")
f.chmod(0o644)
warnings = check_file_permissions(f)
assert len(warnings) >= 1
assert "0644" in warnings[0] or "permissions" in warnings[0].lower()
def test_silent_on_0600(self):
with tempfile.TemporaryDirectory() as tmp:
f = Path(tmp) / "secure.yml"
f.write_text("key: value")
f.chmod(0o600)
warnings = check_file_permissions(f)
assert warnings == []