mirror of
https://github.com/rennf93/roboco.git
synced 2026-08-03 07:23:24 +02:00
112 lines
3.8 KiB
Python
112 lines
3.8 KiB
Python
"""DepUpdateEngine — dedupe by (repo, command) + single open-task fetch.
|
|||
|
|
|
||
|
|
Two unit tests (no DB): a monorepo with two distinct ``dep_update_command``
|
||
|
|
values is not blocked by ``git_url`` alone (one open task per command, not one
|
||
|
|
per repo), and ``run_cycle`` fetches the open-task set exactly once regardless
|
||
|
|
of project count (the per-project scoped query is gone).
|
||
|
|
"""
|
||
|
|
|
||
|
|
from __future__ import annotations
|
||
|
|
|
||
|
|
from types import SimpleNamespace
|
||
|
|
from typing import Any
|
||
|
|
from unittest.mock import AsyncMock, MagicMock, patch
|
||
|
|
from uuid import uuid4
|
||
|
|
|
||
|
|
import pytest
|
||
|
|
from roboco.config import settings
|
||
|
|
from roboco.services.dep_update_engine import DepUpdateEngine
|
||
|
|
|
||
|
|
|
||
|
|
def _project(slug: str, git_url: str, command: str) -> SimpleNamespace:
|
||
|
|
return SimpleNamespace(
|
||
|
|
id=uuid4(), slug=slug, git_url=git_url, dep_update_command=command
|
||
|
|
)
|
||
|
|
|
||
|
|
|
||
|
|
def _open_task(project: Any) -> SimpleNamespace:
|
||
|
|
# run_cycle reads t.project.git_url / t.project.dep_update_command.
|
||
|
|
return SimpleNamespace(id=uuid4(), project_id=project.id, project=project)
|
||
|
|
|
||
|
|
|
||
|
|
class _FakeWorkspace:
|
||
|
|
def __init__(self, updates: bool = True) -> None:
|
||
|
|
self._updates = updates
|
||
|
|
|
||
|
|
async def dry_upgrade_changes_lockfile(self, _project: Any) -> bool:
|
||
|
|
return self._updates
|
||
|
|
|
||
|
|
|
||
|
|
def _make_engine(
|
||
|
|
task_svc: Any, workspace: _FakeWorkspace | None = None
|
||
|
|
) -> tuple[DepUpdateEngine, Any]:
|
||
|
|
session = MagicMock()
|
||
|
|
engine = DepUpdateEngine(session, workspace=workspace or _FakeWorkspace())
|
||
|
|
patcher = patch(
|
||
|
|
"roboco.services.dep_update_engine.get_task_service", return_value=task_svc
|
||
|
|
)
|
||
|
|
patcher.start()
|
||
|
|
return engine, patcher
|
||
|
|
|
||
|
|
|
||
|
|
@pytest.fixture
|
||
|
|
def _enabled(monkeypatch: pytest.MonkeyPatch) -> None:
|
||
|
|
monkeypatch.setattr(settings, "dep_update_enabled", True)
|
||
|
|
monkeypatch.setattr(settings, "dep_update_max_per_cycle", 10)
|
||
|
|
monkeypatch.setattr(settings, "dep_update_max_open_tasks", 10)
|
||
|
|
|
||
|
|
|
||
|
|
@pytest.mark.asyncio
|
||
|
|
async def test_dedupe_spared_by_command_not_git_url(_enabled: None) -> None:
|
||
|
|
"""A monorepo with two distinct dep_update_command values is not blocked
|
||
|
|
by git_url alone — one open task per command, not one per repo."""
|
||
|
|
git_url = "https://github.com/x/mono.git"
|
||
|
|
p1 = _project("mono-uv", git_url, "uv lock -U")
|
||
|
|
p2 = _project("mono-pnpm", git_url, "pnpm update -L")
|
||
|
|
open_task = _open_task(p1)
|
||
|
|
|
||
|
|
task_svc = MagicMock()
|
||
|
|
task_svc.list_open_dep_update_tasks = AsyncMock(return_value=[open_task])
|
||
|
|
task_svc.create = AsyncMock(
|
||
|
|
side_effect=lambda req: SimpleNamespace(id=uuid4(), project_id=req.project_id)
|
||
|
|
)
|
||
|
|
|
||
|
|
engine, patcher = _make_engine(task_svc)
|
||
|
|
try:
|
||
|
|
created = await engine.run_cycle([p1, p2])
|
||
|
|
finally:
|
||
|
|
patcher.stop()
|
||
|
|
|
||
|
|
# p1 deduped (open task already covers (mono, "uv lock -U"));
|
||
|
|
# p2 still eligible (different command) -> opened.
|
||
|
|
assert len(created) == 1
|
||
|
|
assert created[0].project_id == p2.id
|
||
|
|
|
||
|
|
|
||
|
|
@pytest.mark.asyncio
|
||
|
|
async def test_run_cycle_fetches_open_tasks_once(_enabled: None) -> None:
|
||
|
|
"""run_cycle fetches the open-task set exactly once regardless of project
|
||
|
|
count — the per-project scoped query is gone."""
|
||
|
|
project_count = 5
|
||
|
|
projects = [
|
||
|
|
_project(f"p{i}", f"https://github.com/x/r{i}.git", f"cmd{i}")
|
||
|
|
for i in range(project_count)
|
||
|
|
]
|
||
|
|
|
||
|
|
task_svc = MagicMock()
|
||
|
|
task_svc.list_open_dep_update_tasks = AsyncMock(return_value=[])
|
||
|
|
task_svc.create = AsyncMock(
|
||
|
|
side_effect=lambda req: SimpleNamespace(id=uuid4(), project_id=req.project_id)
|
||
|
|
)
|
||
|
|
|
||
|
|
engine, patcher = _make_engine(task_svc)
|
||
|
|
try:
|
||
|
|
created = await engine.run_cycle(projects)
|
||
|
|
finally:
|
||
|
|
patcher.stop()
|
||
|
|
|
||
|
|
assert len(created) == project_count
|
||
|
|
# One fetch for both the cap and the per-(repo, command) dedupe —
|
||
|
|
# not one per project.
|
||
|
|
assert task_svc.list_open_dep_update_tasks.call_count == 1
|