257 lines
8.2 KiB
Python
257 lines
8.2 KiB
Python
"""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
|