mirror of
https://github.com/jxxghp/MoviePilot.git
synced 2026-08-29 03:56:43 +08:00
174 lines
5.0 KiB
Python
174 lines
5.0 KiB
Python
"""按媒体身份批量删除订阅的应用用例测试。"""
|
|
|
|
import pytest
|
|
|
|
from app.application.subscription.delete import (
|
|
SubscribeDeletionActor,
|
|
SubscribeDeletionCandidate,
|
|
)
|
|
from app.application.subscription.identity import (
|
|
DeleteSubscriptionsByIdentityCommand,
|
|
)
|
|
from app.schemas.types import MediaSource
|
|
|
|
|
|
class _Repository:
|
|
"""记录批量订阅删除顺序的仓储替身。"""
|
|
|
|
def __init__(self, candidates, calls):
|
|
"""保存候选订阅和共享调用序列。"""
|
|
self.candidates = candidates
|
|
self.calls = calls
|
|
|
|
async def list_candidates_by_identity(self, *args):
|
|
"""记录媒体身份查询参数并返回候选订阅。"""
|
|
self.calls.append(("list", *args))
|
|
return self.candidates
|
|
|
|
async def stage_delete(self, subscribe_id):
|
|
"""记录待删除订阅。"""
|
|
self.calls.append(("delete", subscribe_id))
|
|
|
|
|
|
class _UnitOfWork:
|
|
"""可注入提交异常的批量事务替身。"""
|
|
|
|
def __init__(self, calls, commit_error=None):
|
|
"""保存共享调用序列与可选提交异常。"""
|
|
self.calls = calls
|
|
self.commit_error = commit_error
|
|
|
|
async def commit(self):
|
|
"""记录提交并按需失败。"""
|
|
self.calls.append(("commit",))
|
|
if self.commit_error:
|
|
raise self.commit_error
|
|
|
|
async def rollback(self):
|
|
"""记录回滚。"""
|
|
self.calls.append(("rollback",))
|
|
|
|
|
|
def _candidate(subscribe_id, username):
|
|
"""构造批量删除候选订阅。"""
|
|
return SubscribeDeletionCandidate(
|
|
subscribe_id=subscribe_id,
|
|
username=username,
|
|
event_payload={
|
|
"id": subscribe_id,
|
|
"username": username,
|
|
"media_source": "tmdb",
|
|
"media_id": "123",
|
|
},
|
|
)
|
|
|
|
|
|
def _command(candidates, calls, commit_error=None, failing_event_id=None):
|
|
"""构造带可观察事件错误处理的批量删除用例。"""
|
|
async def publish(payload):
|
|
"""记录事件并按订阅编号注入失败。"""
|
|
subscribe_id = payload["subscribe_id"]
|
|
calls.append(("event", subscribe_id, payload))
|
|
if subscribe_id == failing_event_id:
|
|
raise RuntimeError("event failed")
|
|
|
|
def handle_error(subscribe_id, error):
|
|
"""记录被隔离的单条事件异常。"""
|
|
calls.append(("event_error", subscribe_id, str(error)))
|
|
|
|
return DeleteSubscriptionsByIdentityCommand(
|
|
repository=_Repository(candidates, calls),
|
|
unit_of_work=_UnitOfWork(calls, commit_error),
|
|
publish_deleted=publish,
|
|
handle_event_error=handle_error,
|
|
)
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_bulk_delete_filters_owner_and_commits_before_events():
|
|
"""普通用户只删除自己的候选,并在提交后发送事件。"""
|
|
calls = []
|
|
command = _command([_candidate(1, "bob"), _candidate(2, "alice")], calls)
|
|
|
|
deleted = await command.execute(
|
|
MediaSource.TMDB,
|
|
"123",
|
|
1,
|
|
None,
|
|
SubscribeDeletionActor(username="alice", is_superuser=False),
|
|
)
|
|
|
|
assert deleted == 1
|
|
assert [call[0] for call in calls] == ["list", "delete", "commit", "event"]
|
|
assert calls[1] == ("delete", 2)
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_bulk_delete_commits_even_when_nothing_matches():
|
|
"""无匹配订阅时仍保持历史上的空事务提交行为。"""
|
|
calls = []
|
|
command = _command([], calls)
|
|
|
|
deleted = await command.execute(
|
|
MediaSource.TMDB,
|
|
"123",
|
|
None,
|
|
None,
|
|
SubscribeDeletionActor(username="alice", is_superuser=False),
|
|
)
|
|
|
|
assert deleted == 0
|
|
assert [call[0] for call in calls] == ["list", "commit"]
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_bulk_delete_commit_failure_rolls_back_without_events():
|
|
"""批量提交失败必须回滚且不发送任何删除事件。"""
|
|
calls = []
|
|
command = _command(
|
|
[_candidate(1, "alice")],
|
|
calls,
|
|
commit_error=RuntimeError("commit failed"),
|
|
)
|
|
|
|
with pytest.raises(RuntimeError, match="commit failed"):
|
|
await command.execute(
|
|
MediaSource.TMDB,
|
|
"123",
|
|
None,
|
|
None,
|
|
SubscribeDeletionActor(username="alice", is_superuser=False),
|
|
)
|
|
|
|
assert [call[0] for call in calls] == ["list", "delete", "commit", "rollback"]
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_bulk_delete_isolates_one_event_failure_and_continues():
|
|
"""单条事件失败只记录错误,后续已提交订阅仍继续发事件。"""
|
|
calls = []
|
|
command = _command(
|
|
[_candidate(1, "alice"), _candidate(2, "alice")],
|
|
calls,
|
|
failing_event_id=1,
|
|
)
|
|
|
|
deleted = await command.execute(
|
|
MediaSource.TMDB,
|
|
"123",
|
|
None,
|
|
None,
|
|
SubscribeDeletionActor(username="alice", is_superuser=False),
|
|
)
|
|
|
|
assert deleted == 2
|
|
assert [call[0] for call in calls] == [
|
|
"list",
|
|
"delete",
|
|
"delete",
|
|
"commit",
|
|
"event",
|
|
"event_error",
|
|
"event",
|
|
]
|