mirror of
https://github.com/jxxghp/MoviePilot.git
synced 2026-09-05 07:27:15 +08:00
refactor: close transactional boundary debt batch
This commit is contained in:
@@ -5,11 +5,12 @@ from unittest.mock import AsyncMock, MagicMock
|
||||
import pytest
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from app.application.outbox import ClaimedOutboxMessage
|
||||
from app.application.subscription.delete import (
|
||||
DeleteSubscribeCommand,
|
||||
SyncDeleteSubscribeCommand,
|
||||
SubscribeDeletionActor,
|
||||
SubscribeDeletionCandidate,
|
||||
SyncDeleteSubscribeCommand,
|
||||
)
|
||||
from app.db.models.subscribe import Subscribe
|
||||
from app.db.oper.subscribe import SubscribeOper
|
||||
@@ -69,9 +70,27 @@ class _Outbox:
|
||||
if self.stage_error:
|
||||
raise self.stage_error
|
||||
|
||||
async def complete_by_event_key(self, event_key, _completed_at):
|
||||
"""记录即时事件成功后的 intent 收口。"""
|
||||
self.calls.append(("outbox_complete", event_key))
|
||||
async def claim_by_event_key(self, event_key, _now, _lease_until):
|
||||
"""记录并返回当前测试拥有的异步 lease。"""
|
||||
self.calls.append(("outbox_claim", event_key))
|
||||
return ClaimedOutboxMessage(
|
||||
message_id=len(self.calls),
|
||||
event_key=event_key,
|
||||
topic="test",
|
||||
payload={},
|
||||
payload_version=1,
|
||||
attempt=1,
|
||||
)
|
||||
|
||||
async def complete(self, message_id, attempt, _completed_at):
|
||||
"""记录带 attempt fencing 的异步完成。"""
|
||||
self.calls.append(("outbox_complete", message_id, attempt))
|
||||
return True
|
||||
|
||||
async def retry(self, message_id, attempt, **_kwargs):
|
||||
"""记录带 attempt fencing 的异步重试。"""
|
||||
self.calls.append(("outbox_retry", message_id, attempt))
|
||||
return True
|
||||
|
||||
|
||||
class _SyncRepository:
|
||||
@@ -125,9 +144,27 @@ class _SyncOutbox:
|
||||
"""记录同步暂存的 intent。"""
|
||||
self.calls.append(("outbox_stage", intent))
|
||||
|
||||
def complete_by_event_key(self, event_key, _completed_at):
|
||||
"""记录同步完成的 intent。"""
|
||||
self.calls.append(("outbox_complete", event_key))
|
||||
def claim_by_event_key(self, event_key, _now, _lease_until):
|
||||
"""记录并返回当前测试拥有的同步 lease。"""
|
||||
self.calls.append(("outbox_claim", event_key))
|
||||
return ClaimedOutboxMessage(
|
||||
message_id=len(self.calls),
|
||||
event_key=event_key,
|
||||
topic="test",
|
||||
payload={},
|
||||
payload_version=1,
|
||||
attempt=1,
|
||||
)
|
||||
|
||||
def complete(self, message_id, attempt, _completed_at):
|
||||
"""记录带 attempt fencing 的同步完成。"""
|
||||
self.calls.append(("outbox_complete", message_id, attempt))
|
||||
return True
|
||||
|
||||
def retry(self, message_id, attempt, **_kwargs):
|
||||
"""记录带 attempt fencing 的同步重试。"""
|
||||
self.calls.append(("outbox_retry", message_id, attempt))
|
||||
return True
|
||||
|
||||
|
||||
def _candidate(username="alice"):
|
||||
@@ -175,6 +212,7 @@ def _command(
|
||||
publish_deleted=publish,
|
||||
report_deleted=report,
|
||||
outbox=outbox,
|
||||
dispatch_store=outbox,
|
||||
)
|
||||
|
||||
|
||||
@@ -197,6 +235,7 @@ def _async_report_command(candidate, calls, result=True, error=None, outbox=None
|
||||
publish_deleted=publish,
|
||||
report_deleted=report,
|
||||
outbox=outbox,
|
||||
dispatch_store=outbox,
|
||||
)
|
||||
|
||||
|
||||
@@ -224,6 +263,7 @@ def _sync_command(
|
||||
publish_deleted=publish,
|
||||
report_deleted=report,
|
||||
outbox=outbox,
|
||||
dispatch_store=outbox,
|
||||
)
|
||||
|
||||
|
||||
@@ -242,7 +282,9 @@ async def test_owner_delete_commits_before_event_and_report():
|
||||
assert [call[0] for call in calls] == ["get", "delete", "commit", "event", "report"]
|
||||
assert calls[3][2]["subscribe_info"] == _candidate().event_payload
|
||||
assert calls[3][2]["idempotency_key"].startswith("subscribe.deleted:7:")
|
||||
assert calls[4][1] == _candidate().event_payload
|
||||
report_payload = dict(calls[4][1])
|
||||
assert report_payload.pop("idempotency_key").endswith(":report")
|
||||
assert report_payload == _candidate().event_payload
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@@ -357,8 +399,10 @@ async def test_delete_stages_outbox_before_commit_and_completes_after_event():
|
||||
"outbox_stage",
|
||||
"outbox_stage",
|
||||
"commit",
|
||||
"outbox_claim",
|
||||
"event",
|
||||
"outbox_complete",
|
||||
"outbox_claim",
|
||||
"report",
|
||||
"outbox_complete",
|
||||
]
|
||||
@@ -366,8 +410,8 @@ async def test_delete_stages_outbox_before_commit_and_completes_after_event():
|
||||
assert intent.topic == "subscribe.deleted"
|
||||
report_intent = calls[3][1]
|
||||
assert report_intent.topic == "subscribe.deleted.report"
|
||||
assert intent.event_key == calls[5][2]["idempotency_key"]
|
||||
assert calls[6][1] == intent.event_key
|
||||
assert intent.event_key == calls[6][2]["idempotency_key"]
|
||||
assert calls[5][1] == intent.event_key
|
||||
assert calls[8][1] == report_intent.event_key
|
||||
|
||||
|
||||
@@ -408,7 +452,8 @@ async def test_async_reporter_completes_report_intent_only_after_confirmation():
|
||||
|
||||
assert [call[0] for call in calls] == [
|
||||
"get", "delete", "outbox_stage", "outbox_stage", "commit",
|
||||
"event", "outbox_complete", "report", "outbox_complete",
|
||||
"outbox_claim", "event", "outbox_complete", "outbox_claim",
|
||||
"report", "outbox_complete",
|
||||
]
|
||||
|
||||
|
||||
@@ -425,7 +470,8 @@ async def test_async_reporter_false_keeps_report_intent_pending():
|
||||
|
||||
assert [call[0] for call in calls] == [
|
||||
"get", "delete", "outbox_stage", "outbox_stage", "commit",
|
||||
"event", "outbox_complete", "report",
|
||||
"outbox_claim", "event", "outbox_complete", "outbox_claim",
|
||||
"report", "outbox_retry",
|
||||
]
|
||||
|
||||
|
||||
@@ -447,7 +493,8 @@ async def test_async_reporter_error_keeps_report_intent_pending():
|
||||
|
||||
assert [call[0] for call in calls] == [
|
||||
"get", "delete", "outbox_stage", "outbox_stage", "commit",
|
||||
"event", "outbox_complete", "report",
|
||||
"outbox_claim", "event", "outbox_complete", "outbox_claim",
|
||||
"report", "outbox_retry",
|
||||
]
|
||||
|
||||
|
||||
@@ -505,7 +552,8 @@ def test_sync_delete_uses_same_durable_effect_order():
|
||||
|
||||
assert [call[0] for call in calls] == [
|
||||
"get", "delete", "outbox_stage", "outbox_stage", "commit",
|
||||
"event", "outbox_complete", "report", "outbox_complete",
|
||||
"outbox_claim", "event", "outbox_complete", "outbox_claim",
|
||||
"report", "outbox_complete",
|
||||
]
|
||||
assert calls[2][1].topic == "subscribe.deleted"
|
||||
assert calls[3][1].topic == "subscribe.deleted.report"
|
||||
@@ -527,9 +575,12 @@ def test_sync_delete_report_failure_returns_success_and_keeps_intent_pending():
|
||||
) is True
|
||||
assert [call[0] for call in calls] == [
|
||||
"get", "delete", "outbox_stage", "outbox_stage", "commit",
|
||||
"event", "outbox_complete", "report",
|
||||
"outbox_claim", "event", "outbox_complete", "outbox_claim",
|
||||
"report", "outbox_retry",
|
||||
]
|
||||
assert calls[7][1] == _candidate().event_payload
|
||||
report_payload = dict(calls[9][1])
|
||||
assert report_payload.pop("idempotency_key").endswith(":report")
|
||||
assert report_payload == _candidate().event_payload
|
||||
|
||||
|
||||
@pytest.mark.parametrize("failure", ["delete", "commit"])
|
||||
|
||||
Reference in New Issue
Block a user