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 == []