Skip to content

Commit 8d60214

Browse files
committed
test(plugin): stabilize identity race coverage
1 parent 43d52a3 commit 8d60214

1 file changed

Lines changed: 26 additions & 0 deletions

File tree

tests/test_plugin_identity.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
from concurrent.futures import ThreadPoolExecutor
55
from dataclasses import replace
66
from datetime import datetime, timedelta, timezone
7+
from unittest.mock import Mock
78

89
import pytest
910
import sqlalchemy as sa
@@ -18,6 +19,7 @@
1819
PluginPayloadSourceType,
1920
PluginSourceCandidate,
2021
TrustedPluginSourceType,
22+
WritePluginIdentityCommand,
2123
plan_legacy_plugin_identity,
2224
)
2325
from app.db.adapters.pluginidentity import TransactionalPluginIdentityStore
@@ -199,6 +201,30 @@ def update(version: str) -> str:
199201
assert identity_store.get("DemoPlugin").revision == 2
200202

201203

204+
def test_write_command_rolls_back_when_conditional_replace_loses_race() -> None:
205+
"""条件更新在最终 CAS 失利时必须确定回滚并报告竞争冲突。"""
206+
current = _identity()
207+
repository = Mock()
208+
repository.get.return_value = current
209+
repository.stage_replace.return_value = False
210+
unit_of_work = Mock()
211+
command = WritePluginIdentityCommand(repository, unit_of_work)
212+
213+
with pytest.raises(PluginIdentityConflictError, match="其他任务更新"):
214+
command.execute(
215+
replace(
216+
current,
217+
declared_version="2.0.0",
218+
updated_at=NOW + timedelta(seconds=1),
219+
),
220+
expected_revision=current.revision,
221+
)
222+
223+
repository.stage_replace.assert_called_once()
224+
unit_of_work.commit.assert_not_called()
225+
unit_of_work.rollback.assert_called_once()
226+
227+
202228
def test_local_payload_preserves_trusted_online_binding() -> None:
203229
"""本地开发覆盖只改变载荷事实,不得抹掉可信在线更新仓库。"""
204230
identity = replace(

0 commit comments

Comments
 (0)