fix: make frozen product backup unique and atomic on --force
This commit is contained in:
@@ -329,3 +329,56 @@ def test_build_split_force_backs_up_old(tmp_path):
|
||||
_guard_frozen_products(out_path, manifest_path, force=True)
|
||||
baks = list(tmp_path.glob("pools.json.bak.*"))
|
||||
assert len(baks) == 1, f"未备份旧产物: {list(tmp_path.iterdir())}"
|
||||
|
||||
|
||||
def test_build_split_force_twice_same_suffix_keeps_both(tmp_path):
|
||||
"""连续两次 force 命中同后缀时,第二次备份不覆盖第一次(追加递增序号)。"""
|
||||
from app.harness.build_split import _guard_frozen_products
|
||||
|
||||
out_path = tmp_path / "pools.json"
|
||||
manifest_path = tmp_path / "split_manifest.json"
|
||||
|
||||
# 第一次 force:两文件都无 manifest 里的 pools_sha256 之外的差异,后缀恒为同值。
|
||||
out_path.write_text('{"gen":1}', encoding="utf-8")
|
||||
manifest_path.write_text('{"pools_sha256":"deadbeef00000000"}', encoding="utf-8")
|
||||
_guard_frozen_products(out_path, manifest_path, force=True)
|
||||
|
||||
# 第二次 force:写入相同 sha 的新产物,触发同后缀备份。
|
||||
out_path.write_text('{"gen":2}', encoding="utf-8")
|
||||
manifest_path.write_text('{"pools_sha256":"deadbeef00000000"}', encoding="utf-8")
|
||||
_guard_frozen_products(out_path, manifest_path, force=True)
|
||||
|
||||
pools_baks = sorted(p.name for p in tmp_path.glob("pools.json.bak.*"))
|
||||
assert len(pools_baks) == 2, f"同后缀第二次备份覆盖了第一次: {pools_baks}"
|
||||
|
||||
|
||||
def test_build_split_force_rollback_on_partial_failure(tmp_path, monkeypatch):
|
||||
"""备份第二个文件失败时,第一个已备份文件被 rollback 回原名(原子性)。"""
|
||||
from pathlib import Path
|
||||
|
||||
from app.harness import build_split as bs
|
||||
|
||||
out_path = tmp_path / "pools.json"
|
||||
manifest_path = tmp_path / "split_manifest.json"
|
||||
out_path.write_text('{"gen":1}', encoding="utf-8")
|
||||
manifest_path.write_text('{"pools_sha256":"deadbeef00000000"}', encoding="utf-8")
|
||||
|
||||
real_rename = Path.rename
|
||||
calls = {"n": 0}
|
||||
|
||||
def flaky_rename(self, target):
|
||||
# 第一次 rename(备份 pools.json)成功,第二次(备份 manifest)抛错。
|
||||
calls["n"] += 1
|
||||
if calls["n"] == 2:
|
||||
raise OSError("模拟第二个备份 rename 失败")
|
||||
return real_rename(self, target)
|
||||
|
||||
monkeypatch.setattr(Path, "rename", flaky_rename)
|
||||
|
||||
with pytest.raises(OSError, match="模拟第二个备份"):
|
||||
bs._guard_frozen_products(out_path, manifest_path, force=True)
|
||||
|
||||
# rollback 后:两原文件仍在原位,无残留 .bak.*
|
||||
assert out_path.exists(), "第一个文件未被 rollback 回原名"
|
||||
assert manifest_path.exists(), "第二个文件不应被移动"
|
||||
assert not list(tmp_path.glob("*.bak.*")), f"残留半备份: {list(tmp_path.iterdir())}"
|
||||
|
||||
Reference in New Issue
Block a user