"""Real API integration tests - tests that use actual 42crunch API calls.
These tests make real API calls and validate the responses.
No mocks are used - all tests interact with the live 42crunch API.
"""
import pytest
import requests
from typing import Dict, Any
class HTTPMCPClient:
"""HTTP client for real API testing."""
def __init__(self, base_url: str = "http://localhost:8000"):
self.base_url = base_url.rstrip('/')
self.jsonrpc_url = f"{self.base_url}/jsonrpc"
self.request_id = 1
def call_tool(self, name: str, arguments: Dict[str, Any] = None) -> Dict[str, Any]:
"""Call a tool."""
request = {
"jsonrpc": "2.0",
"id": self.request_id,
"method": "tools/call",
"params": {
"name": name,
"arguments": arguments or {}
}
}
self.request_id += 1
response = requests.post(
self.jsonrpc_url,
json=request,
headers={"Content-Type": "application/json"},
timeout=30
)
response.raise_for_status()
return response.json()
@pytest.fixture(scope="module")
def http_client():
"""Create HTTP client fixture."""
client = HTTPMCPClient()
try:
response = requests.get(f"{client.base_url}/health", timeout=5)
response.raise_for_status()
except requests.exceptions.ConnectionError:
pytest.skip("HTTP server is not running. Start it with: python http_main.py")
return client
@pytest.fixture(scope="module")
def real_collection_id(http_client):
"""Get a real collection ID from the API."""
response = http_client.call_tool("list_collections", {"per_page": 10})
assert response["result"]["success"] is True, "Failed to get collections from real API"
data = response["result"]["data"]
# Try different response structures
collection = None
if "collections" in data and len(data["collections"]) > 0:
collection = data["collections"][0]
elif isinstance(data, list) and len(data) > 0:
collection = data[0]
elif isinstance(data, dict):
for key in ["items", "results", "data"]:
if key in data and isinstance(data[key], list) and len(data[key]) > 0:
collection = data[key][0]
break
assert collection is not None, "No collections found in real API response"
collection_id = collection.get("id") or collection.get("collection_id") or collection.get("uuid")
assert collection_id is not None, f"Collection object found but no ID: {collection}"
return collection_id
@pytest.fixture(scope="module")
def real_api_id(http_client, real_collection_id):
"""Get a real API ID from the API."""
response = http_client.call_tool("get_collection_apis", {
"collection_id": real_collection_id,
"with_tags": True
})
assert response["result"]["success"] is True, "Failed to get APIs from real API"
data = response["result"]["data"]
# Try different response structures
api = None
if "apis" in data and len(data["apis"]) > 0:
api = data["apis"][0]
elif isinstance(data, list) and len(data) > 0:
api = data[0]
elif isinstance(data, dict):
for key in ["items", "results", "data"]:
if key in data and isinstance(data[key], list) and len(data[key]) > 0:
api = data[key][0]
break
if api is None:
pytest.skip("No APIs found in collection")
api_id = api.get("id") or api.get("api_id") or api.get("uuid")
assert api_id is not None, f"API object found but no ID: {api}"
return api_id
class TestRealAPICollections:
"""Test list_collections with real API data."""
def test_list_collections_real_data(self, http_client):
"""Test list_collections returns real data from API."""
response = http_client.call_tool("list_collections", {
"page": 1,
"per_page": 10
})
assert response["result"]["success"] is True
assert "data" in response["result"]
data = response["result"]["data"]
assert data is not None
assert isinstance(data, (dict, list))
# Should have some collections
if isinstance(data, dict):
# Check for common collection keys
has_collections = any(
key in data and isinstance(data[key], list) and len(data[key]) > 0
for key in ["collections", "items", "results", "data"]
)
assert has_collections or len(data) > 0, "No collections found in real API response"
def test_list_collections_pagination_real(self, http_client):
"""Test pagination with real API data."""
# Get first page
page1 = http_client.call_tool("list_collections", {
"page": 1,
"per_page": 5
})
assert page1["result"]["success"] is True
# Get second page
page2 = http_client.call_tool("list_collections", {
"page": 2,
"per_page": 5
})
assert page2["result"]["success"] is True
class TestRealAPICollectionAPIs:
"""Test get_collection_apis with real API data."""
def test_get_collection_apis_real_data(self, http_client, real_collection_id):
"""Test get_collection_apis returns real data from API."""
response = http_client.call_tool("get_collection_apis", {
"collection_id": real_collection_id,
"with_tags": True
})
assert response["result"]["success"] is True
assert "data" in response["result"]
data = response["result"]["data"]
assert data is not None
assert isinstance(data, (dict, list))
# Should have API information
if isinstance(data, dict):
has_apis = any(
key in data and isinstance(data[key], list)
for key in ["apis", "items", "results", "data"]
)
assert has_apis or len(data) > 0, "No APIs found in real API response"
def test_get_collection_apis_with_tags(self, http_client, real_collection_id):
"""Test get_collection_apis with tags from real API."""
response_with_tags = http_client.call_tool("get_collection_apis", {
"collection_id": real_collection_id,
"with_tags": True
})
response_without_tags = http_client.call_tool("get_collection_apis", {
"collection_id": real_collection_id,
"with_tags": False
})
assert response_with_tags["result"]["success"] is True
assert response_without_tags["result"]["success"] is True
class TestRealAPIDetails:
"""Test get_api_details with real API data."""
def test_get_api_details_real_data(self, http_client, real_api_id):
"""Test get_api_details returns real data from API."""
response = http_client.call_tool("get_api_details", {
"api_id": real_api_id,
"branch": "main",
"include_definition": True,
"include_assessment": True,
"include_scan": True
})
assert response["result"]["success"] is True
assert "data" in response["result"]
data = response["result"]["data"]
assert data is not None
assert isinstance(data, dict)
assert len(data) > 0, "API details should contain data"
def test_get_api_details_with_definition(self, http_client, real_api_id):
"""Test get_api_details with OpenAPI definition from real API."""
response = http_client.call_tool("get_api_details", {
"api_id": real_api_id,
"branch": "main",
"include_definition": True,
"include_assessment": False,
"include_scan": False
})
assert response["result"]["success"] is True
data = response["result"]["data"]
assert isinstance(data, dict)
def test_get_api_details_with_assessment(self, http_client, real_api_id):
"""Test get_api_details with assessment data from real API."""
response = http_client.call_tool("get_api_details", {
"api_id": real_api_id,
"branch": "main",
"include_definition": False,
"include_assessment": True,
"include_scan": False
})
assert response["result"]["success"] is True
data = response["result"]["data"]
assert isinstance(data, dict)
def test_get_api_details_with_scan(self, http_client, real_api_id):
"""Test get_api_details with scan results from real API."""
response = http_client.call_tool("get_api_details", {
"api_id": real_api_id,
"branch": "main",
"include_definition": False,
"include_assessment": False,
"include_scan": True
})
assert response["result"]["success"] is True
data = response["result"]["data"]
assert isinstance(data, dict)
def test_get_api_details_different_branch(self, http_client, real_api_id):
"""Test get_api_details with different branch from real API."""
# Try main branch
main_response = http_client.call_tool("get_api_details", {
"api_id": real_api_id,
"branch": "main"
})
assert main_response["result"]["success"] is True
# Try master branch (if different)
master_response = http_client.call_tool("get_api_details", {
"api_id": real_api_id,
"branch": "master"
})
# May succeed or fail depending on branch existence
assert "result" in master_response or "error" in master_response
class TestRealAPIEndToEnd:
"""End-to-end tests using real API data."""
def test_full_workflow_real_api(self, http_client):
"""Test complete workflow: list -> get collection -> get API details."""
# Step 1: List collections
collections_response = http_client.call_tool("list_collections", {"per_page": 5})
assert collections_response["result"]["success"] is True
data = collections_response["result"]["data"]
# Extract collection ID
collection = None
if "collections" in data and len(data["collections"]) > 0:
collection = data["collections"][0]
elif isinstance(data, list) and len(data) > 0:
collection = data[0]
if not collection:
pytest.skip("No collections available for end-to-end test")
collection_id = collection.get("id") or collection.get("collection_id") or collection.get("uuid")
if not collection_id:
pytest.skip("Collection found but no ID available")
# Step 2: Get APIs in collection
apis_response = http_client.call_tool("get_collection_apis", {
"collection_id": collection_id,
"with_tags": True
})
assert apis_response["result"]["success"] is True
apis_data = apis_response["result"]["data"]
# Extract API ID
api = None
if "apis" in apis_data and len(apis_data["apis"]) > 0:
api = apis_data["apis"][0]
elif isinstance(apis_data, list) and len(apis_data) > 0:
api = apis_data[0]
if not api:
pytest.skip("No APIs in collection for end-to-end test")
api_id = api.get("id") or api.get("api_id") or api.get("uuid")
if not api_id:
pytest.skip("API found but no ID available")
# Step 3: Get API details
details_response = http_client.call_tool("get_api_details", {
"api_id": api_id,
"branch": "main"
})
assert details_response["result"]["success"] is True
assert "data" in details_response["result"]
# All steps completed successfully with real API
print(f"\n✅ End-to-end test completed:")
print(f" Collection ID: {collection_id}")
print(f" API ID: {api_id}")
def test_multiple_collections_real(self, http_client):
"""Test retrieving multiple collections from real API."""
response = http_client.call_tool("list_collections", {
"page": 1,
"per_page": 20
})
assert response["result"]["success"] is True
data = response["result"]["data"]
# Should be able to handle multiple collections
assert data is not None