"""Unit tests for the operation report tracker.""" from __future__ import annotations from unittest.mock import 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: with patch("gitea_runner_manager.report.say") as mock_say: with track_steps() as tracker: tracker.begin("step1") tracker.done() tracker.begin("step2") tracker.done() messages = [call.args[0] for call in mock_say.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: with patch("gitea_runner_manager.report.say") as mock_say: with pytest.raises(AnsibleError, match="fail"): with track_steps() as tracker: tracker.begin("step1") tracker.done() tracker.begin("step2") raise AnsibleError("fail") messages = [call.args[0] for call in mock_say.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: with patch("gitea_runner_manager.report.say") as mock_say: with pytest.raises(AnsibleError, match="fail"): with track_steps() as tracker: tracker.begin("step1") raise AnsibleError("fail") messages = [call.args[0] for call in mock_say.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: with patch("gitea_runner_manager.report.say") as mock_say: with track_steps(): pass messages = [call.args[0] for call in mock_say.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") with patch("gitea_runner_manager.report.say") as mock_say: with track_steps() as tracker: tracker.begin("step1") tracker.done() messages = [call.args[0] for call in mock_say.call_args_list] assert any("Отчет за операцията" in msg for msg in messages) assert any("завършено" in msg for msg in messages)