mirror of
https://github.com/rennf93/roboco.git
synced 2026-08-03 07:23:24 +02:00
115 lines
3.3 KiB
Python
115 lines
3.3 KiB
Python
"""Cancel closes the task's own open PR (GAP A).
|
|||
|
|
|
||
|
|
``_delete_task_branch_best_effort`` already force-deletes the task's remote
|
||
|
|
branch + worktree on cancel, but an open PR (``task.pr_number`` set) was left
|
||
|
|
open on the forge forever — nothing ever closed it. This is the isolated
|
||
|
|
unit test for the new ``_close_task_pr_best_effort`` best-effort chokepoint;
|
||
|
|
``tests/integration/test_task_service_lifecycle_misc.py`` covers the full
|
||
|
|
``cancel()`` wiring against a real DB.
|
||
|
|
"""
|
||
|
|
|
||
|
|
from __future__ import annotations
|
||
|
|
|
||
|
|
from unittest.mock import AsyncMock, MagicMock, patch
|
||
|
|
from uuid import uuid4
|
||
|
|
|
||
|
|
import pytest
|
||
|
|
from roboco.services.task import TaskService
|
||
|
|
|
||
|
|
|
||
|
|
def _service() -> TaskService:
|
||
|
|
svc = TaskService.__new__(TaskService)
|
||
|
|
svc.log = MagicMock()
|
||
|
|
svc.session = MagicMock()
|
||
|
|
return svc
|
||
|
|
|
||
|
|
|
||
|
|
def _session(slug: str | None) -> MagicMock:
|
||
|
|
session = MagicMock()
|
||
|
|
result = MagicMock()
|
||
|
|
result.scalar_one_or_none.return_value = slug
|
||
|
|
session.execute = AsyncMock(return_value=result)
|
||
|
|
return session
|
||
|
|
|
||
|
|
|
||
|
|
def _task(*, pr_number: int | None) -> MagicMock:
|
||
|
|
return MagicMock(id=uuid4(), project_id=uuid4(), pr_number=pr_number)
|
||
|
|
|
||
|
|
|
||
|
|
@pytest.mark.asyncio
|
||
|
|
async def test_closes_open_pr_via_git_service() -> None:
|
||
|
|
svc = _service()
|
||
|
|
task = _task(pr_number=42)
|
||
|
|
svc.session = _session("roboco-api")
|
||
|
|
|
||
|
|
git_service = MagicMock()
|
||
|
|
git_service.close_task_pr_best_effort = AsyncMock()
|
||
|
|
|
||
|
|
with patch(
|
||
|
|
"roboco.services.git.get_git_service",
|
||
|
|
MagicMock(return_value=git_service),
|
||
|
|
):
|
||
|
|
await svc._close_task_pr_best_effort(task)
|
||
|
|
|
||
|
|
git_service.close_task_pr_best_effort.assert_awaited_once_with("roboco-api", 42)
|
||
|
|
|
||
|
|
|
||
|
|
@pytest.mark.asyncio
|
||
|
|
async def test_skips_when_no_pr_number() -> None:
|
||
|
|
# Task never opened a PR (or was cancelled before claim) — nothing to
|
||
|
|
# close, and no project lookup should even fire.
|
||
|
|
svc = _service()
|
||
|
|
task = _task(pr_number=None)
|
||
|
|
svc.session = _session("roboco-api")
|
||
|
|
|
||
|
|
git_service = MagicMock()
|
||
|
|
git_service.close_task_pr_best_effort = AsyncMock()
|
||
|
|
|
||
|
|
with patch(
|
||
|
|
"roboco.services.git.get_git_service",
|
||
|
|
MagicMock(return_value=git_service),
|
||
|
|
):
|
||
|
|
await svc._close_task_pr_best_effort(task)
|
||
|
|
|
||
|
|
git_service.close_task_pr_best_effort.assert_not_awaited()
|
||
|
|
svc.session.execute.assert_not_awaited()
|
||
|
|
|
||
|
|
|
||
|
|
@pytest.mark.asyncio
|
||
|
|
async def test_skips_when_project_slug_unresolvable() -> None:
|
||
|
|
svc = _service()
|
||
|
|
task = _task(pr_number=42)
|
||
|
|
svc.session = _session(None)
|
||
|
|
|
||
|
|
git_service = MagicMock()
|
||
|
|
git_service.close_task_pr_best_effort = AsyncMock()
|
||
|
|
|
||
|
|
with patch(
|
||
|
|
"roboco.services.git.get_git_service",
|
||
|
|
MagicMock(return_value=git_service),
|
||
|
|
):
|
||
|
|
await svc._close_task_pr_best_effort(task)
|
||
|
|
|
||
|
|
git_service.close_task_pr_best_effort.assert_not_awaited()
|
||
|
|
|
||
|
|
|
||
|
|
@pytest.mark.asyncio
|
||
|
|
async def test_pr_close_failure_does_not_raise() -> None:
|
||
|
|
# Best-effort: a forge failure logs and never blocks the cancellation.
|
||
|
|
svc = _service()
|
||
|
|
task = _task(pr_number=42)
|
||
|
|
svc.session = _session("roboco-api")
|
||
|
|
|
||
|
|
git_service = MagicMock()
|
||
|
|
git_service.close_task_pr_best_effort = AsyncMock(
|
||
|
|
side_effect=RuntimeError("forge unreachable")
|
||
|
|
)
|
||
|
|
|
||
|
|
with patch(
|
||
|
|
"roboco.services.git.get_git_service",
|
||
|
|
MagicMock(return_value=git_service),
|
||
|
|
):
|
||
|
|
await svc._close_task_pr_best_effort(task) # must not raise
|
||
|
|
|
||
|
|
svc.log.warning.assert_called_once()
|