"""Unit tests for devx.tools.pr_label.""" from __future__ import annotations from unittest.mock import MagicMock, patch import pytest from click.testing import CliRunner from devx.tools.pr_label import cli class TestCli: @patch("devx.tools.pr_status.subprocess.run") def test_no_token_raises(self, mock_subproc: MagicMock, monkeypatch: pytest.MonkeyPatch) -> None: for name in ("DEVELOPER_GITEA_API_TOKEN", "CI_GITEA_API_TOKEN", "CI_GITEA_TOKEN"): monkeypatch.delenv(name, raising=False) runner = CliRunner() result = runner.invoke( cli, ["--pr", "42", "--label", "ready-to-merge"], env={"DEVELOPER_GITEA_API_TOKEN": "", "CI_GITEA_API_TOKEN": "", "CI_GITEA_TOKEN": ""}, ) assert result.exit_code != 0 assert "CI_GITEA_TOKEN" in result.output @patch("devx.tools.pr_status.subprocess.run") @patch("devx.tools.pr_label.REPO_OWNER", "") def test_no_owner_raises(self, mock_subproc: MagicMock, monkeypatch: pytest.MonkeyPatch) -> None: monkeypatch.setenv("CI_GITEA_TOKEN", "tok") runner = CliRunner() result = runner.invoke(cli, ["--pr", "42", "--label", "ready-to-merge"]) assert result.exit_code != 0 assert "owner" in result.output.lower() @patch("devx.tools.pr_status.subprocess.run") @patch("devx.tools.pr_label.GiteaClient") def test_adds_new_label( self, mock_client_cls: MagicMock, mock_subproc: MagicMock, monkeypatch: pytest.MonkeyPatch ) -> None: monkeypatch.setenv("CI_GITEA_TOKEN", "tok") monkeypatch.setenv("DEVX_REPO_OWNER", "owner") monkeypatch.setenv("DEVX_REPO_NAME", "repo") client = mock_client_cls.return_value client.get_pr_label_names.return_value = [] runner = CliRunner() result = runner.invoke(cli, ["--pr", "42", "--label", "ready-to-merge"]) assert result.exit_code == 0 client.add_pr_label.assert_called_once_with(42, ["ready-to-merge"]) assert "Added label" in result.output @patch("devx.tools.pr_status.subprocess.run") @patch("devx.tools.pr_label.GiteaClient") def test_skips_existing_label( self, mock_client_cls: MagicMock, mock_subproc: MagicMock, monkeypatch: pytest.MonkeyPatch ) -> None: monkeypatch.setenv("CI_GITEA_TOKEN", "tok") monkeypatch.setenv("DEVX_REPO_OWNER", "owner") monkeypatch.setenv("DEVX_REPO_NAME", "repo") client = mock_client_cls.return_value client.get_pr_label_names.return_value = ["ready-to-merge"] runner = CliRunner() result = runner.invoke(cli, ["--pr", "42", "--label", "ready-to-merge"]) assert result.exit_code == 0 client.add_pr_label.assert_not_called() assert "already" in result.output @patch("devx.tools.pr_status.subprocess.run") @patch("devx.tools.pr_label.GiteaClient") def test_mixed_new_and_existing( self, mock_client_cls: MagicMock, mock_subproc: MagicMock, monkeypatch: pytest.MonkeyPatch ) -> None: monkeypatch.setenv("CI_GITEA_TOKEN", "tok") monkeypatch.setenv("DEVX_REPO_OWNER", "owner") monkeypatch.setenv("DEVX_REPO_NAME", "repo") client = mock_client_cls.return_value client.get_pr_label_names.return_value = ["reviewed"] runner = CliRunner() result = runner.invoke(cli, ["--pr", "42", "--label", "ready-to-merge", "--label", "reviewed"]) assert result.exit_code == 0 client.add_pr_label.assert_called_once_with(42, ["ready-to-merge"]) assert "Added label" in result.output assert "already" in result.output @patch("devx.tools.pr_label.GiteaClient") @patch("devx.tools.pr_status.subprocess.run") def test_auto_detect_pr( self, mock_subprocess: MagicMock, mock_client_cls: MagicMock, monkeypatch: pytest.MonkeyPatch ) -> None: monkeypatch.setenv("CI_GITEA_TOKEN", "tok") monkeypatch.setenv("DEVX_REPO_OWNER", "owner") monkeypatch.setenv("DEVX_REPO_NAME", "repo") mock_subprocess.return_value = MagicMock(returncode=0, stdout="feature-branch\n") client = mock_client_cls.return_value client.list_prs.return_value = [{"number": 42, "head": {"ref": "feature-branch"}}] client.get_pr_label_names.return_value = [] runner = CliRunner() result = runner.invoke(cli, ["--label", "ready-to-merge"]) assert result.exit_code == 0 client.add_pr_label.assert_called_once_with(42, ["ready-to-merge"])