新增并完善多模块接口测试用例,覆盖: 1. 权限校验:认证缺失、普通用户越权访问场景 2. 参数合法性校验:空值、格式错误、边界值、枚举约束 3. 响应字段增强:补充业务字段断言与分页回传校验 4. 异常场景:不存在资源、超限参数、无效格式等400/404/422场景
649 lines
22 KiB
Python
649 lines
22 KiB
Python
"""Integration tests for channels account_router endpoints."""
|
||
|
||
from __future__ import annotations
|
||
|
||
import httpx
|
||
import pytest
|
||
|
||
from .conftest import (
|
||
BASE_URL,
|
||
DEFAULT_CHANNEL_TYPE,
|
||
NON_EXISTENT_ACCOUNT_ID,
|
||
)
|
||
|
||
pytestmark = [pytest.mark.asyncio, pytest.mark.integration]
|
||
|
||
ACCOUNTS_URL = f"{BASE_URL}/accounts"
|
||
|
||
|
||
def _make_create_account_body(display_name: str = "pytest_account") -> dict:
|
||
"""Build a minimal CreateAccountRequest body."""
|
||
return {
|
||
"display_name": display_name,
|
||
"raw_config": {"app_id": "pytest_app", "app_secret": "pytest_secret"},
|
||
}
|
||
|
||
|
||
# =============================================================================
|
||
# === Auth three-tier for list accounts ===
|
||
# =============================================================================
|
||
|
||
|
||
async def test_list_accounts_requires_auth(test_client: httpx.AsyncClient):
|
||
# Act
|
||
response = await test_client.get(ACCOUNTS_URL)
|
||
# Assert
|
||
assert response.status_code == 401, response.text
|
||
|
||
|
||
async def test_list_accounts_forbids_standard_user(test_client: httpx.AsyncClient, standard_user):
|
||
# Act
|
||
response = await test_client.get(ACCOUNTS_URL, headers=standard_user["headers"])
|
||
# Assert
|
||
assert response.status_code == 403, response.text
|
||
|
||
|
||
async def test_admin_can_list_accounts(test_client: httpx.AsyncClient, admin_headers):
|
||
# Act
|
||
response = await test_client.get(ACCOUNTS_URL, headers=admin_headers)
|
||
# Assert
|
||
assert response.status_code == 200, response.text
|
||
|
||
payload = response.json()
|
||
assert payload["success"] is True
|
||
assert isinstance(payload["data"], dict)
|
||
assert "accounts" in payload["data"]
|
||
assert isinstance(payload["data"]["accounts"], list)
|
||
|
||
|
||
# =============================================================================
|
||
# === Pagination validation for list accounts ===
|
||
# =============================================================================
|
||
|
||
|
||
async def test_list_accounts_rejects_limit_zero(test_client: httpx.AsyncClient, admin_headers):
|
||
# Act — limit=0 violates ge=1
|
||
response = await test_client.get(
|
||
ACCOUNTS_URL,
|
||
params={"limit": 0},
|
||
headers=admin_headers,
|
||
)
|
||
# Assert
|
||
assert response.status_code == 422, response.text
|
||
|
||
|
||
async def test_list_accounts_rejects_limit_over_max(test_client: httpx.AsyncClient, admin_headers):
|
||
# Act — limit=1001 violates le=1000
|
||
response = await test_client.get(
|
||
ACCOUNTS_URL,
|
||
params={"limit": 1001},
|
||
headers=admin_headers,
|
||
)
|
||
# Assert
|
||
assert response.status_code == 422, response.text
|
||
|
||
|
||
# =============================================================================
|
||
# === batch enable / disable ===
|
||
# =============================================================================
|
||
|
||
|
||
async def test_batch_enable_accounts_requires_auth(test_client: httpx.AsyncClient):
|
||
# Arrange
|
||
body = {"account_ids": [NON_EXISTENT_ACCOUNT_ID]}
|
||
# Act
|
||
response = await test_client.post(f"{ACCOUNTS_URL}/batch/enable", json=body)
|
||
# Assert
|
||
assert response.status_code == 401, response.text
|
||
|
||
|
||
async def test_batch_enable_accounts_forbids_standard_user(test_client: httpx.AsyncClient, standard_user):
|
||
# Arrange
|
||
body = {"account_ids": [NON_EXISTENT_ACCOUNT_ID]}
|
||
# Act
|
||
response = await test_client.post(
|
||
f"{ACCOUNTS_URL}/batch/enable",
|
||
json=body,
|
||
headers=standard_user["headers"],
|
||
)
|
||
# Assert
|
||
assert response.status_code == 403, response.text
|
||
|
||
|
||
async def test_batch_enable_accounts_rejects_empty_body(test_client: httpx.AsyncClient, admin_headers):
|
||
# Arrange — account_ids 与 filter 同时为空
|
||
body = {"account_ids": None, "filter": None}
|
||
# Act
|
||
response = await test_client.post(
|
||
f"{ACCOUNTS_URL}/batch/enable",
|
||
json=body,
|
||
headers=admin_headers,
|
||
)
|
||
# Assert
|
||
assert response.status_code in (400, 422), response.text
|
||
|
||
|
||
async def test_batch_disable_accounts_rejects_empty_body(test_client: httpx.AsyncClient, admin_headers):
|
||
# Arrange
|
||
body = {"account_ids": None, "filter": None}
|
||
# Act
|
||
response = await test_client.post(
|
||
f"{ACCOUNTS_URL}/batch/disable",
|
||
json=body,
|
||
headers=admin_headers,
|
||
)
|
||
# Assert
|
||
assert response.status_code in (400, 422), response.text
|
||
|
||
|
||
async def test_batch_enable_accounts_rejects_ids_without_channel_type(
|
||
test_client: httpx.AsyncClient,
|
||
admin_headers,
|
||
):
|
||
# Arrange — account_ids 模式下 channel_type 必填(account_id 仅在
|
||
# (channel_type, account_id) 复合键下唯一,需显式限定渠道才能定位账户)
|
||
body = {"account_ids": [NON_EXISTENT_ACCOUNT_ID], "channel_type": None}
|
||
# Act
|
||
response = await test_client.post(
|
||
f"{ACCOUNTS_URL}/batch/enable",
|
||
json=body,
|
||
headers=admin_headers,
|
||
)
|
||
# Assert — schema 校验层拦截,返回 422
|
||
assert response.status_code == 422, response.text
|
||
|
||
|
||
async def test_batch_disable_accounts_rejects_ids_without_channel_type(
|
||
test_client: httpx.AsyncClient,
|
||
admin_headers,
|
||
):
|
||
# Arrange
|
||
body = {"account_ids": [NON_EXISTENT_ACCOUNT_ID], "channel_type": None}
|
||
# Act
|
||
response = await test_client.post(
|
||
f"{ACCOUNTS_URL}/batch/disable",
|
||
json=body,
|
||
headers=admin_headers,
|
||
)
|
||
# Assert
|
||
assert response.status_code == 422, response.text
|
||
|
||
|
||
async def test_batch_enable_accounts_with_channel_type_passes_schema(
|
||
test_client: httpx.AsyncClient,
|
||
admin_headers,
|
||
):
|
||
# Arrange — account_ids + channel_type 通过 schema 校验,到达 handler
|
||
# 后因账户不存在返回 404(证明 P0 修复:channel_type 透传至 handler)
|
||
body = {
|
||
"account_ids": [NON_EXISTENT_ACCOUNT_ID],
|
||
"channel_type": DEFAULT_CHANNEL_TYPE,
|
||
}
|
||
# Act
|
||
response = await test_client.post(
|
||
f"{ACCOUNTS_URL}/batch/enable",
|
||
json=body,
|
||
headers=admin_headers,
|
||
)
|
||
# Assert — 不再是 422 schema 错误,而是 404(账户不存在)或 200
|
||
# (批量操作部分失败语义),关键是不被 schema 拦截
|
||
assert response.status_code != 422, response.text
|
||
|
||
|
||
async def test_batch_disable_accounts_with_channel_type_passes_schema(
|
||
test_client: httpx.AsyncClient,
|
||
admin_headers,
|
||
):
|
||
# Arrange
|
||
body = {
|
||
"account_ids": [NON_EXISTENT_ACCOUNT_ID],
|
||
"channel_type": DEFAULT_CHANNEL_TYPE,
|
||
}
|
||
# Act
|
||
response = await test_client.post(
|
||
f"{ACCOUNTS_URL}/batch/disable",
|
||
json=body,
|
||
headers=admin_headers,
|
||
)
|
||
# Assert
|
||
assert response.status_code != 422, response.text
|
||
|
||
|
||
async def test_batch_disable_accounts_requires_auth(test_client: httpx.AsyncClient):
|
||
# Arrange
|
||
body = {"account_ids": [NON_EXISTENT_ACCOUNT_ID], "channel_type": DEFAULT_CHANNEL_TYPE}
|
||
# Act
|
||
response = await test_client.post(f"{ACCOUNTS_URL}/batch/disable", json=body)
|
||
# Assert
|
||
assert response.status_code == 401, response.text
|
||
|
||
|
||
async def test_batch_disable_accounts_forbids_standard_user(
|
||
test_client: httpx.AsyncClient, standard_user
|
||
):
|
||
# Arrange
|
||
body = {"account_ids": [NON_EXISTENT_ACCOUNT_ID], "channel_type": DEFAULT_CHANNEL_TYPE}
|
||
# Act
|
||
response = await test_client.post(
|
||
f"{ACCOUNTS_URL}/batch/disable",
|
||
json=body,
|
||
headers=standard_user["headers"],
|
||
)
|
||
# Assert
|
||
assert response.status_code == 403, response.text
|
||
|
||
|
||
# =============================================================================
|
||
# === create account under channel_type ===
|
||
# =============================================================================
|
||
|
||
|
||
async def test_create_account_requires_auth(test_client: httpx.AsyncClient):
|
||
# Arrange
|
||
body = _make_create_account_body()
|
||
# Act
|
||
response = await test_client.post(
|
||
f"{BASE_URL}/{DEFAULT_CHANNEL_TYPE}/accounts",
|
||
json=body,
|
||
)
|
||
# Assert
|
||
assert response.status_code == 401, response.text
|
||
|
||
|
||
async def test_create_account_forbids_standard_user(test_client: httpx.AsyncClient, standard_user):
|
||
# Arrange
|
||
body = _make_create_account_body()
|
||
# Act
|
||
response = await test_client.post(
|
||
f"{BASE_URL}/{DEFAULT_CHANNEL_TYPE}/accounts",
|
||
json=body,
|
||
headers=standard_user["headers"],
|
||
)
|
||
# Assert
|
||
assert response.status_code == 403, response.text
|
||
|
||
|
||
async def test_create_account_invalid_channel_type_returns_422(test_client: httpx.AsyncClient, admin_headers):
|
||
# Arrange — 适配器未注册时返回 422
|
||
body = _make_create_account_body()
|
||
# Act
|
||
response = await test_client.post(
|
||
f"{BASE_URL}/invalid_channel/accounts",
|
||
json=body,
|
||
headers=admin_headers,
|
||
)
|
||
# Assert — 不应是 401/403;适配器不存在应返回 422
|
||
assert response.status_code == 422, response.text
|
||
|
||
|
||
async def test_create_account_with_invalid_body_returns_not_4xx(test_client: httpx.AsyncClient, admin_headers):
|
||
# Arrange — display_name 过长触发校验失败
|
||
body = {
|
||
"display_name": "x" * 200,
|
||
"raw_config": {"app_id": "pytest_app"},
|
||
}
|
||
# Act
|
||
response = await test_client.post(
|
||
f"{BASE_URL}/{DEFAULT_CHANNEL_TYPE}/accounts",
|
||
json=body,
|
||
headers=admin_headers,
|
||
)
|
||
# Assert
|
||
assert response.status_code == 422, response.text
|
||
|
||
|
||
# =============================================================================
|
||
# === get / update / delete / state / runtime / rotate / test / export / clone ===
|
||
# =============================================================================
|
||
|
||
|
||
async def test_get_account_non_existent_returns_404(test_client: httpx.AsyncClient, admin_headers):
|
||
# Act
|
||
response = await test_client.get(
|
||
f"{BASE_URL}/{DEFAULT_CHANNEL_TYPE}/accounts/{NON_EXISTENT_ACCOUNT_ID}",
|
||
headers=admin_headers,
|
||
)
|
||
# Assert
|
||
assert response.status_code == 404, response.text
|
||
|
||
|
||
async def test_update_account_non_existent_returns_404(test_client: httpx.AsyncClient, admin_headers):
|
||
# Arrange
|
||
body = {"display_name": "updated_name"}
|
||
# Act
|
||
response = await test_client.put(
|
||
f"{BASE_URL}/{DEFAULT_CHANNEL_TYPE}/accounts/{NON_EXISTENT_ACCOUNT_ID}",
|
||
json=body,
|
||
headers=admin_headers,
|
||
)
|
||
# Assert
|
||
assert response.status_code == 404, response.text
|
||
|
||
|
||
async def test_update_account_rejects_empty_body(test_client: httpx.AsyncClient, admin_headers):
|
||
# Arrange — display_name 与 raw_config 同时为空,dispatch 校验返回 400
|
||
body = {"display_name": None, "raw_config": None}
|
||
# Act
|
||
response = await test_client.put(
|
||
f"{BASE_URL}/{DEFAULT_CHANNEL_TYPE}/accounts/{NON_EXISTENT_ACCOUNT_ID}",
|
||
json=body,
|
||
headers=admin_headers,
|
||
)
|
||
# Assert
|
||
assert response.status_code in (400, 422, 404), response.text
|
||
|
||
|
||
async def test_delete_account_non_existent_returns_404(test_client: httpx.AsyncClient, admin_headers):
|
||
# Act
|
||
response = await test_client.delete(
|
||
f"{BASE_URL}/{DEFAULT_CHANNEL_TYPE}/accounts/{NON_EXISTENT_ACCOUNT_ID}",
|
||
headers=admin_headers,
|
||
)
|
||
# Assert
|
||
assert response.status_code == 404, response.text
|
||
|
||
|
||
async def test_enable_account_non_existent_returns_404(test_client: httpx.AsyncClient, admin_headers):
|
||
# Act
|
||
response = await test_client.post(
|
||
f"{BASE_URL}/{DEFAULT_CHANNEL_TYPE}/accounts/{NON_EXISTENT_ACCOUNT_ID}/enable",
|
||
headers=admin_headers,
|
||
)
|
||
# Assert
|
||
assert response.status_code == 404, response.text
|
||
|
||
|
||
async def test_disable_account_non_existent_returns_404(test_client: httpx.AsyncClient, admin_headers):
|
||
# Act
|
||
response = await test_client.post(
|
||
f"{BASE_URL}/{DEFAULT_CHANNEL_TYPE}/accounts/{NON_EXISTENT_ACCOUNT_ID}/disable",
|
||
headers=admin_headers,
|
||
)
|
||
# Assert
|
||
assert response.status_code == 404, response.text
|
||
|
||
|
||
async def test_recover_account_non_existent_returns_404(test_client: httpx.AsyncClient, admin_headers):
|
||
# Act
|
||
response = await test_client.post(
|
||
f"{BASE_URL}/{DEFAULT_CHANNEL_TYPE}/accounts/{NON_EXISTENT_ACCOUNT_ID}/recover",
|
||
headers=admin_headers,
|
||
)
|
||
# Assert
|
||
assert response.status_code == 404, response.text
|
||
|
||
|
||
async def test_get_runtime_status_non_existent_returns_404(test_client: httpx.AsyncClient, admin_headers):
|
||
# Act
|
||
response = await test_client.get(
|
||
f"{BASE_URL}/{DEFAULT_CHANNEL_TYPE}/accounts/{NON_EXISTENT_ACCOUNT_ID}/runtime-status",
|
||
headers=admin_headers,
|
||
)
|
||
# Assert
|
||
assert response.status_code == 404, response.text
|
||
|
||
|
||
async def test_rotate_credentials_non_existent_returns_404(test_client: httpx.AsyncClient, admin_headers):
|
||
# Act
|
||
response = await test_client.post(
|
||
f"{BASE_URL}/{DEFAULT_CHANNEL_TYPE}/accounts/{NON_EXISTENT_ACCOUNT_ID}/rotate-credentials",
|
||
headers=admin_headers,
|
||
)
|
||
# Assert
|
||
assert response.status_code == 404, response.text
|
||
|
||
|
||
async def test_test_connection_non_existent_returns_404(test_client: httpx.AsyncClient, admin_headers):
|
||
# Act
|
||
response = await test_client.post(
|
||
f"{BASE_URL}/{DEFAULT_CHANNEL_TYPE}/accounts/{NON_EXISTENT_ACCOUNT_ID}/test-connection",
|
||
headers=admin_headers,
|
||
)
|
||
# Assert
|
||
assert response.status_code == 404, response.text
|
||
|
||
|
||
# =============================================================================
|
||
# === export account (with include_secrets authorization) ===
|
||
# =============================================================================
|
||
|
||
|
||
async def test_export_account_non_existent_returns_404(test_client: httpx.AsyncClient, admin_headers):
|
||
# Act
|
||
response = await test_client.get(
|
||
f"{BASE_URL}/{DEFAULT_CHANNEL_TYPE}/accounts/{NON_EXISTENT_ACCOUNT_ID}/export",
|
||
headers=admin_headers,
|
||
)
|
||
# Assert
|
||
assert response.status_code == 404, response.text
|
||
|
||
|
||
async def test_export_account_with_secrets_non_existent_returns_404(test_client: httpx.AsyncClient, admin_headers):
|
||
# Arrange — admin_headers 为 superadmin,include_secrets=True 的 superadmin
|
||
# 校验在 dispatch 阶段通过;账户不存在 → 404。
|
||
# 注:include_secrets=True 拒绝非 superadmin 管理员的场景无法用 admin_headers
|
||
# 验证(admin_headers 本身即 superadmin);standard_user 会被 get_admin_user
|
||
# 守门拦截(403),无法抵达 include_secrets 校验。
|
||
# Act
|
||
response = await test_client.get(
|
||
f"{BASE_URL}/{DEFAULT_CHANNEL_TYPE}/accounts/{NON_EXISTENT_ACCOUNT_ID}/export",
|
||
params={"include_secrets": True},
|
||
headers=admin_headers,
|
||
)
|
||
# Assert
|
||
assert response.status_code == 404, response.text
|
||
|
||
|
||
# =============================================================================
|
||
# === clone account ===
|
||
# =============================================================================
|
||
|
||
|
||
async def test_clone_account_non_existent_returns_404(test_client: httpx.AsyncClient, admin_headers):
|
||
# Arrange
|
||
body = {"new_display_name": "cloned_account", "include_credentials": False}
|
||
# Act
|
||
response = await test_client.post(
|
||
f"{BASE_URL}/{DEFAULT_CHANNEL_TYPE}/accounts/{NON_EXISTENT_ACCOUNT_ID}/clone",
|
||
json=body,
|
||
headers=admin_headers,
|
||
)
|
||
# Assert
|
||
assert response.status_code == 404, response.text
|
||
|
||
|
||
async def test_clone_account_rejects_invalid_body(test_client: httpx.AsyncClient, admin_headers):
|
||
# Arrange — new_display_name 过长触发校验失败
|
||
body = {"new_display_name": "x" * 200, "include_credentials": False}
|
||
# Act
|
||
response = await test_client.post(
|
||
f"{BASE_URL}/{DEFAULT_CHANNEL_TYPE}/accounts/{NON_EXISTENT_ACCOUNT_ID}/clone",
|
||
json=body,
|
||
headers=admin_headers,
|
||
)
|
||
# Assert
|
||
assert response.status_code == 422, response.text
|
||
|
||
|
||
# =============================================================================
|
||
# === Auth matrix for individual account operations ===
|
||
# =============================================================================
|
||
|
||
ACCOUNT_URL = f"{BASE_URL}/{DEFAULT_CHANNEL_TYPE}/accounts/{NON_EXISTENT_ACCOUNT_ID}"
|
||
|
||
|
||
async def test_get_account_requires_auth(test_client: httpx.AsyncClient):
|
||
# Act
|
||
response = await test_client.get(ACCOUNT_URL)
|
||
# Assert
|
||
assert response.status_code == 401, response.text
|
||
|
||
|
||
async def test_get_account_forbids_standard_user(test_client: httpx.AsyncClient, standard_user):
|
||
# Act
|
||
response = await test_client.get(ACCOUNT_URL, headers=standard_user["headers"])
|
||
# Assert
|
||
assert response.status_code == 403, response.text
|
||
|
||
|
||
async def test_update_account_requires_auth(test_client: httpx.AsyncClient):
|
||
# Arrange
|
||
body = {"display_name": "updated_name"}
|
||
# Act
|
||
response = await test_client.put(ACCOUNT_URL, json=body)
|
||
# Assert
|
||
assert response.status_code == 401, response.text
|
||
|
||
|
||
async def test_update_account_forbids_standard_user(test_client: httpx.AsyncClient, standard_user):
|
||
# Arrange
|
||
body = {"display_name": "updated_name"}
|
||
# Act
|
||
response = await test_client.put(ACCOUNT_URL, json=body, headers=standard_user["headers"])
|
||
# Assert
|
||
assert response.status_code == 403, response.text
|
||
|
||
|
||
async def test_delete_account_requires_auth(test_client: httpx.AsyncClient):
|
||
# Act
|
||
response = await test_client.delete(ACCOUNT_URL)
|
||
# Assert
|
||
assert response.status_code == 401, response.text
|
||
|
||
|
||
async def test_delete_account_forbids_standard_user(test_client: httpx.AsyncClient, standard_user):
|
||
# Act
|
||
response = await test_client.delete(ACCOUNT_URL, headers=standard_user["headers"])
|
||
# Assert
|
||
assert response.status_code == 403, response.text
|
||
|
||
|
||
async def test_enable_account_requires_auth(test_client: httpx.AsyncClient):
|
||
# Act
|
||
response = await test_client.post(f"{ACCOUNT_URL}/enable")
|
||
# Assert
|
||
assert response.status_code == 401, response.text
|
||
|
||
|
||
async def test_enable_account_forbids_standard_user(test_client: httpx.AsyncClient, standard_user):
|
||
# Act
|
||
response = await test_client.post(f"{ACCOUNT_URL}/enable", headers=standard_user["headers"])
|
||
# Assert
|
||
assert response.status_code == 403, response.text
|
||
|
||
|
||
async def test_disable_account_requires_auth(test_client: httpx.AsyncClient):
|
||
# Act
|
||
response = await test_client.post(f"{ACCOUNT_URL}/disable")
|
||
# Assert
|
||
assert response.status_code == 401, response.text
|
||
|
||
|
||
async def test_disable_account_forbids_standard_user(test_client: httpx.AsyncClient, standard_user):
|
||
# Act
|
||
response = await test_client.post(f"{ACCOUNT_URL}/disable", headers=standard_user["headers"])
|
||
# Assert
|
||
assert response.status_code == 403, response.text
|
||
|
||
|
||
async def test_recover_account_requires_auth(test_client: httpx.AsyncClient):
|
||
# Act
|
||
response = await test_client.post(f"{ACCOUNT_URL}/recover")
|
||
# Assert
|
||
assert response.status_code == 401, response.text
|
||
|
||
|
||
async def test_recover_account_forbids_standard_user(test_client: httpx.AsyncClient, standard_user):
|
||
# Act
|
||
response = await test_client.post(f"{ACCOUNT_URL}/recover", headers=standard_user["headers"])
|
||
# Assert
|
||
assert response.status_code == 403, response.text
|
||
|
||
|
||
async def test_get_runtime_status_requires_auth(test_client: httpx.AsyncClient):
|
||
# Act
|
||
response = await test_client.get(f"{ACCOUNT_URL}/runtime-status")
|
||
# Assert
|
||
assert response.status_code == 401, response.text
|
||
|
||
|
||
async def test_get_runtime_status_forbids_standard_user(
|
||
test_client: httpx.AsyncClient, standard_user
|
||
):
|
||
# Act
|
||
response = await test_client.get(
|
||
f"{ACCOUNT_URL}/runtime-status", headers=standard_user["headers"]
|
||
)
|
||
# Assert
|
||
assert response.status_code == 403, response.text
|
||
|
||
|
||
async def test_rotate_credentials_requires_auth(test_client: httpx.AsyncClient):
|
||
# Act
|
||
response = await test_client.post(f"{ACCOUNT_URL}/rotate-credentials")
|
||
# Assert
|
||
assert response.status_code == 401, response.text
|
||
|
||
|
||
async def test_rotate_credentials_forbids_standard_user(
|
||
test_client: httpx.AsyncClient, standard_user
|
||
):
|
||
# Act
|
||
response = await test_client.post(
|
||
f"{ACCOUNT_URL}/rotate-credentials", headers=standard_user["headers"]
|
||
)
|
||
# Assert
|
||
assert response.status_code == 403, response.text
|
||
|
||
|
||
async def test_test_connection_requires_auth(test_client: httpx.AsyncClient):
|
||
# Act
|
||
response = await test_client.post(f"{ACCOUNT_URL}/test-connection")
|
||
# Assert
|
||
assert response.status_code == 401, response.text
|
||
|
||
|
||
async def test_test_connection_forbids_standard_user(
|
||
test_client: httpx.AsyncClient, standard_user
|
||
):
|
||
# Act
|
||
response = await test_client.post(
|
||
f"{ACCOUNT_URL}/test-connection", headers=standard_user["headers"]
|
||
)
|
||
# Assert
|
||
assert response.status_code == 403, response.text
|
||
|
||
|
||
async def test_export_account_requires_auth(test_client: httpx.AsyncClient):
|
||
# Act
|
||
response = await test_client.get(f"{ACCOUNT_URL}/export")
|
||
# Assert
|
||
assert response.status_code == 401, response.text
|
||
|
||
|
||
async def test_export_account_forbids_standard_user(test_client: httpx.AsyncClient, standard_user):
|
||
# Act
|
||
response = await test_client.get(f"{ACCOUNT_URL}/export", headers=standard_user["headers"])
|
||
# Assert
|
||
assert response.status_code == 403, response.text
|
||
|
||
|
||
async def test_clone_account_requires_auth(test_client: httpx.AsyncClient):
|
||
# Arrange
|
||
body = {"new_display_name": "cloned_account", "include_credentials": False}
|
||
# Act
|
||
response = await test_client.post(f"{ACCOUNT_URL}/clone", json=body)
|
||
# Assert
|
||
assert response.status_code == 401, response.text
|
||
|
||
|
||
async def test_clone_account_forbids_standard_user(test_client: httpx.AsyncClient, standard_user):
|
||
# Arrange
|
||
body = {"new_display_name": "cloned_account", "include_credentials": False}
|
||
# Act
|
||
response = await test_client.post(
|
||
f"{ACCOUNT_URL}/clone", json=body, headers=standard_user["headers"]
|
||
)
|
||
# Assert
|
||
assert response.status_code == 403, response.text
|