GRM-12: feat: add translated operation report for success and failure cases
- Create StepTracker context manager in new report.py module - Track each step of lifecycle operations (install, update, start, stop, enable, disable, status, remove) with begin/done markers - On success: report lists all completed steps with ✓ icons - On failure: report shows failed step (✗), completed steps before failure (✓), and pending steps that never started (○) - Add translations for report header, status labels, and registry step names in all 5 supported languages (EN/BG/DE/RU/ZH) - 105 tests, 100% coverage, pyright clean, ruff clean
This commit is contained in:
@@ -0,0 +1,102 @@
|
||||
"""Unit tests for the operation report tracker."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
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, capsys: pytest.CaptureFixture[str]) -> None:
|
||||
with track_steps() as tracker:
|
||||
tracker.begin("step1")
|
||||
tracker.done()
|
||||
tracker.begin("step2")
|
||||
tracker.done()
|
||||
|
||||
captured = capsys.readouterr()
|
||||
assert "Operation Report" in captured.out
|
||||
assert "step1" in captured.out
|
||||
assert "step2" in captured.out
|
||||
assert "✓" in captured.out
|
||||
assert "completed" in captured.out
|
||||
|
||||
def test_failure_marks_step(self, capsys: pytest.CaptureFixture[str]) -> None:
|
||||
with pytest.raises(AnsibleError, match="fail"):
|
||||
with track_steps() as tracker:
|
||||
tracker.begin("step1")
|
||||
tracker.done()
|
||||
tracker.begin("step2")
|
||||
raise AnsibleError("fail")
|
||||
|
||||
captured = capsys.readouterr()
|
||||
assert "Operation Report" in captured.out
|
||||
assert "step1" in captured.out
|
||||
assert "step2" in captured.out
|
||||
assert "✓" in captured.out
|
||||
assert "✗" in captured.out
|
||||
assert "completed" in captured.out
|
||||
assert "failed" in captured.out
|
||||
|
||||
def test_pending_shown_on_failure(self, capsys: pytest.CaptureFixture[str]) -> None:
|
||||
with pytest.raises(AnsibleError, match="fail"):
|
||||
with track_steps() as tracker:
|
||||
tracker.begin("step1")
|
||||
raise AnsibleError("fail")
|
||||
|
||||
captured = capsys.readouterr()
|
||||
assert "✗" in captured.out
|
||||
assert "failed" in captured.out
|
||||
|
||||
def test_empty_report(self, capsys: pytest.CaptureFixture[str]) -> None:
|
||||
with track_steps():
|
||||
pass
|
||||
|
||||
captured = capsys.readouterr()
|
||||
assert "Operation Report" in captured.out
|
||||
|
||||
def test_translated_report(self, monkeypatch: pytest.MonkeyPatch, capsys: pytest.CaptureFixture[str]) -> None:
|
||||
monkeypatch.setenv("GRM_LANG", "bg")
|
||||
with track_steps() as tracker:
|
||||
tracker.begin("step1")
|
||||
tracker.done()
|
||||
|
||||
captured = capsys.readouterr()
|
||||
assert "Отчет за операцията" in captured.out
|
||||
assert "завършено" in captured.out
|
||||
Reference in New Issue
Block a user