| from __future__ import annotations | |
| import errno | |
| import json | |
| import os | |
| from pathlib import Path | |
| import autocad_bench.common.io as io_utils | |
| def test_atomic_write_json_retries_transient_replace_failure( | |
| monkeypatch, tmp_path: Path | |
| ) -> None: | |
| attempts = 0 | |
| real_replace = os.replace | |
| sleeps: list[float] = [] | |
| def flaky_replace(source, destination) -> None: | |
| nonlocal attempts | |
| attempts += 1 | |
| if attempts < 3: | |
| raise PermissionError(errno.EPERM, "transient replace denial") | |
| real_replace(source, destination) | |
| monkeypatch.setattr(io_utils.os, "replace", flaky_replace) | |
| target = tmp_path / "state.json" | |
| io_utils.atomic_write_json( | |
| target, | |
| {"state": "running"}, | |
| sleep=sleeps.append, | |
| ) | |
| assert attempts == 3 | |
| assert sleeps == [0.05, 0.1] | |
| assert json.loads(target.read_text(encoding="utf-8")) == {"state": "running"} | |
| assert list(tmp_path.glob(".*.tmp")) == [] | |