ForcePilot/backend/test/integration/api/channels/test_login_router.py

257 lines
8.2 KiB
Python
Raw Normal View History

"""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