Files
grm/tests/unit/test_report.py
T
Emil Simeonov 3b3e002f7f GRM-13: feat: replace print() with stdlib logging module
- Create logging_config.py with get_logger() providing dual handlers:
  - Console handler (stderr) controlled by GRM_LOG_LEVEL env var (default INFO)
  - File handler (~/.local/state/grm/logs/grm.log) capturing everything at DEBUG
- Replace all print() calls in executor.py and report.py with logger.info()/error()
- Add error logging before raising AnsibleError in executor.run()
- Add GRM_LOG_LEVEL to README configuration table and logging documentation
- Update all unit tests to mock logger instead of using capsys
- 114 tests, 100% coverage, pyright clean, ruff clean
2026-06-19 01:24:35 +02:00

114 lines
4.3 KiB
Python

"""Unit tests for the operation report tracker."""
from __future__ import annotations
from unittest.mock import MagicMock, patch
import pytest
from gitea_runner_manager.exceptions import AnsibleError
from gitea_runner_manager.report import Step, StepTracker, track_steps
class TestStep:
def test_init(self) -> None:
step = Step("test")
assert step.name == "test"
assert step.status == "pending"
class TestStepTracker:
def test_begin(self) -> None:
tracker = StepTracker()
tracker.begin("step1")
assert len(tracker.steps) == 1
assert tracker.steps[0].name == "step1"
assert tracker.steps[0].status == "in_progress"
def test_done(self) -> None:
tracker = StepTracker()
tracker.begin("step1")
tracker.done()
assert tracker.steps[0].status == "completed"
def test_done_no_steps(self) -> None:
tracker = StepTracker()
tracker.done() # should not raise
assert tracker.steps == []
def test_done_last_not_in_progress(self) -> None:
tracker = StepTracker()
tracker.begin("step1")
tracker.done()
tracker.done() # second done is a no-op
assert tracker.steps[0].status == "completed"
class TestTrackSteps:
def test_success(self) -> None:
mock_logger = MagicMock()
with patch("gitea_runner_manager.report.get_logger", return_value=mock_logger):
with track_steps() as tracker:
tracker.begin("step1")
tracker.done()
tracker.begin("step2")
tracker.done()
messages = [call.args[0] for call in mock_logger.info.call_args_list]
assert any("Operation Report" in msg for msg in messages)
assert any("step1" in msg for msg in messages)
assert any("step2" in msg for msg in messages)
assert any("✓" in msg for msg in messages)
assert any("completed" in msg for msg in messages)
def test_failure_marks_step(self) -> None:
mock_logger = MagicMock()
with pytest.raises(AnsibleError, match="fail"):
with patch("gitea_runner_manager.report.get_logger", return_value=mock_logger):
with track_steps() as tracker:
tracker.begin("step1")
tracker.done()
tracker.begin("step2")
raise AnsibleError("fail")
messages = [call.args[0] for call in mock_logger.info.call_args_list]
assert any("step1" in msg for msg in messages)
assert any("step2" in msg for msg in messages)
assert any("✓" in msg for msg in messages)
assert any("✗" in msg for msg in messages)
assert any("completed" in msg for msg in messages)
assert any("failed" in msg for msg in messages)
def test_pending_shown_on_failure(self) -> None:
mock_logger = MagicMock()
with pytest.raises(AnsibleError, match="fail"):
with patch("gitea_runner_manager.report.get_logger", return_value=mock_logger):
with track_steps() as tracker:
tracker.begin("step1")
raise AnsibleError("fail")
messages = [call.args[0] for call in mock_logger.info.call_args_list]
assert any("✗" in msg for msg in messages)
assert any("failed" in msg for msg in messages)
def test_empty_report(self) -> None:
mock_logger = MagicMock()
with patch("gitea_runner_manager.report.get_logger", return_value=mock_logger):
with track_steps():
pass
messages = [call.args[0] for call in mock_logger.info.call_args_list]
assert any("Operation Report" in msg for msg in messages)
def test_translated_report(self, monkeypatch: pytest.MonkeyPatch) -> None:
monkeypatch.setenv("GRM_LANG", "bg")
mock_logger = MagicMock()
with patch("gitea_runner_manager.report.get_logger", return_value=mock_logger):
with track_steps() as tracker:
tracker.begin("step1")
tracker.done()
messages = [call.args[0] for call in mock_logger.info.call_args_list]
assert any("Отчет за операцията" in msg for msg in messages)
assert any("завършено" in msg for msg in messages)