import pytest
from app.services.gitbucket import fetch_repos, _cache
from unittest.mock import AsyncMock, MagicMock, patch
@pytest.fixture(autouse=True)
def clear_cache():
"""Clear cache before each test."""
_cache["data"] = []
_cache["timestamp"] = 0.0
yield
_cache["data"] = []
_cache["timestamp"] = 0.0
@pytest.mark.asyncio
async def test_fetch_repos_success(mocker):
# Create mock response
mock_response = MagicMock()
mock_response.status_code = 200
mock_response.json.return_value = [
{"name": "repo1", "description": "Desc 1"},
{"name": "repo2", "description": "Desc 2"}
]
# Create mock client that works with async with
mock_client = MagicMock()
mock_client.__aenter__ = AsyncMock(return_value=mock_client)
mock_client.__aexit__ = AsyncMock(return_value=None)
mock_client.get = AsyncMock(return_value=mock_response)
# Patch AsyncClient to return our mock
with patch("httpx.AsyncClient", return_value=mock_client):
repos = await fetch_repos()
assert len(repos) == 2
assert repos[0]["name"] == "repo1"
@pytest.mark.asyncio
async def test_fetch_repos_fallback_on_error(mocker):
"""Test that empty list returned on error when no cache."""
mock_client = MagicMock()
mock_client.__aenter__ = AsyncMock(return_value=mock_client)
mock_client.__aexit__ = AsyncMock(return_value=None)
mock_client.get = AsyncMock(side_effect=Exception("Connection error"))
with patch("httpx.AsyncClient", return_value=mock_client):
repos = await fetch_repos()
assert repos == []
@pytest.mark.asyncio
async def test_fetch_repos_uses_cache(mocker):
"""Test that cache returns within TTL."""
mock_response = MagicMock()
mock_response.status_code = 200
mock_response.json.return_value = [{"name": "cached_repo", "description": "Cached"}]
mock_client = MagicMock()
mock_client.__aenter__ = AsyncMock(return_value=mock_client)
mock_client.__aexit__ = AsyncMock(return_value=None)
mock_client.get = AsyncMock(return_value=mock_response)
with patch("httpx.AsyncClient", return_value=mock_client):
# First call populates cache
repos1 = await fetch_repos()
assert len(repos1) == 1
assert repos1[0]["name"] == "cached_repo"
# Second call within TTL should return cached data without calling API again
repos2 = await fetch_repos()
assert repos2 == repos1
# Verify cache was populated
assert _cache["data"] == repos1