feat: status translation overrides via YAML config
report.status_translation in YAML config overrides/extends the builtin
STATUS_TRANSLATION dictionary; without the section behavior is unchanged.
- AppConfig.report_status_translation + _resolve_str_dict (warns and
skips non-scalar values, resolves ${VAR} references)
- Config.get_status_translation() returns merged copy (lazy import,
builtin dict never mutated)
- build_grouped_report() accepts optional status_translation parameter
- --init-config template gains commented example, docs/CONFIG.md updated
Closes #65
This commit is contained in:
@@ -483,6 +483,55 @@ class TestInitConfig:
|
||||
assert "report" in data
|
||||
assert data["report"]["no_time"] is False
|
||||
|
||||
@mock.patch.dict(os.environ, VALID_ENV, clear=True)
|
||||
def test_init_config_includes_status_translation_example(self, tmp_path):
|
||||
"""#65: --init-config дописывает закомментированный пример status_translation.
|
||||
|
||||
Файл остаётся валидным YAML, активной секции status_translation нет.
|
||||
"""
|
||||
import yaml
|
||||
|
||||
config_path = tmp_path / "config.yml"
|
||||
code = main(["--init-config", "--config-path", str(config_path)])
|
||||
assert code == 0
|
||||
|
||||
text = config_path.read_text(encoding="utf-8")
|
||||
assert "# status_translation:" in text
|
||||
|
||||
with open(config_path) as fh:
|
||||
data = yaml.safe_load(fh)
|
||||
|
||||
assert "status_translation" not in data["report"]
|
||||
|
||||
|
||||
@mock.patch.dict(os.environ, VALID_ENV, clear=True)
|
||||
@mock.patch("redmine_reporter.cli.fetch_issues_with_spent_time")
|
||||
def test_cli_status_translation_from_yaml_reaches_builder(mock_fetch, tmp_path):
|
||||
"""#65: report.status_translation из YAML доезжает до build_grouped_report."""
|
||||
issue = _MockIssue()
|
||||
mock_fetch.return_value = [(issue, 1.0, None)]
|
||||
config_path = tmp_path / "config.yml"
|
||||
config_path.write_text(
|
||||
"report:\n"
|
||||
" status_translation:\n"
|
||||
' New: "Новая"\n'
|
||||
' "Custom Status": "Кастом"\n'
|
||||
)
|
||||
|
||||
with mock.patch("redmine_reporter.cli.build_grouped_report") as mock_build:
|
||||
mock_build.return_value = []
|
||||
code = main(
|
||||
["--date", "2026-01-01--2026-01-31", "--config-path", str(config_path)]
|
||||
)
|
||||
|
||||
assert code == 0
|
||||
_, kwargs = mock_build.call_args
|
||||
translation = kwargs["status_translation"]
|
||||
assert translation["New"] == "Новая"
|
||||
assert translation["Custom Status"] == "Кастом"
|
||||
# встроенные переводы сохранились
|
||||
assert translation["Closed"] == "Закрыто"
|
||||
|
||||
|
||||
@mock.patch.dict(os.environ, {}, clear=True)
|
||||
@mock.patch("redmine_reporter.cli.fetch_issues_with_spent_time")
|
||||
|
||||
Reference in New Issue
Block a user