1. 新增channels模块集成测试目录与基础fixture 2. 为capability、doctor、reports、dashboard、directory、webhook、wizard、health、analytics、allowlist、config等路由编写完整的鉴权、参数校验与异常场景测试 3. 修复基础集成测试setup,新增external/scheduler/channel数据库schema初始化步骤
272 lines
7.6 KiB
Python
272 lines
7.6 KiB
Python
"""Integration tests for channels analytics_router endpoints."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import httpx
|
|
import pytest
|
|
|
|
from .conftest import BASE_URL, DEFAULT_CHANNEL_TYPE, ISO_TIME_END, ISO_TIME_START
|
|
|
|
pytestmark = [pytest.mark.asyncio, pytest.mark.integration]
|
|
|
|
ANALYTICS_URL = f"{BASE_URL}/analytics"
|
|
|
|
|
|
def _valid_params(**overrides) -> dict:
|
|
"""Build valid query params (start_time + end_time) for analytics endpoints."""
|
|
params = {"start_time": ISO_TIME_START, "end_time": ISO_TIME_END}
|
|
params.update(overrides)
|
|
return params
|
|
|
|
|
|
# =============================================================================
|
|
# === Auth three-tier for /analytics/messages ===
|
|
# =============================================================================
|
|
|
|
|
|
async def test_analyze_messages_requires_auth(test_client: httpx.AsyncClient):
|
|
# Act
|
|
response = await test_client.get(f"{ANALYTICS_URL}/messages", params=_valid_params())
|
|
# Assert
|
|
assert response.status_code == 401, response.text
|
|
|
|
|
|
async def test_analyze_messages_requires_admin(
|
|
test_client: httpx.AsyncClient, standard_user
|
|
):
|
|
# Act
|
|
response = await test_client.get(
|
|
f"{ANALYTICS_URL}/messages",
|
|
params=_valid_params(),
|
|
headers=standard_user["headers"],
|
|
)
|
|
# Assert
|
|
assert response.status_code == 403, response.text
|
|
|
|
|
|
async def test_admin_can_analyze_messages(
|
|
test_client: httpx.AsyncClient, admin_headers
|
|
):
|
|
# Act
|
|
response = await test_client.get(
|
|
f"{ANALYTICS_URL}/messages",
|
|
params=_valid_params(),
|
|
headers=admin_headers,
|
|
)
|
|
# Assert
|
|
assert response.status_code == 200, response.text
|
|
payload = response.json()
|
|
assert payload["success"] is True
|
|
assert "data" in payload
|
|
|
|
|
|
# =============================================================================
|
|
# === Parameter validation for /analytics/messages ===
|
|
# =============================================================================
|
|
|
|
|
|
async def test_analyze_messages_rejects_missing_start_time(
|
|
test_client: httpx.AsyncClient, admin_headers
|
|
):
|
|
# Arrange — omit start_time
|
|
params = {"end_time": ISO_TIME_END}
|
|
# Act
|
|
response = await test_client.get(
|
|
f"{ANALYTICS_URL}/messages", params=params, headers=admin_headers
|
|
)
|
|
# Assert
|
|
assert response.status_code == 422, response.text
|
|
|
|
|
|
async def test_analyze_messages_rejects_missing_end_time(
|
|
test_client: httpx.AsyncClient, admin_headers
|
|
):
|
|
# Arrange — omit end_time
|
|
params = {"start_time": ISO_TIME_START}
|
|
# Act
|
|
response = await test_client.get(
|
|
f"{ANALYTICS_URL}/messages", params=params, headers=admin_headers
|
|
)
|
|
# Assert
|
|
assert response.status_code == 422, response.text
|
|
|
|
|
|
# =============================================================================
|
|
# === All analytics endpoints with valid params return 200 ===
|
|
# =============================================================================
|
|
|
|
|
|
async def test_admin_can_get_message_distribution(
|
|
test_client: httpx.AsyncClient, admin_headers
|
|
):
|
|
# Act
|
|
response = await test_client.get(
|
|
f"{ANALYTICS_URL}/messages/distribution",
|
|
params=_valid_params(),
|
|
headers=admin_headers,
|
|
)
|
|
# Assert
|
|
assert response.status_code == 200, response.text
|
|
payload = response.json()
|
|
assert payload["success"] is True
|
|
assert "data" in payload
|
|
|
|
|
|
async def test_admin_can_analyze_sessions(
|
|
test_client: httpx.AsyncClient, admin_headers
|
|
):
|
|
# Act
|
|
response = await test_client.get(
|
|
f"{ANALYTICS_URL}/sessions",
|
|
params=_valid_params(),
|
|
headers=admin_headers,
|
|
)
|
|
# Assert
|
|
assert response.status_code == 200, response.text
|
|
payload = response.json()
|
|
assert payload["success"] is True
|
|
assert "data" in payload
|
|
|
|
|
|
async def test_admin_can_analyze_delivery(
|
|
test_client: httpx.AsyncClient, admin_headers
|
|
):
|
|
# Act
|
|
response = await test_client.get(
|
|
f"{ANALYTICS_URL}/delivery",
|
|
params=_valid_params(),
|
|
headers=admin_headers,
|
|
)
|
|
# Assert
|
|
assert response.status_code == 200, response.text
|
|
payload = response.json()
|
|
assert payload["success"] is True
|
|
assert "data" in payload
|
|
|
|
|
|
async def test_admin_can_get_delivery_latency(
|
|
test_client: httpx.AsyncClient, admin_headers
|
|
):
|
|
# Act
|
|
response = await test_client.get(
|
|
f"{ANALYTICS_URL}/delivery/latency",
|
|
params=_valid_params(),
|
|
headers=admin_headers,
|
|
)
|
|
# Assert
|
|
assert response.status_code == 200, response.text
|
|
payload = response.json()
|
|
assert payload["success"] is True
|
|
assert "data" in payload
|
|
|
|
|
|
async def test_admin_can_get_delivery_funnel(
|
|
test_client: httpx.AsyncClient, admin_headers
|
|
):
|
|
# Act
|
|
response = await test_client.get(
|
|
f"{ANALYTICS_URL}/delivery/funnel",
|
|
params=_valid_params(),
|
|
headers=admin_headers,
|
|
)
|
|
# Assert
|
|
assert response.status_code == 200, response.text
|
|
payload = response.json()
|
|
assert payload["success"] is True
|
|
assert "data" in payload
|
|
|
|
|
|
async def test_admin_can_analyze_accounts(
|
|
test_client: httpx.AsyncClient, admin_headers
|
|
):
|
|
# Act
|
|
response = await test_client.get(
|
|
f"{ANALYTICS_URL}/accounts",
|
|
params=_valid_params(),
|
|
headers=admin_headers,
|
|
)
|
|
# Assert
|
|
assert response.status_code == 200, response.text
|
|
payload = response.json()
|
|
assert payload["success"] is True
|
|
assert "data" in payload
|
|
|
|
|
|
async def test_admin_can_analyze_peers(
|
|
test_client: httpx.AsyncClient, admin_headers
|
|
):
|
|
# Act
|
|
response = await test_client.get(
|
|
f"{ANALYTICS_URL}/peers",
|
|
params=_valid_params(),
|
|
headers=admin_headers,
|
|
)
|
|
# Assert
|
|
assert response.status_code == 200, response.text
|
|
payload = response.json()
|
|
assert payload["success"] is True
|
|
assert "data" in payload
|
|
|
|
|
|
async def test_admin_can_analyze_content_review(
|
|
test_client: httpx.AsyncClient, admin_headers
|
|
):
|
|
# Act
|
|
response = await test_client.get(
|
|
f"{ANALYTICS_URL}/content-review",
|
|
params=_valid_params(),
|
|
headers=admin_headers,
|
|
)
|
|
# Assert
|
|
assert response.status_code == 200, response.text
|
|
payload = response.json()
|
|
assert payload["success"] is True
|
|
assert "data" in payload
|
|
|
|
|
|
async def test_admin_can_analyze_messages_with_channel_type(
|
|
test_client: httpx.AsyncClient, admin_headers
|
|
):
|
|
# Act
|
|
response = await test_client.get(
|
|
f"{ANALYTICS_URL}/messages",
|
|
params=_valid_params(channel_type=DEFAULT_CHANNEL_TYPE),
|
|
headers=admin_headers,
|
|
)
|
|
# Assert
|
|
assert response.status_code == 200, response.text
|
|
payload = response.json()
|
|
assert payload["success"] is True
|
|
assert "data" in payload
|
|
|
|
|
|
# =============================================================================
|
|
# === /analytics/peers limit validation ===
|
|
# =============================================================================
|
|
|
|
|
|
async def test_analyze_peers_rejects_limit_zero(
|
|
test_client: httpx.AsyncClient, admin_headers
|
|
):
|
|
# Act — limit=0 violates ge=1
|
|
response = await test_client.get(
|
|
f"{ANALYTICS_URL}/peers",
|
|
params=_valid_params(limit=0),
|
|
headers=admin_headers,
|
|
)
|
|
# Assert
|
|
assert response.status_code == 422, response.text
|
|
|
|
|
|
async def test_analyze_peers_rejects_limit_over_max(
|
|
test_client: httpx.AsyncClient, admin_headers
|
|
):
|
|
# Act — limit=501 violates le=500
|
|
response = await test_client.get(
|
|
f"{ANALYTICS_URL}/peers",
|
|
params=_valid_params(limit=501),
|
|
headers=admin_headers,
|
|
)
|
|
# Assert
|
|
assert response.status_code == 422, response.text
|