1. 新增channels模块集成测试目录与基础fixture 2. 为capability、doctor、reports、dashboard、directory、webhook、wizard、health、analytics、allowlist、config等路由编写完整的鉴权、参数校验与异常场景测试 3. 修复基础集成测试setup,新增external/scheduler/channel数据库schema初始化步骤
242 lines
7.3 KiB
Python
242 lines
7.3 KiB
Python
"""Integration tests for channels wizard_router endpoints."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import httpx
|
|
import pytest
|
|
|
|
from .conftest import BASE_URL, DEFAULT_CHANNEL_TYPE
|
|
|
|
pytestmark = [pytest.mark.asyncio, pytest.mark.integration]
|
|
|
|
|
|
def _make_validate_payload() -> dict:
|
|
"""Build a valid ValidateWizardRequest body."""
|
|
return {
|
|
"step_id": "account_info",
|
|
"values": {"app_id": "pytest_app", "app_secret": "pytest_secret"},
|
|
}
|
|
|
|
|
|
def _make_finalize_payload() -> dict:
|
|
"""Build a valid FinalizeWizardRequest body."""
|
|
return {
|
|
"patches": [
|
|
{
|
|
"step_id": "account_info",
|
|
"values": {"app_id": "pytest_app", "app_secret": "pytest_secret"},
|
|
}
|
|
]
|
|
}
|
|
|
|
|
|
# =============================================================================
|
|
# === Auth three-tier for GET /wizard/steps ===
|
|
# =============================================================================
|
|
|
|
|
|
async def test_list_wizard_steps_requires_auth(test_client: httpx.AsyncClient):
|
|
# Act
|
|
response = await test_client.get(f"{BASE_URL}/{DEFAULT_CHANNEL_TYPE}/wizard/steps")
|
|
# Assert
|
|
assert response.status_code == 401
|
|
|
|
|
|
async def test_list_wizard_steps_requires_admin(
|
|
test_client: httpx.AsyncClient, standard_user
|
|
):
|
|
# Act
|
|
response = await test_client.get(
|
|
f"{BASE_URL}/{DEFAULT_CHANNEL_TYPE}/wizard/steps",
|
|
headers=standard_user["headers"],
|
|
)
|
|
# Assert
|
|
assert response.status_code == 403
|
|
|
|
|
|
async def test_admin_can_list_wizard_steps_returns_200_or_404_or_501(
|
|
test_client: httpx.AsyncClient, admin_headers
|
|
):
|
|
# Act
|
|
response = await test_client.get(
|
|
f"{BASE_URL}/{DEFAULT_CHANNEL_TYPE}/wizard/steps",
|
|
headers=admin_headers,
|
|
)
|
|
# Assert — adapter not registered → 404 or 501, or 200 with steps
|
|
assert response.status_code in (200, 404, 501), response.text
|
|
|
|
|
|
# =============================================================================
|
|
# === POST /wizard/validate ===
|
|
# =============================================================================
|
|
|
|
|
|
async def test_wizard_validate_returns_200_or_404_or_501(
|
|
test_client: httpx.AsyncClient, admin_headers
|
|
):
|
|
# Arrange
|
|
body = _make_validate_payload()
|
|
# Act
|
|
response = await test_client.post(
|
|
f"{BASE_URL}/{DEFAULT_CHANNEL_TYPE}/wizard/validate",
|
|
json=body,
|
|
headers=admin_headers,
|
|
)
|
|
# Assert
|
|
assert response.status_code in (200, 404, 501), response.text
|
|
|
|
|
|
async def test_wizard_validate_rejects_empty_step_id(
|
|
test_client: httpx.AsyncClient, admin_headers
|
|
):
|
|
# Arrange — step_id min_length=1
|
|
body = {"step_id": "", "values": {}}
|
|
# Act
|
|
response = await test_client.post(
|
|
f"{BASE_URL}/{DEFAULT_CHANNEL_TYPE}/wizard/validate",
|
|
json=body,
|
|
headers=admin_headers,
|
|
)
|
|
# Assert
|
|
assert response.status_code == 422, response.text
|
|
|
|
|
|
# =============================================================================
|
|
# === POST /wizard/steps/{step_id}/apply ===
|
|
# =============================================================================
|
|
|
|
|
|
async def test_wizard_apply_returns_200_or_404_or_501(
|
|
test_client: httpx.AsyncClient, admin_headers
|
|
):
|
|
# Arrange
|
|
step_id = "account_info"
|
|
body = {"values": {"app_id": "pytest_app", "app_secret": "pytest_secret"}}
|
|
# Act
|
|
response = await test_client.post(
|
|
f"{BASE_URL}/{DEFAULT_CHANNEL_TYPE}/wizard/steps/{step_id}/apply",
|
|
json=body,
|
|
headers=admin_headers,
|
|
)
|
|
# Assert
|
|
assert response.status_code in (200, 404, 501), response.text
|
|
|
|
|
|
# =============================================================================
|
|
# === POST /wizard/finalize ===
|
|
# =============================================================================
|
|
|
|
|
|
async def test_wizard_finalize_returns_200_or_404_or_501(
|
|
test_client: httpx.AsyncClient, admin_headers
|
|
):
|
|
# Arrange
|
|
body = _make_finalize_payload()
|
|
# Act
|
|
response = await test_client.post(
|
|
f"{BASE_URL}/{DEFAULT_CHANNEL_TYPE}/wizard/finalize",
|
|
json=body,
|
|
headers=admin_headers,
|
|
)
|
|
# Assert
|
|
assert response.status_code in (200, 404, 501), response.text
|
|
|
|
|
|
async def test_wizard_finalize_rejects_empty_patches(
|
|
test_client: httpx.AsyncClient, admin_headers
|
|
):
|
|
# Arrange — patches min_length=1
|
|
body = {"patches": []}
|
|
# Act
|
|
response = await test_client.post(
|
|
f"{BASE_URL}/{DEFAULT_CHANNEL_TYPE}/wizard/finalize",
|
|
json=body,
|
|
headers=admin_headers,
|
|
)
|
|
# Assert
|
|
assert response.status_code == 422, response.text
|
|
|
|
|
|
# =============================================================================
|
|
# === GET /wizard/oauth-callback ===
|
|
# =============================================================================
|
|
|
|
|
|
async def test_oauth_callback_requires_code(test_client: httpx.AsyncClient, admin_headers):
|
|
# Arrange — code required
|
|
params = {"state": "pytest_state", "redirect_uri": "http://localhost/callback"}
|
|
# Act
|
|
response = await test_client.get(
|
|
f"{BASE_URL}/{DEFAULT_CHANNEL_TYPE}/wizard/oauth-callback",
|
|
params=params,
|
|
headers=admin_headers,
|
|
)
|
|
# Assert
|
|
assert response.status_code == 422, response.text
|
|
|
|
|
|
async def test_oauth_callback_requires_state(test_client: httpx.AsyncClient, admin_headers):
|
|
# Arrange — state required
|
|
params = {"code": "pytest_code", "redirect_uri": "http://localhost/callback"}
|
|
# Act
|
|
response = await test_client.get(
|
|
f"{BASE_URL}/{DEFAULT_CHANNEL_TYPE}/wizard/oauth-callback",
|
|
params=params,
|
|
headers=admin_headers,
|
|
)
|
|
# Assert
|
|
assert response.status_code == 422, response.text
|
|
|
|
|
|
async def test_oauth_callback_requires_redirect_uri(
|
|
test_client: httpx.AsyncClient, admin_headers
|
|
):
|
|
# Arrange — redirect_uri required
|
|
params = {"code": "pytest_code", "state": "pytest_state"}
|
|
# Act
|
|
response = await test_client.get(
|
|
f"{BASE_URL}/{DEFAULT_CHANNEL_TYPE}/wizard/oauth-callback",
|
|
params=params,
|
|
headers=admin_headers,
|
|
)
|
|
# Assert
|
|
assert response.status_code == 422, response.text
|
|
|
|
|
|
async def test_oauth_callback_with_all_params_returns_404_or_501_or_400(
|
|
test_client: httpx.AsyncClient, admin_headers
|
|
):
|
|
# Arrange — all params present but channel not registered
|
|
params = {
|
|
"code": "pytest_code",
|
|
"state": "pytest_state",
|
|
"redirect_uri": "http://localhost/callback",
|
|
}
|
|
# Act
|
|
response = await test_client.get(
|
|
f"{BASE_URL}/{DEFAULT_CHANNEL_TYPE}/wizard/oauth-callback",
|
|
params=params,
|
|
headers=admin_headers,
|
|
)
|
|
# Assert — non-existent channel / adapter → 404, 501, or 400
|
|
assert response.status_code in (400, 404, 501), response.text
|
|
|
|
|
|
# =============================================================================
|
|
# === Invalid channel_type validation ===
|
|
# =============================================================================
|
|
|
|
|
|
async def test_wizard_rejects_invalid_channel_type(
|
|
test_client: httpx.AsyncClient, admin_headers
|
|
):
|
|
# Arrange — channel_type must match ChannelType enum
|
|
invalid_channel = "invalid_channel_type_xyz"
|
|
# Act
|
|
response = await test_client.get(
|
|
f"{BASE_URL}/{invalid_channel}/wizard/steps",
|
|
headers=admin_headers,
|
|
)
|
|
# Assert
|
|
assert response.status_code == 422, response.text
|