Public Access
497 lines
22 KiB
Python
497 lines
22 KiB
Python
"""Unit tests for devx/gitea_cli.py."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
from unittest.mock import MagicMock, patch
|
|
|
|
import pytest
|
|
|
|
from devx.gitea_cli import (
|
|
TeaCLI,
|
|
TeaCLIError,
|
|
_extract_issue_number,
|
|
_extract_pr_number,
|
|
configure_tea_login,
|
|
)
|
|
|
|
|
|
class TestExtractIssueNumber:
|
|
def test_extract_from_created_issue(self) -> None:
|
|
assert _extract_issue_number("Created issue #42: Bug title") == 42
|
|
|
|
def test_extract_no_hash(self) -> None:
|
|
assert _extract_issue_number("No issue number here") == 0
|
|
|
|
def test_extract_multiple_hashes(self) -> None:
|
|
assert _extract_issue_number("Issue #5 and PR #10") == 5
|
|
|
|
def test_extract_with_colon(self) -> None:
|
|
assert _extract_issue_number("Created issue #7: title") == 7
|
|
|
|
def test_extract_invalid_number(self) -> None:
|
|
assert _extract_issue_number("Issue #abc: title") == 0
|
|
|
|
|
|
class TestExtractPrNumber:
|
|
def test_extract_from_created_pr(self) -> None:
|
|
assert _extract_pr_number("Created PR #128: Feature") == 128
|
|
|
|
def test_extract_no_number(self) -> None:
|
|
assert _extract_pr_number("No PR number") == 0
|
|
|
|
|
|
class TestTeaCLIInit:
|
|
def test_auto_detect_tea(self) -> None:
|
|
with patch("shutil.which", return_value="/usr/bin/tea"):
|
|
cli = TeaCLI()
|
|
assert cli._tea == "/usr/bin/tea"
|
|
|
|
def test_explicit_tea_bin(self) -> None:
|
|
cli = TeaCLI(tea_bin="/custom/tea")
|
|
assert cli._tea == "/custom/tea"
|
|
|
|
def test_fallback_to_tea(self) -> None:
|
|
with patch("shutil.which", return_value=None):
|
|
cli = TeaCLI()
|
|
assert cli._tea == "tea"
|
|
|
|
def test_with_repo(self) -> None:
|
|
cli = TeaCLI(repo="owner/repo")
|
|
assert cli._repo == "owner/repo"
|
|
|
|
|
|
class TestTeaCLIRun:
|
|
def test_run_success_json(self) -> None:
|
|
cli = TeaCLI(tea_bin="/fake/tea")
|
|
mock_result = MagicMock(returncode=0, stdout='[{"id": 1}]', stderr="")
|
|
with patch("subprocess.run", return_value=mock_result):
|
|
output = cli._run(["labels", "list"])
|
|
assert output == '[{"id": 1}]'
|
|
|
|
def test_run_success_raw(self) -> None:
|
|
cli = TeaCLI(tea_bin="/fake/tea")
|
|
mock_result = MagicMock(returncode=0, stdout="Created issue #42", stderr="")
|
|
with patch("subprocess.run", return_value=mock_result):
|
|
output = cli._run_raw(["issues", "create"])
|
|
assert output == "Created issue #42"
|
|
|
|
def test_run_failure_raises(self) -> None:
|
|
cli = TeaCLI(tea_bin="/fake/tea")
|
|
mock_result = MagicMock(returncode=1, stdout="", stderr="auth error")
|
|
with patch("subprocess.run", return_value=mock_result):
|
|
with pytest.raises(TeaCLIError, match="auth error"):
|
|
cli._run(["labels", "list"])
|
|
|
|
def test_run_failure_includes_stdout(self) -> None:
|
|
"""tea writes some errors to stdout (e.g. 'no available login')."""
|
|
cli = TeaCLI(tea_bin="/fake/tea")
|
|
mock_result = MagicMock(returncode=1, stdout="no available login", stderr="")
|
|
with patch("subprocess.run", return_value=mock_result):
|
|
with pytest.raises(TeaCLIError, match="no available login"):
|
|
cli._run(["releases", "create"])
|
|
|
|
def test_run_failure_includes_both_stdout_and_stderr(self) -> None:
|
|
"""When both stdout and stderr have content, both are included."""
|
|
cli = TeaCLI(tea_bin="/fake/tea")
|
|
mock_result = MagicMock(returncode=1, stdout="partial error", stderr="auth error")
|
|
with patch("subprocess.run", return_value=mock_result):
|
|
with pytest.raises(TeaCLIError, match="partial error"):
|
|
cli._run(["labels", "list"])
|
|
with patch("subprocess.run", return_value=mock_result):
|
|
with pytest.raises(TeaCLIError, match="auth error"):
|
|
cli._run(["labels", "list"])
|
|
|
|
def test_run_tea_not_found_raises_tea_error(self) -> None:
|
|
cli = TeaCLI(tea_bin="tea")
|
|
with patch("subprocess.run", side_effect=FileNotFoundError("tea not found")):
|
|
with pytest.raises(TeaCLIError, match="tea binary not found"):
|
|
cli._run(["labels", "list"])
|
|
|
|
def test_run_includes_json_flag(self) -> None:
|
|
cli = TeaCLI(tea_bin="/fake/tea")
|
|
mock_result = MagicMock(returncode=0, stdout="[]", stderr="")
|
|
with patch("subprocess.run", return_value=mock_result) as mock_run:
|
|
cli._run(["labels", "list"])
|
|
cmd = mock_run.call_args[0][0]
|
|
assert "--output" in cmd
|
|
assert "json" in cmd
|
|
|
|
def test_run_raw_no_json_flag(self) -> None:
|
|
cli = TeaCLI(tea_bin="/fake/tea")
|
|
mock_result = MagicMock(returncode=0, stdout="ok", stderr="")
|
|
with patch("subprocess.run", return_value=mock_result) as mock_run:
|
|
cli._run_raw(["whoami"])
|
|
cmd = mock_run.call_args[0][0]
|
|
assert "--output" not in cmd
|
|
|
|
def test_run_retries_on_502(self) -> None:
|
|
"""Transient 502 errors should be retried, then succeed."""
|
|
cli = TeaCLI(tea_bin="/fake/tea")
|
|
fail_result = MagicMock(returncode=1, stdout="", stderr="502 Bad Gateway")
|
|
success_result = MagicMock(returncode=0, stdout='[{"id": 1}]', stderr="")
|
|
with patch("subprocess.run", side_effect=[fail_result, success_result]) as mock_run:
|
|
with patch("tenacity.nap.time.sleep"):
|
|
output = cli._run(["labels", "list"])
|
|
assert output == '[{"id": 1}]'
|
|
assert mock_run.call_count == 2
|
|
|
|
def test_run_retries_on_503_then_fails(self) -> None:
|
|
"""If all retries are exhausted on 503, raise TeaCLIError."""
|
|
cli = TeaCLI(tea_bin="/fake/tea")
|
|
fail_result = MagicMock(returncode=1, stdout="", stderr="503 Service Unavailable")
|
|
with patch("subprocess.run", return_value=fail_result):
|
|
with patch("tenacity.nap.time.sleep"):
|
|
with pytest.raises(TeaCLIError, match="503"):
|
|
cli._run(["issues", "create"])
|
|
# MAX_RETRIES=3, so 3 attempts total
|
|
|
|
def test_run_no_retry_on_non_transient_error(self) -> None:
|
|
"""Non-transient errors (e.g. auth) should fail immediately without retry."""
|
|
cli = TeaCLI(tea_bin="/fake/tea")
|
|
fail_result = MagicMock(returncode=1, stdout="", stderr="auth error")
|
|
with patch("subprocess.run", return_value=fail_result) as mock_run:
|
|
with pytest.raises(TeaCLIError, match="auth error"):
|
|
cli._run(["labels", "list"])
|
|
assert mock_run.call_count == 1
|
|
|
|
def test_run_retries_on_429_in_stdout(self) -> None:
|
|
"""429 rate limit in stdout should trigger retry."""
|
|
cli = TeaCLI(tea_bin="/fake/tea")
|
|
fail_result = MagicMock(returncode=1, stdout="429 Too Many Requests", stderr="")
|
|
success_result = MagicMock(returncode=0, stdout="ok", stderr="")
|
|
with patch("subprocess.run", side_effect=[fail_result, success_result]):
|
|
with patch("tenacity.nap.time.sleep"):
|
|
output = cli._run(["releases", "create"])
|
|
assert output == "ok"
|
|
|
|
|
|
class TestRepoArg:
|
|
def test_with_repo_arg(self) -> None:
|
|
cli = TeaCLI(repo="owner/repo")
|
|
assert cli._repo_arg() == ["--repo", "owner/repo"]
|
|
|
|
def test_with_explicit_repo(self) -> None:
|
|
cli = TeaCLI()
|
|
assert cli._repo_arg("other/repo") == ["--repo", "other/repo"]
|
|
|
|
def test_without_repo(self) -> None:
|
|
cli = TeaCLI()
|
|
assert cli._repo_arg() == []
|
|
|
|
def test_explicit_overrides_default(self) -> None:
|
|
cli = TeaCLI(repo="default/repo")
|
|
assert cli._repo_arg("override/repo") == ["--repo", "override/repo"]
|
|
|
|
|
|
class TestCreateIssue:
|
|
def test_create_issue_basic(self) -> None:
|
|
cli = TeaCLI(tea_bin="/fake/tea", repo="owner/repo")
|
|
mock_result = MagicMock(returncode=0, stdout="Created issue #42: Bug", stderr="")
|
|
with patch("subprocess.run", return_value=mock_result):
|
|
issue = cli.create_issue("owner/repo", title="Bug", body="Description")
|
|
assert issue["index"] == 42
|
|
assert issue["title"] == "Bug"
|
|
|
|
def test_create_issue_with_labels(self) -> None:
|
|
cli = TeaCLI(tea_bin="/fake/tea")
|
|
mock_result = MagicMock(returncode=0, stdout="Created issue #5: Title", stderr="")
|
|
with patch("subprocess.run", return_value=mock_result):
|
|
issue = cli.create_issue("owner/repo", title="Title", body="Body", labels=["bug"])
|
|
assert issue["index"] == 5
|
|
|
|
|
|
class TestListLabels:
|
|
def test_list_labels_with_data(self) -> None:
|
|
cli = TeaCLI(tea_bin="/fake/tea")
|
|
labels_json = json.dumps([{"id": 1, "name": "bug"}, {"id": 2, "name": "enhancement"}])
|
|
mock_result = MagicMock(returncode=0, stdout=labels_json, stderr="")
|
|
with patch("subprocess.run", return_value=mock_result):
|
|
labels = cli.list_labels("owner/repo")
|
|
assert len(labels) == 2
|
|
assert labels[0]["name"] == "bug"
|
|
|
|
def test_list_labels_empty(self) -> None:
|
|
cli = TeaCLI(tea_bin="/fake/tea")
|
|
mock_result = MagicMock(returncode=0, stdout="", stderr="")
|
|
with patch("subprocess.run", return_value=mock_result):
|
|
labels = cli.list_labels("owner/repo")
|
|
assert labels == []
|
|
|
|
|
|
class TestCreateLabel:
|
|
def test_create_label_full(self) -> None:
|
|
cli = TeaCLI(tea_bin="/fake/tea")
|
|
mock_result = MagicMock(returncode=0, stdout="Label created", stderr="")
|
|
with patch("subprocess.run", return_value=mock_result):
|
|
label = cli.create_label("owner/repo", name="bug", color="ff0000", description="A bug")
|
|
assert label["name"] == "bug"
|
|
assert label["color"] == "ff0000"
|
|
|
|
def test_create_label_name_only(self) -> None:
|
|
cli = TeaCLI(tea_bin="/fake/tea")
|
|
mock_result = MagicMock(returncode=0, stdout="Label created", stderr="")
|
|
with patch("subprocess.run", return_value=mock_result):
|
|
label = cli.create_label("owner/repo", name="wip")
|
|
assert label["name"] == "wip"
|
|
assert label["color"] == ""
|
|
|
|
|
|
class TestAddLabel:
|
|
def test_add_label_single(self) -> None:
|
|
cli = TeaCLI(tea_bin="/fake/tea")
|
|
mock_result = MagicMock(returncode=0, stdout="ok", stderr="")
|
|
with patch("subprocess.run", return_value=mock_result) as mock_run:
|
|
cli.add_label("owner/repo", 42, ["ready-to-merge"])
|
|
cmd = mock_run.call_args[0][0]
|
|
assert "--add-labels" in cmd
|
|
assert "ready-to-merge" in cmd
|
|
assert "42" in cmd
|
|
|
|
def test_add_label_multiple(self) -> None:
|
|
cli = TeaCLI(tea_bin="/fake/tea")
|
|
mock_result = MagicMock(returncode=0, stdout="ok", stderr="")
|
|
with patch("subprocess.run", return_value=mock_result) as mock_run:
|
|
cli.add_label("owner/repo", 42, ["bug", "urgent"])
|
|
cmd = mock_run.call_args[0][0]
|
|
assert "--add-labels" in cmd
|
|
|
|
def test_add_label_empty_list(self) -> None:
|
|
cli = TeaCLI(tea_bin="/fake/tea")
|
|
with patch("subprocess.run") as mock_run:
|
|
cli.add_label("owner/repo", 42, [])
|
|
mock_run.assert_not_called()
|
|
|
|
|
|
class TestCreatePR:
|
|
def test_create_pr_basic(self) -> None:
|
|
cli = TeaCLI(tea_bin="/fake/tea")
|
|
mock_result = MagicMock(returncode=0, stdout="Created PR #128: Feature", stderr="")
|
|
with patch("subprocess.run", return_value=mock_result):
|
|
pr = cli.create_pr("owner/repo", title="Feature", head="feature-branch", base="master")
|
|
assert pr["index"] == 128
|
|
|
|
def test_create_pr_with_body(self) -> None:
|
|
cli = TeaCLI(tea_bin="/fake/tea")
|
|
mock_result = MagicMock(returncode=0, stdout="Created PR #10: Title", stderr="")
|
|
with patch("subprocess.run", return_value=mock_result) as mock_run:
|
|
cli.create_pr("owner/repo", title="Title", head="feat", base="master", body="Description")
|
|
cmd = mock_run.call_args[0][0]
|
|
assert "--body" in cmd
|
|
assert "Description" in cmd
|
|
|
|
|
|
class TestMergePR:
|
|
def test_merge_pr_squash(self) -> None:
|
|
cli = TeaCLI(tea_bin="/fake/tea")
|
|
mock_result = MagicMock(returncode=0, stdout="Merged", stderr="")
|
|
with patch("subprocess.run", return_value=mock_result) as mock_run:
|
|
cli.merge_pr("owner/repo", 42, style="squash")
|
|
cmd = mock_run.call_args[0][0]
|
|
assert "--style" in cmd
|
|
assert "squash" in cmd
|
|
assert "42" in cmd
|
|
|
|
def test_merge_pr_default_style(self) -> None:
|
|
cli = TeaCLI(tea_bin="/fake/tea")
|
|
mock_result = MagicMock(returncode=0, stdout="Merged", stderr="")
|
|
with patch("subprocess.run", return_value=mock_result) as mock_run:
|
|
cli.merge_pr("owner/repo", 42)
|
|
cmd = mock_run.call_args[0][0]
|
|
assert "squash" in cmd
|
|
|
|
|
|
class TestReviewPR:
|
|
def test_review_approve(self) -> None:
|
|
cli = TeaCLI(tea_bin="/fake/tea")
|
|
mock_result = MagicMock(returncode=0, stdout="Reviewed", stderr="")
|
|
with patch("subprocess.run", return_value=mock_result) as mock_run:
|
|
cli.review_pr("owner/repo", 42, event="APPROVE", body="LGTM")
|
|
cmd = mock_run.call_args[0][0]
|
|
assert "--approve" in cmd
|
|
assert "--comment" in cmd
|
|
|
|
def test_review_reject(self) -> None:
|
|
cli = TeaCLI(tea_bin="/fake/tea")
|
|
mock_result = MagicMock(returncode=0, stdout="Reviewed", stderr="")
|
|
with patch("subprocess.run", return_value=mock_result) as mock_run:
|
|
cli.review_pr("owner/repo", 42, event="REQUEST_CHANGES", body="Needs work")
|
|
cmd = mock_run.call_args[0][0]
|
|
assert "--reject" in cmd
|
|
|
|
def test_review_comment(self) -> None:
|
|
cli = TeaCLI(tea_bin="/fake/tea")
|
|
mock_result = MagicMock(returncode=0, stdout="Reviewed", stderr="")
|
|
with patch("subprocess.run", return_value=mock_result) as mock_run:
|
|
cli.review_pr("owner/repo", 42, event="COMMENT", body="Note")
|
|
cmd = mock_run.call_args[0][0]
|
|
assert "--approve" not in cmd
|
|
assert "--reject" not in cmd
|
|
assert "--comment" in cmd
|
|
|
|
def test_review_no_body(self) -> None:
|
|
cli = TeaCLI(tea_bin="/fake/tea")
|
|
mock_result = MagicMock(returncode=0, stdout="Reviewed", stderr="")
|
|
with patch("subprocess.run", return_value=mock_result) as mock_run:
|
|
cli.review_pr("owner/repo", 42, event="COMMENT")
|
|
cmd = mock_run.call_args[0][0]
|
|
assert "--comment" not in cmd
|
|
|
|
|
|
class TestCreateRelease:
|
|
def test_create_release_full(self) -> None:
|
|
cli = TeaCLI(tea_bin="/fake/tea")
|
|
mock_result = MagicMock(returncode=0, stdout="Release created", stderr="")
|
|
with patch("subprocess.run", return_value=mock_result) as mock_run:
|
|
release = cli.create_release(
|
|
"owner/repo",
|
|
tag="v1.0.0",
|
|
title="Release 1.0.0",
|
|
body="Notes",
|
|
target="master",
|
|
)
|
|
cmd = mock_run.call_args[0][0]
|
|
assert "v1.0.0" in cmd
|
|
assert "--title" in cmd
|
|
assert "--note" in cmd
|
|
assert "--target" in cmd
|
|
assert release["tag"] == "v1.0.0"
|
|
|
|
def test_create_release_draft(self) -> None:
|
|
cli = TeaCLI(tea_bin="/fake/tea")
|
|
mock_result = MagicMock(returncode=0, stdout="Release created", stderr="")
|
|
with patch("subprocess.run", return_value=mock_result) as mock_run:
|
|
cli.create_release("owner/repo", tag="v0.1.0", draft=True)
|
|
cmd = mock_run.call_args[0][0]
|
|
assert "--draft" in cmd
|
|
|
|
def test_create_release_prerelease(self) -> None:
|
|
cli = TeaCLI(tea_bin="/fake/tea")
|
|
mock_result = MagicMock(returncode=0, stdout="Release created", stderr="")
|
|
with patch("subprocess.run", return_value=mock_result) as mock_run:
|
|
cli.create_release("owner/repo", tag="v0.1.0-rc1", prerelease=True)
|
|
cmd = mock_run.call_args[0][0]
|
|
assert "--prerelease" in cmd
|
|
|
|
def test_create_release_minimal(self) -> None:
|
|
cli = TeaCLI(tea_bin="/fake/tea")
|
|
mock_result = MagicMock(returncode=0, stdout="Release created", stderr="")
|
|
with patch("subprocess.run", return_value=mock_result) as mock_run:
|
|
release = cli.create_release("owner/repo", tag="v1.0.0")
|
|
cmd = mock_run.call_args[0][0]
|
|
assert "--title" not in cmd
|
|
assert "--note" not in cmd
|
|
assert release["tag"] == "v1.0.0"
|
|
|
|
|
|
class TestListReleases:
|
|
def test_list_releases_with_data(self) -> None:
|
|
cli = TeaCLI(tea_bin="/fake/tea")
|
|
releases_json = json.dumps([{"tag": "v1.0.0"}, {"tag": "v0.9.0"}])
|
|
mock_result = MagicMock(returncode=0, stdout=releases_json, stderr="")
|
|
with patch("subprocess.run", return_value=mock_result):
|
|
releases = cli.list_releases("owner/repo")
|
|
assert len(releases) == 2
|
|
|
|
def test_list_releases_empty(self) -> None:
|
|
cli = TeaCLI(tea_bin="/fake/tea")
|
|
mock_result = MagicMock(returncode=0, stdout="", stderr="")
|
|
with patch("subprocess.run", return_value=mock_result):
|
|
releases = cli.list_releases("owner/repo")
|
|
assert releases == []
|
|
|
|
|
|
class TestListBranches:
|
|
def test_list_branches_with_data(self) -> None:
|
|
cli = TeaCLI(tea_bin="/fake/tea")
|
|
branches_json = json.dumps([{"name": "master"}, {"name": "develop"}])
|
|
mock_result = MagicMock(returncode=0, stdout=branches_json, stderr="")
|
|
with patch("subprocess.run", return_value=mock_result):
|
|
branches = cli.list_branches("owner/repo")
|
|
assert len(branches) == 2
|
|
|
|
def test_list_branches_empty(self) -> None:
|
|
cli = TeaCLI(tea_bin="/fake/tea")
|
|
mock_result = MagicMock(returncode=0, stdout="", stderr="")
|
|
with patch("subprocess.run", return_value=mock_result):
|
|
branches = cli.list_branches("owner/repo")
|
|
assert branches == []
|
|
|
|
|
|
class TestWhoami:
|
|
def test_whoami(self) -> None:
|
|
cli = TeaCLI(tea_bin="/fake/tea")
|
|
mock_result = MagicMock(returncode=0, stdout="testuser", stderr="")
|
|
with patch("subprocess.run", return_value=mock_result):
|
|
assert cli.whoami() == "testuser"
|
|
|
|
|
|
class TestConfigureTeaLogin:
|
|
@patch.dict("os.environ", {"CI_GITEA_TOKEN": ""}, clear=True)
|
|
@patch("devx.gitea_cli.shutil.which", return_value="/usr/bin/tea")
|
|
def test_no_token_skips(self, mock_which: MagicMock) -> None:
|
|
"""configure_tea_login with no token prints skip message and returns."""
|
|
configure_tea_login()
|
|
|
|
@patch.dict("os.environ", {"CI_GITEA_TOKEN": "tok"})
|
|
@patch("devx.gitea_cli.shutil.which", return_value=None)
|
|
def test_no_tea_skips(self, mock_which: MagicMock) -> None:
|
|
"""configure_tea_login with no tea binary prints skip message and returns."""
|
|
configure_tea_login()
|
|
|
|
@patch.dict("os.environ", {"CI_GITEA_TOKEN": "tok"})
|
|
@patch("devx.gitea_cli.shutil.which", return_value="/usr/bin/tea")
|
|
@patch("devx.gitea_cli.subprocess.run")
|
|
def test_configures_login_when_not_present(self, mock_subprocess: MagicMock, mock_which: MagicMock) -> None:
|
|
"""configure_tea_login adds login when not already configured."""
|
|
mock_list = MagicMock(returncode=0, stdout="")
|
|
mock_add = MagicMock(returncode=0, stdout="Login successful", stderr="")
|
|
mock_default = MagicMock(returncode=0, stdout="", stderr="")
|
|
mock_subprocess.side_effect = [mock_list, mock_add, mock_default]
|
|
configure_tea_login()
|
|
assert mock_subprocess.call_count == 3 # login list + login add + login default
|
|
|
|
@patch.dict("os.environ", {"CI_GITEA_TOKEN": "tok"})
|
|
@patch("devx.gitea_cli.shutil.which", return_value="/usr/bin/tea")
|
|
@patch("devx.gitea_cli.subprocess.run")
|
|
def test_skips_when_already_configured(self, mock_subprocess: MagicMock, mock_which: MagicMock) -> None:
|
|
"""configure_tea_login skips if login already exists."""
|
|
mock_list = MagicMock(returncode=0, stdout="devx https://git.example.com")
|
|
mock_subprocess.return_value = mock_list
|
|
configure_tea_login()
|
|
assert mock_subprocess.call_count == 1 # only login list, no add
|
|
|
|
@patch.dict("os.environ", {"CI_GITEA_TOKEN": "tok"})
|
|
@patch("devx.gitea_cli.shutil.which", return_value="/usr/bin/tea")
|
|
@patch("devx.gitea_cli.subprocess.run")
|
|
def test_raises_on_login_add_failure(self, mock_subprocess: MagicMock, mock_which: MagicMock) -> None:
|
|
"""configure_tea_login raises TeaCLIError if tea login add fails."""
|
|
mock_list = MagicMock(returncode=0, stdout="")
|
|
mock_add = MagicMock(returncode=1, stdout="", stderr="invalid token")
|
|
mock_subprocess.side_effect = [mock_list, mock_add]
|
|
with pytest.raises(TeaCLIError, match="login add failed"):
|
|
configure_tea_login()
|
|
|
|
@patch.dict("os.environ", {"CI_GITEA_TOKEN": "tok"})
|
|
@patch("devx.gitea_cli.shutil.which", return_value="/usr/bin/tea")
|
|
@patch("devx.gitea_cli.subprocess.run")
|
|
def test_raises_on_login_default_failure(self, mock_subprocess: MagicMock, mock_which: MagicMock) -> None:
|
|
"""configure_tea_login raises TeaCLIError if tea login default fails."""
|
|
mock_list = MagicMock(returncode=0, stdout="")
|
|
mock_add = MagicMock(returncode=0, stdout="Login successful", stderr="")
|
|
mock_default = MagicMock(returncode=1, stdout="", stderr="login not found")
|
|
mock_subprocess.side_effect = [mock_list, mock_add, mock_default]
|
|
with pytest.raises(TeaCLIError, match="login default failed"):
|
|
configure_tea_login()
|
|
|
|
@patch.dict("os.environ", {"CI_GITEA_TOKEN": "tok"})
|
|
@patch("devx.gitea_cli.shutil.which", return_value="/usr/bin/tea")
|
|
@patch("devx.gitea_cli.subprocess.run")
|
|
def test_login_add_failure_includes_stdout(self, mock_subprocess: MagicMock, mock_which: MagicMock) -> None:
|
|
"""Error message includes stdout when tea writes errors there."""
|
|
mock_list = MagicMock(returncode=0, stdout="")
|
|
mock_add = MagicMock(returncode=1, stdout="Error: invalid username", stderr="")
|
|
mock_subprocess.side_effect = [mock_list, mock_add]
|
|
with pytest.raises(TeaCLIError, match="invalid username"):
|
|
configure_tea_login()
|