mirror of
https://github.com/jxxghp/MoviePilot.git
synced 2026-08-29 20:17:13 +08:00
test(plugin): stabilize identity race coverage
This commit is contained in:
@@ -4,6 +4,7 @@ import threading
|
||||
from concurrent.futures import ThreadPoolExecutor
|
||||
from dataclasses import replace
|
||||
from datetime import datetime, timedelta, timezone
|
||||
from unittest.mock import Mock
|
||||
|
||||
import pytest
|
||||
import sqlalchemy as sa
|
||||
@@ -18,6 +19,7 @@ from app.application.plugin.identity import (
|
||||
PluginPayloadSourceType,
|
||||
PluginSourceCandidate,
|
||||
TrustedPluginSourceType,
|
||||
WritePluginIdentityCommand,
|
||||
plan_legacy_plugin_identity,
|
||||
)
|
||||
from app.db.adapters.pluginidentity import TransactionalPluginIdentityStore
|
||||
@@ -199,6 +201,30 @@ def test_plugin_identity_store_allows_only_one_same_revision_writer(
|
||||
assert identity_store.get("DemoPlugin").revision == 2
|
||||
|
||||
|
||||
def test_write_command_rolls_back_when_conditional_replace_loses_race() -> None:
|
||||
"""条件更新在最终 CAS 失利时必须确定回滚并报告竞争冲突。"""
|
||||
current = _identity()
|
||||
repository = Mock()
|
||||
repository.get.return_value = current
|
||||
repository.stage_replace.return_value = False
|
||||
unit_of_work = Mock()
|
||||
command = WritePluginIdentityCommand(repository, unit_of_work)
|
||||
|
||||
with pytest.raises(PluginIdentityConflictError, match="其他任务更新"):
|
||||
command.execute(
|
||||
replace(
|
||||
current,
|
||||
declared_version="2.0.0",
|
||||
updated_at=NOW + timedelta(seconds=1),
|
||||
),
|
||||
expected_revision=current.revision,
|
||||
)
|
||||
|
||||
repository.stage_replace.assert_called_once()
|
||||
unit_of_work.commit.assert_not_called()
|
||||
unit_of_work.rollback.assert_called_once()
|
||||
|
||||
|
||||
def test_local_payload_preserves_trusted_online_binding() -> None:
|
||||
"""本地开发覆盖只改变载荷事实,不得抹掉可信在线更新仓库。"""
|
||||
identity = replace(
|
||||
|
||||
Reference in New Issue
Block a user