Add admin panel, WebSocket support, and API versioning
Introduce an admin portal (React + Nginx), WebSocket routing, and API versioning middleware with `/api/v1/` prefix deprecation. Add master API key authentication, new Prometheus metrics for AI token consumption and active WebSockets, and extend S3 config with a public endpoint URL. Update test paths and fixtures to align with the new routing structure.
This commit is contained in:
@@ -0,0 +1,54 @@
|
||||
import pytest
|
||||
import respx
|
||||
from httpx import Response
|
||||
|
||||
from imago_client import HubClient, ImagoError, APIError, QuotaError, AuthError
|
||||
|
||||
@pytest.fixture
|
||||
def base_url():
|
||||
return "http://testserver"
|
||||
|
||||
@pytest.fixture
|
||||
def api_key():
|
||||
return "sk-test-key"
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_client_initialization(base_url, api_key):
|
||||
client = HubClient(base_url, api_key=api_key)
|
||||
assert client.base_url == "http://testserver"
|
||||
assert client.api_key == "sk-test-key"
|
||||
assert "X-API-Key" in client._http_client.headers
|
||||
await client.close()
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@respx.mock
|
||||
async def test_auth_error(base_url, api_key):
|
||||
respx.get(f"{base_url}/api/v1/images").mock(return_value=Response(401, json={"detail": "Unauthorized"}))
|
||||
|
||||
async with HubClient(base_url, api_key="wrong") as client:
|
||||
with pytest.raises(AuthError):
|
||||
await client.images.list()
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@respx.mock
|
||||
async def test_quota_error(base_url, api_key):
|
||||
respx.get(f"{base_url}/api/v1/images").mock(return_value=Response(429, json={"detail": "Rate Limit Exceeded"}))
|
||||
|
||||
async with HubClient(base_url, api_key=api_key) as client:
|
||||
with pytest.raises(QuotaError):
|
||||
await client.images.list()
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@respx.mock
|
||||
async def test_api_error_retry(base_url, api_key):
|
||||
route = respx.get(f"{base_url}/api/v1/images")
|
||||
route.side_effect = [
|
||||
Response(502, json={"error": "Bad Gateway"}),
|
||||
Response(200, json={"items": []})
|
||||
]
|
||||
|
||||
async with HubClient(base_url, api_key=api_key) as client:
|
||||
# Should automatically retry on 502
|
||||
response = await client.images.list()
|
||||
assert response == {"items": []}
|
||||
assert route.call_count == 2
|
||||
@@ -0,0 +1,76 @@
|
||||
import pytest
|
||||
import respx
|
||||
from httpx import Response
|
||||
|
||||
from imago_client import HubClient, HubImage
|
||||
|
||||
@pytest.fixture
|
||||
def base_url():
|
||||
return "http://testserver"
|
||||
|
||||
@pytest.fixture
|
||||
def api_key():
|
||||
return "sk-test-key"
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@respx.mock
|
||||
async def test_get_image(base_url, api_key):
|
||||
mock_data = {
|
||||
"id": 1,
|
||||
"uuid": "fake-uuid",
|
||||
"original_name": "test.jpg",
|
||||
"processing_status": "done"
|
||||
}
|
||||
respx.get(f"{base_url}/api/v1/images/1").mock(return_value=Response(200, json=mock_data))
|
||||
|
||||
async with HubClient(base_url, api_key=api_key) as client:
|
||||
img = await client.images.get(1)
|
||||
|
||||
assert isinstance(img, HubImage)
|
||||
assert img.id == 1
|
||||
assert img.uuid == "fake-uuid"
|
||||
assert img.processing_status == "done"
|
||||
assert img.original_name == "test.jpg"
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@respx.mock
|
||||
async def test_upload_image(base_url, api_key):
|
||||
mock_data = {
|
||||
"id": 2,
|
||||
"uuid": "new-uuid",
|
||||
"original_name": "upload.jpg",
|
||||
"processing_status": "pending"
|
||||
}
|
||||
respx.post(f"{base_url}/api/v1/images/upload").mock(return_value=Response(200, json=mock_data))
|
||||
|
||||
async with HubClient(base_url, api_key=api_key) as client:
|
||||
img = await client.images.upload(b"fakebytes", "upload.jpg", "image/jpeg")
|
||||
|
||||
assert isinstance(img, HubImage)
|
||||
assert img.id == 2
|
||||
assert img.processing_status == "pending"
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@respx.mock
|
||||
async def test_list_images(base_url, api_key):
|
||||
mock_response = {
|
||||
"total": 1,
|
||||
"page": 1,
|
||||
"size": 50,
|
||||
"items": [
|
||||
{
|
||||
"id": 1,
|
||||
"uuid": "fake-uuid",
|
||||
"original_name": "test.jpg",
|
||||
"processing_status": "done"
|
||||
}
|
||||
]
|
||||
}
|
||||
respx.get(f"{base_url}/api/v1/images?page=1&size=50").mock(return_value=Response(200, json=mock_response))
|
||||
|
||||
async with HubClient(base_url, api_key=api_key) as client:
|
||||
result = await client.images.list()
|
||||
|
||||
assert result["total"] == 1
|
||||
assert len(result["items"]) == 1
|
||||
assert result["items"][0]["id"] == 1
|
||||
Reference in New Issue
Block a user