新增并完善多模块接口测试用例,覆盖: 1. 权限校验:认证缺失、普通用户越权访问场景 2. 参数合法性校验:空值、格式错误、边界值、枚举约束 3. 响应字段增强:补充业务字段断言与分页回传校验 4. 异常场景:不存在资源、超限参数、无效格式等400/404/422场景
226 lines
7.6 KiB
Python
226 lines
7.6 KiB
Python
"""Integration tests for channels dashboard_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]
|
|
|
|
DASHBOARD_URL = f"{BASE_URL}/dashboard"
|
|
|
|
|
|
# =============================================================================
|
|
# === Auth three-tier for /dashboard/overview ===
|
|
# =============================================================================
|
|
|
|
|
|
async def test_get_dashboard_overview_requires_auth(test_client: httpx.AsyncClient):
|
|
# Act
|
|
response = await test_client.get(f"{DASHBOARD_URL}/overview")
|
|
# Assert
|
|
assert response.status_code == 401, response.text
|
|
|
|
|
|
async def test_get_dashboard_overview_requires_admin(test_client: httpx.AsyncClient, standard_user):
|
|
# Act
|
|
response = await test_client.get(f"{DASHBOARD_URL}/overview", headers=standard_user["headers"])
|
|
# Assert
|
|
assert response.status_code == 403, response.text
|
|
|
|
|
|
async def test_admin_can_get_dashboard_overview(test_client: httpx.AsyncClient, admin_headers):
|
|
# Act
|
|
response = await test_client.get(f"{DASHBOARD_URL}/overview", 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_dashboard_overview_with_time_range(test_client: httpx.AsyncClient, admin_headers):
|
|
# Act — start_time / end_time 仅影响 messages 子域统计
|
|
response = await test_client.get(
|
|
f"{DASHBOARD_URL}/overview",
|
|
params={"start_time": ISO_TIME_START, "end_time": ISO_TIME_END},
|
|
headers=admin_headers,
|
|
)
|
|
# Assert
|
|
assert response.status_code == 200, response.text
|
|
payload = response.json()
|
|
assert payload["success"] is True
|
|
assert "data" in payload
|
|
|
|
|
|
# =============================================================================
|
|
# === All dashboard endpoints with admin return 200 ===
|
|
# =============================================================================
|
|
|
|
|
|
async def test_admin_can_get_dashboard_accounts(test_client: httpx.AsyncClient, admin_headers):
|
|
# Act
|
|
response = await test_client.get(
|
|
f"{DASHBOARD_URL}/accounts",
|
|
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
|
|
|
|
|
|
async def test_admin_can_get_dashboard_messages(test_client: httpx.AsyncClient, admin_headers):
|
|
# Act
|
|
response = await test_client.get(
|
|
f"{DASHBOARD_URL}/messages",
|
|
params={
|
|
"channel_type": DEFAULT_CHANNEL_TYPE,
|
|
"start_time": ISO_TIME_START,
|
|
"end_time": ISO_TIME_END,
|
|
},
|
|
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_dashboard_sessions(test_client: httpx.AsyncClient, admin_headers):
|
|
# Act
|
|
response = await test_client.get(
|
|
f"{DASHBOARD_URL}/sessions",
|
|
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
|
|
|
|
|
|
async def test_admin_can_get_dashboard_delivery(test_client: httpx.AsyncClient, admin_headers):
|
|
# Act
|
|
response = await test_client.get(
|
|
f"{DASHBOARD_URL}/delivery",
|
|
params={
|
|
"channel_type": DEFAULT_CHANNEL_TYPE,
|
|
"start_time": ISO_TIME_START,
|
|
"end_time": ISO_TIME_END,
|
|
},
|
|
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_dashboard_realtime(test_client: httpx.AsyncClient, admin_headers):
|
|
# Act
|
|
response = await test_client.get(
|
|
f"{DASHBOARD_URL}/realtime",
|
|
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
|
|
|
|
|
|
# =============================================================================
|
|
# === /dashboard/todos — DSB-TODOS workbench todo counts ===
|
|
# =============================================================================
|
|
|
|
|
|
async def test_get_dashboard_todos_requires_auth(test_client: httpx.AsyncClient):
|
|
# Act
|
|
response = await test_client.get(f"{DASHBOARD_URL}/todos")
|
|
# Assert
|
|
assert response.status_code == 401, response.text
|
|
|
|
|
|
async def test_get_dashboard_todos_requires_admin(test_client: httpx.AsyncClient, standard_user):
|
|
# Act
|
|
response = await test_client.get(f"{DASHBOARD_URL}/todos", headers=standard_user["headers"])
|
|
# Assert
|
|
assert response.status_code == 403, response.text
|
|
|
|
|
|
async def test_admin_can_get_dashboard_todos(test_client: httpx.AsyncClient, admin_headers):
|
|
# Act
|
|
response = await test_client.get(
|
|
f"{DASHBOARD_URL}/todos",
|
|
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
|
|
|
|
|
|
# =============================================================================
|
|
# === /dashboard/realtime window_seconds validation ===
|
|
# =============================================================================
|
|
|
|
|
|
async def test_dashboard_realtime_rejects_window_below_min(test_client: httpx.AsyncClient, admin_headers):
|
|
# Act — window_seconds=9 violates ge=10
|
|
response = await test_client.get(
|
|
f"{DASHBOARD_URL}/realtime",
|
|
params={"window_seconds": 9},
|
|
headers=admin_headers,
|
|
)
|
|
# Assert
|
|
assert response.status_code == 422, response.text
|
|
|
|
|
|
async def test_dashboard_realtime_rejects_window_above_max(test_client: httpx.AsyncClient, admin_headers):
|
|
# Act — window_seconds=301 violates le=300
|
|
response = await test_client.get(
|
|
f"{DASHBOARD_URL}/realtime",
|
|
params={"window_seconds": 301},
|
|
headers=admin_headers,
|
|
)
|
|
# Assert
|
|
assert response.status_code == 422, response.text
|
|
|
|
|
|
# =============================================================================
|
|
# === /dashboard/delivery invalid time format ===
|
|
# =============================================================================
|
|
|
|
|
|
async def test_dashboard_delivery_rejects_invalid_start_time_format(test_client: httpx.AsyncClient, admin_headers):
|
|
# Act — "not-a-date" is not ISO 8601, parse_datetime raises ValidationError (400)
|
|
response = await test_client.get(
|
|
f"{DASHBOARD_URL}/delivery",
|
|
params={"start_time": "not-a-date"},
|
|
headers=admin_headers,
|
|
)
|
|
# Assert
|
|
assert response.status_code == 400, response.text
|
|
|
|
|
|
async def test_dashboard_messages_rejects_invalid_end_time_format(test_client: httpx.AsyncClient, admin_headers):
|
|
# Act — "not-a-date" is not ISO 8601, parse_datetime raises ValidationError (400)
|
|
response = await test_client.get(
|
|
f"{DASHBOARD_URL}/messages",
|
|
params={"end_time": "not-a-date"},
|
|
headers=admin_headers,
|
|
)
|
|
# Assert
|
|
assert response.status_code == 400, response.text
|