test_integration.py•5.7 kB
"""Integration tests that contact the real Repology API.
These tests are marked with @pytest.mark.integration and can be run with:
pytest -m integration
To exclude them from regular test runs:
pytest -m "not integration"
"""
import pytest
import asyncio
from repology_mcp.client import RepologyClient, RepologyNotFoundError, RepologyAPIError
from repology_mcp.models import Package, Problem
@pytest.mark.integration
@pytest.mark.asyncio
async def test_real_get_project_firefox():
"""Test getting a real project (firefox) from Repology API."""
async with RepologyClient() as client:
# Firefox should exist and have packages
packages = await client.get_project("firefox")
assert len(packages) > 0
assert all(isinstance(pkg, Package) for pkg in packages)
# Firefox should be in multiple repositories
repos = {pkg.repo for pkg in packages}
assert len(repos) > 1
# Should have common repositories (check actual repo names from API)
common_repos = {
"arch",
"fedora_41",
"fedora_40",
"ubuntu_24_04",
"ubuntu_22_04",
"debian_12",
"debian_11",
}
assert len(repos & common_repos) > 0
@pytest.mark.integration
@pytest.mark.asyncio
async def test_real_get_project_nonexistent():
"""Test getting a non-existent project from Repology API."""
async with RepologyClient() as client:
# This project name should be unique enough to not exist
nonexistent_name = "definitely-nonexistent-project-12345-xyz-abc"
try:
result = await client.get_project(nonexistent_name)
# If we get here, check it's actually empty (can be {} or [])
assert len(result) == 0
except RepologyNotFoundError:
# This is also acceptable
pass
@pytest.mark.integration
@pytest.mark.asyncio
async def test_real_list_projects_small_subset():
"""Test listing a small subset of real projects from Repology API."""
async with RepologyClient() as client:
# Get a small subset to avoid overwhelming the API
projects = await client.list_projects(limit=5)
# Note: API may return more than the limit requested
assert len(projects) > 0
assert len(projects) <= 200 # API default/max is 200
# All should be valid project data
for project_name, packages in list(projects.items())[:5]: # Check first 5
assert isinstance(project_name, str)
assert len(project_name) > 0
assert isinstance(packages, list)
assert len(packages) > 0
assert all(isinstance(pkg, Package) for pkg in packages)
@pytest.mark.integration
@pytest.mark.asyncio
async def test_real_search_projects_python():
"""Test searching for real Python-related projects."""
async with RepologyClient() as client:
# Search for python projects (should return many results)
projects = await client.search_projects("python", limit=10)
assert len(projects) > 0
# API sometimes returns more than limit, so check it's reasonable
assert len(projects) <= 250 # API max
# Should have some common project names in the keys
project_names = list(projects.keys())
python_related = [name for name in project_names if "python" in name.lower()]
assert len(python_related) > 0
@pytest.mark.integration
@pytest.mark.asyncio
async def test_real_get_repository_problems_freebsd():
"""Test getting real repository problems from FreeBSD."""
async with RepologyClient() as client:
# FreeBSD usually has some problems reported
problems = await client.get_repository_problems("freebsd")
# FreeBSD should have at least some problems
assert len(problems) > 0
# Check first few problems to avoid processing too many
for problem in problems[:5]:
assert isinstance(problem, Problem)
assert problem.type # Should have a problem type
assert problem.project_name # Should have project name
assert isinstance(problem.data, dict) # Should have data dict
@pytest.mark.integration
@pytest.mark.asyncio
async def test_real_api_rate_limiting():
"""Test that API respects rate limiting properly."""
async with RepologyClient() as client:
# Make a few quick requests to test rate limiting behavior
start_time = asyncio.get_event_loop().time()
# Get a small project
await client.get_project("firefox")
# Make another request immediately
await client.list_projects(limit=1)
end_time = asyncio.get_event_loop().time()
# Should complete without errors (basic rate limiting test)
# If rate limiting is working, this won't fail but may be slower
assert end_time >= start_time
@pytest.mark.integration
@pytest.mark.asyncio
async def test_real_maintainer_problems_invalid():
"""Test handling of invalid maintainer for problems endpoint."""
async with RepologyClient() as client:
# Use an invalid email format that should return empty results
invalid_maintainer = "definitely-not-a-real-maintainer@invalid-domain-12345.com"
try:
problems = await client.get_maintainer_problems(invalid_maintainer)
# Should return empty list for non-existent maintainer
assert isinstance(problems, list)
assert len(problems) == 0
except (RepologyNotFoundError, RepologyAPIError):
# Either exception is acceptable for invalid maintainer
pass