ForcePilot/backend/test/integration/api/channels/test_login_router.py
Kris 04a2472014 test(channel): add full integration test suite for channels module
1. 新增channels模块集成测试目录与基础fixture
2. 为capability、doctor、reports、dashboard、directory、webhook、wizard、health、analytics、allowlist、config等路由编写完整的鉴权、参数校验与异常场景测试
3. 修复基础集成测试setup,新增external/scheduler/channel数据库schema初始化步骤
2026-07-02 03:25:27 +08:00

277 lines
8.3 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""Integration tests for channels login_router endpoints."""
from __future__ import annotations
import httpx
import pytest
from .conftest import (
BASE_URL,
DEFAULT_CHANNEL_TYPE,
NON_EXISTENT_ACCOUNT_ID,
NON_EXISTENT_SESSION_ID,
)
pytestmark = [pytest.mark.asyncio, pytest.mark.integration]
LOGIN_BASE = (
f"{BASE_URL}/{DEFAULT_CHANNEL_TYPE}/accounts/{NON_EXISTENT_ACCOUNT_ID}/login"
)
# =============================================================================
# === Auth three-tier for login status ===
# =============================================================================
async def test_get_login_status_requires_auth(test_client: httpx.AsyncClient):
# Act
response = await test_client.get(f"{LOGIN_BASE}/status")
# Assert
assert response.status_code == 401, response.text
async def test_get_login_status_forbids_standard_user(
test_client: httpx.AsyncClient, standard_user
):
# Act
response = await test_client.get(
f"{LOGIN_BASE}/status",
headers=standard_user["headers"],
)
# Assert
assert response.status_code == 403, response.text
async def test_get_login_status_non_existent_returns_404(
test_client: httpx.AsyncClient, admin_headers
):
# Act
response = await test_client.get(f"{LOGIN_BASE}/status", headers=admin_headers)
# Assert
assert response.status_code == 404, response.text
# =============================================================================
# === qr/start ===
# =============================================================================
async def test_qr_start_requires_auth(test_client: httpx.AsyncClient):
# Act
response = await test_client.post(f"{LOGIN_BASE}/qr/start")
# Assert
assert response.status_code == 401, response.text
async def test_qr_start_forbids_standard_user(test_client: httpx.AsyncClient, standard_user):
# Act
response = await test_client.post(
f"{LOGIN_BASE}/qr/start",
headers=standard_user["headers"],
)
# Assert
assert response.status_code == 403, response.text
async def test_qr_start_non_existent_account_returns_404(
test_client: httpx.AsyncClient, admin_headers
):
# Act
response = await test_client.post(f"{LOGIN_BASE}/qr/start", headers=admin_headers)
# Assert
assert response.status_code == 404, response.text
# =============================================================================
# === qr/wait ===
# =============================================================================
async def test_qr_wait_requires_session_id(test_client: httpx.AsyncClient, admin_headers):
# Act — 缺少必填 query 参数 session_id
response = await test_client.get(f"{LOGIN_BASE}/qr/wait", headers=admin_headers)
# Assert
assert response.status_code == 422, response.text
async def test_qr_wait_non_existent_account_returns_404_or_422(
test_client: httpx.AsyncClient, admin_headers
):
# Arrange
params = {"session_id": NON_EXISTENT_SESSION_ID}
# Act
response = await test_client.get(
f"{LOGIN_BASE}/qr/wait",
params=params,
headers=admin_headers,
)
# Assert — 账户不存在返回 404或登录会话无效返回 422
assert response.status_code in (404, 422), response.text
async def test_qr_wait_rejects_timeout_out_of_range(test_client: httpx.AsyncClient, admin_headers):
# Arrange — timeout_ms=500 低于下限 1000
params = {"session_id": NON_EXISTENT_SESSION_ID, "timeout_ms": 500}
# Act
response = await test_client.get(
f"{LOGIN_BASE}/qr/wait",
params=params,
headers=admin_headers,
)
# Assert
assert response.status_code == 422, response.text
async def test_qr_wait_rejects_timeout_over_max(test_client: httpx.AsyncClient, admin_headers):
# Arrange — timeout_ms=70000 超过上限 60000
params = {"session_id": NON_EXISTENT_SESSION_ID, "timeout_ms": 70000}
# Act
response = await test_client.get(
f"{LOGIN_BASE}/qr/wait",
params=params,
headers=admin_headers,
)
# Assert
assert response.status_code == 422, response.text
# =============================================================================
# === qr/cancel ===
# =============================================================================
async def test_qr_cancel_requires_session_id(test_client: httpx.AsyncClient, admin_headers):
# Act — 缺少必填 query 参数 session_id
response = await test_client.post(f"{LOGIN_BASE}/qr/cancel", headers=admin_headers)
# Assert
assert response.status_code == 422, response.text
async def test_qr_cancel_non_existent_account_returns_404_or_422(
test_client: httpx.AsyncClient, admin_headers
):
# Arrange
params = {"session_id": NON_EXISTENT_SESSION_ID}
# Act
response = await test_client.post(
f"{LOGIN_BASE}/qr/cancel",
params=params,
headers=admin_headers,
)
# Assert
assert response.status_code in (404, 422), response.text
# =============================================================================
# === qr/refresh ===
# =============================================================================
async def test_qr_refresh_requires_session_id(test_client: httpx.AsyncClient, admin_headers):
# Act — 缺少必填 query 参数 session_id
response = await test_client.post(f"{LOGIN_BASE}/qr/refresh", headers=admin_headers)
# Assert
assert response.status_code == 422, response.text
async def test_qr_refresh_non_existent_account_returns_404_or_422(
test_client: httpx.AsyncClient, admin_headers
):
# Arrange
params = {"session_id": NON_EXISTENT_SESSION_ID}
# Act
response = await test_client.post(
f"{LOGIN_BASE}/qr/refresh",
params=params,
headers=admin_headers,
)
# Assert
assert response.status_code in (404, 422), response.text
# =============================================================================
# === logout ===
# =============================================================================
async def test_logout_requires_auth(test_client: httpx.AsyncClient):
# Act
response = await test_client.post(f"{LOGIN_BASE}/logout")
# Assert
assert response.status_code == 401, response.text
async def test_logout_forbids_standard_user(test_client: httpx.AsyncClient, standard_user):
# Act
response = await test_client.post(
f"{LOGIN_BASE}/logout",
headers=standard_user["headers"],
)
# Assert
assert response.status_code == 403, response.text
async def test_logout_non_existent_account_returns_404(
test_client: httpx.AsyncClient, admin_headers
):
# Act
response = await test_client.post(f"{LOGIN_BASE}/logout", headers=admin_headers)
# Assert
assert response.status_code == 404, response.text
# =============================================================================
# === force-logout ===
# =============================================================================
async def test_force_logout_requires_auth(test_client: httpx.AsyncClient):
# Arrange
body = {"reason": "pytest"}
# Act
response = await test_client.post(f"{LOGIN_BASE}/force-logout", json=body)
# Assert
assert response.status_code == 401, response.text
async def test_force_logout_forbids_standard_user(
test_client: httpx.AsyncClient, standard_user
):
# Arrange
body = {"reason": "pytest"}
# Act
response = await test_client.post(
f"{LOGIN_BASE}/force-logout",
json=body,
headers=standard_user["headers"],
)
# Assert
assert response.status_code == 403, response.text
async def test_force_logout_requires_reason(test_client: httpx.AsyncClient, admin_headers):
# Act — 缺少必填字段 reason
response = await test_client.post(
f"{LOGIN_BASE}/force-logout",
json={},
headers=admin_headers,
)
# Assert
assert response.status_code == 422, response.text
async def test_force_logout_non_existent_account_returns_404(
test_client: httpx.AsyncClient, admin_headers
):
# Arrange
body = {"reason": "pytest"}
# Act
response = await test_client.post(
f"{LOGIN_BASE}/force-logout",
json=body,
headers=admin_headers,
)
# Assert
assert response.status_code == 404, response.text