Files
Aether/tests/services/test_user_service_delete_user.py
fawney19 cfa5535f6e refactor: 引入 safe_create_task 防止后台任务被 GC 回收,降低默认连接池和 worker 数量
- 新增 safe_create_task 统一替代裸 asyncio.create_task,通过全局集合持有 task 引用
- 默认 worker 数量从 4 降为 1,HTTP 连接池总预算从 800 降为 200
- 为 health_cache 和 affinity_manager 内存缓存增加上限淘汰机制
- MemoryCachePlugin 支持延迟启动清理任务
- gunicorn when_ready 增加 gc.collect() 并记录 post_worker_init RSS
2026-03-10 14:42:33 +08:00

158 lines
4.9 KiB
Python

from __future__ import annotations
from types import SimpleNamespace
from unittest.mock import AsyncMock, MagicMock, call
import pytest
from src.models.database import (
ApiKey,
PaymentOrder,
RefundRequest,
RequestCandidate,
Usage,
User,
Wallet,
)
from src.services.user.service import UserService
def test_delete_user_blocks_when_unfinished_refund_exists() -> None:
user = SimpleNamespace(id="user-1", email="u1@example.com")
user_query = MagicMock()
user_query.filter.return_value = user_query
user_query.first.return_value = user
wallet_ids_query = MagicMock()
wallet_ids_query.outerjoin.return_value = wallet_ids_query
wallet_ids_query.filter.return_value = wallet_ids_query
wallet_ids_query.all.return_value = [("wallet-1",)]
refund_query = MagicMock()
refund_query.filter.return_value = refund_query
refund_query.count.return_value = 1
db = MagicMock()
db.in_transaction.return_value = True
def _query(model: object) -> MagicMock:
if model is User:
return user_query
if model is Wallet.id:
return wallet_ids_query
if model is RefundRequest:
return refund_query
raise AssertionError(f"unexpected query target: {model}")
db.query.side_effect = _query
with pytest.raises(ValueError, match="未完结退款"):
UserService.delete_user(db, "user-1")
db.delete.assert_not_called()
def test_delete_user_blocks_when_unfinished_payment_order_exists() -> None:
user = SimpleNamespace(id="user-2", email="u2@example.com")
user_query = MagicMock()
user_query.filter.return_value = user_query
user_query.first.return_value = user
wallet_ids_query = MagicMock()
wallet_ids_query.outerjoin.return_value = wallet_ids_query
wallet_ids_query.filter.return_value = wallet_ids_query
wallet_ids_query.all.return_value = [("wallet-2",)]
refund_query = MagicMock()
refund_query.filter.return_value = refund_query
refund_query.count.return_value = 0
order_query = MagicMock()
order_query.filter.return_value = order_query
order_query.count.return_value = 2
db = MagicMock()
db.in_transaction.return_value = True
def _query(model: object) -> MagicMock:
if model is User:
return user_query
if model is Wallet.id:
return wallet_ids_query
if model is RefundRequest:
return refund_query
if model is PaymentOrder:
return order_query
raise AssertionError(f"unexpected query target: {model}")
db.query.side_effect = _query
with pytest.raises(ValueError, match="未完结充值订单"):
UserService.delete_user(db, "user-2")
db.delete.assert_not_called()
def test_delete_user_precleans_large_tables_before_final_delete(
monkeypatch: pytest.MonkeyPatch,
) -> None:
user = SimpleNamespace(id="user-3", email="u3@example.com")
user_query = MagicMock()
user_query.filter.return_value = user_query
user_query.first.return_value = user
wallet_ids_query = MagicMock()
wallet_ids_query.outerjoin.return_value = wallet_ids_query
wallet_ids_query.filter.return_value = wallet_ids_query
wallet_ids_query.all.return_value = []
api_key_ids_query = MagicMock()
api_key_ids_query.filter.return_value = api_key_ids_query
api_key_ids_query.all.return_value = [("key-1",), ("key-2",)]
delete_query = MagicMock()
delete_query.filter.return_value = delete_query
delete_query.delete.return_value = 2
db = MagicMock()
def _query(model: object) -> MagicMock:
if model is User:
return user_query
if model is Wallet.id:
return wallet_ids_query
if model is ApiKey.id:
return api_key_ids_query
return delete_query
db.query.side_effect = _query
pre_clean_api_key = MagicMock()
batch_nullify_fk = MagicMock()
invalidate_user_cache = AsyncMock()
create_task = MagicMock()
monkeypatch.setattr("src.services.user.service.pre_clean_api_key", pre_clean_api_key)
monkeypatch.setattr("src.services.user.service.batch_nullify_fk", batch_nullify_fk)
monkeypatch.setattr(
"src.services.user.service.UserCacheService.invalidate_user_cache",
invalidate_user_cache,
)
monkeypatch.setattr("src.services.user.service.safe_create_task", create_task)
assert UserService.delete_user(db, "user-3") is True
assert pre_clean_api_key.call_args_list == [call(db, "key-1"), call(db, "key-2")]
assert batch_nullify_fk.call_args_list == [
call(db, Usage, "user_id", "user-3"),
call(db, RequestCandidate, "user_id", "user-3"),
]
db.delete.assert_called_once_with(user)
db.commit.assert_called_once()
db.rollback.assert_not_called()
invalidate_user_cache.assert_called_once_with("user-3", "u3@example.com")
create_task.assert_called_once()