"""Tests for CLI entry point.""" from unittest.mock import MagicMock, patch import pytest import requests from gitea_dashboard.cli import main class TestMainNominal: """Test main() happy path.""" @patch("gitea_dashboard.cli.render_dashboard") @patch("gitea_dashboard.cli.collect_all") @patch("gitea_dashboard.cli.GiteaClient") def test_main_runs_full_pipeline(self, mock_client_cls, mock_collect, mock_render): """main() creates client, collects, and renders.""" env = {"GITEA_TOKEN": "test-token-123", "GITEA_URL": "http://localhost:3000"} mock_client = MagicMock() mock_client_cls.return_value = mock_client mock_collect.return_value = [] with patch.dict("os.environ", env, clear=False): main() mock_client_cls.assert_called_once_with("http://localhost:3000", "test-token-123") mock_collect.assert_called_once_with(mock_client) mock_render.assert_called_once_with(mock_collect.return_value) @patch("gitea_dashboard.cli.render_dashboard") @patch("gitea_dashboard.cli.collect_all") @patch("gitea_dashboard.cli.GiteaClient") def test_main_uses_default_url(self, mock_client_cls, mock_collect, mock_render): """main() uses default URL when GITEA_URL is not set.""" env = {"GITEA_TOKEN": "my-token"} mock_client_cls.return_value = MagicMock() mock_collect.return_value = [] with patch.dict("os.environ", env, clear=True): main() mock_client_cls.assert_called_once_with("http://192.168.0.106:3000", "my-token") class TestMainMissingToken: """Test main() when GITEA_TOKEN is not set.""" def test_error_message_when_token_missing(self, capsys): """main() exits with code 1 and prints message mentioning GITEA_TOKEN.""" with patch.dict("os.environ", {}, clear=True): with pytest.raises(SystemExit) as exc_info: main() assert exc_info.value.code == 1 captured = capsys.readouterr() assert "GITEA_TOKEN" in captured.err class TestMainConnectionErrors: """Test main() error handling for network issues.""" @patch("gitea_dashboard.cli.collect_all") @patch("gitea_dashboard.cli.GiteaClient") def test_connection_error_handled(self, mock_client_cls, mock_collect): """ConnectionError is caught and exits with code 1.""" env = {"GITEA_TOKEN": "test-token"} mock_client_cls.return_value = MagicMock() mock_collect.side_effect = requests.ConnectionError("Connection refused") with patch.dict("os.environ", env, clear=True): with pytest.raises(SystemExit) as exc_info: main() assert exc_info.value.code == 1 @patch("gitea_dashboard.cli.collect_all") @patch("gitea_dashboard.cli.GiteaClient") def test_timeout_error_handled(self, mock_client_cls, mock_collect): """Timeout is caught and exits with code 1.""" env = {"GITEA_TOKEN": "test-token"} mock_client_cls.return_value = MagicMock() mock_collect.side_effect = requests.Timeout("Request timed out") with patch.dict("os.environ", env, clear=True): with pytest.raises(SystemExit) as exc_info: main() assert exc_info.value.code == 1 @patch("gitea_dashboard.cli.collect_all") @patch("gitea_dashboard.cli.GiteaClient") def test_request_exception_handled(self, mock_client_cls, mock_collect): """Generic RequestException is caught and exits with code 1.""" env = {"GITEA_TOKEN": "test-token"} mock_client_cls.return_value = MagicMock() mock_collect.side_effect = requests.RequestException("Something went wrong") with patch.dict("os.environ", env, clear=True): with pytest.raises(SystemExit) as exc_info: main() assert exc_info.value.code == 1 @patch("gitea_dashboard.cli.collect_all") @patch("gitea_dashboard.cli.GiteaClient") def test_token_not_in_error_output(self, mock_client_cls, mock_collect, capsys): """Token must never appear in error messages, even when present in the exception.""" env = {"GITEA_TOKEN": "super-secret-token-xyz"} mock_client_cls.return_value = MagicMock() # Build exception message that embeds the token value from env # to simulate a real-world leak scenario def make_exc(environ): leaked = environ["GITEA_TOKEN"] return requests.RequestException(f"HTTP Error: Authorization token {leaked} rejected") import os as _os with patch.dict("os.environ", env, clear=True): mock_collect.side_effect = make_exc(_os.environ) with pytest.raises(SystemExit): main() captured = capsys.readouterr() assert env["GITEA_TOKEN"] not in captured.out assert env["GITEA_TOKEN"] not in captured.err